GK SDK 源码库: XMIPCLinuxV100R005C00SPC030 (kernel/tools/open_source excluded)
This commit is contained in:
@@ -0,0 +1,133 @@
|
||||
menu "Sound support"
|
||||
|
||||
config SOUND
|
||||
bool "Enable sound support"
|
||||
help
|
||||
Support making sounds through an audio codec. This is normally a
|
||||
beep at a chosen frequency for a selected length of time. However
|
||||
the drivers support playing arbitrary sound samples using a
|
||||
PCM interface.
|
||||
|
||||
Note: At present the sound setup is somewhat tangled up in that the
|
||||
audio codecs are called from the sound-i2s code. This could be
|
||||
converted to driver model.
|
||||
|
||||
config I2S
|
||||
bool "Enable I2S support"
|
||||
depends on SOUND
|
||||
help
|
||||
I2S is a serial bus often used to transmit audio data from the
|
||||
SoC to the audio codec. This option enables sound support using
|
||||
I2S. It calls either of the two supported codecs (no use is made
|
||||
of driver model at present).
|
||||
|
||||
config I2S_ROCKCHIP
|
||||
bool "Enable I2S support for Rockchip SoCs"
|
||||
depends on I2S
|
||||
help
|
||||
Rockchip SoCs support an I2S interface for sending audio data to an
|
||||
audio codec. This option enables support for this, using one of the
|
||||
available audio codec drivers. This driver does not make use of
|
||||
DMA, but writes each word directly to the hardware.
|
||||
|
||||
config I2S_SAMSUNG
|
||||
bool "Enable I2C support for Samsung SoCs"
|
||||
depends on I2S
|
||||
help
|
||||
Samsung Exynos SoCs support an I2S interface for sending audio
|
||||
data to an audio codec. This option enables support for this,
|
||||
using one of the available audio codec drivers. Enabling this
|
||||
option provides an implementation for sound_init() and
|
||||
sound_play().
|
||||
|
||||
config SOUND_I8254
|
||||
bool "Intel i8254 timer / beeper"
|
||||
depends on SOUND
|
||||
help
|
||||
This enables support for a beeper that uses the i8254 timer chip.
|
||||
This can emit beeps at a fixed frequency. It is possible to control
|
||||
the length of the beeps, by turning a beep on, waiting for a period
|
||||
of time, then turning it off.
|
||||
|
||||
This is quite an old feature, called PIT (Programmable Interval
|
||||
Timer), but is nonetheless still available on modern x86 machines.
|
||||
|
||||
config SOUND_INTEL_HDA
|
||||
bool "Intel HDA audio codec"
|
||||
depends on SOUND
|
||||
help
|
||||
Most Intel chips have an HDA (High-definition audio) codec which can
|
||||
be used by U-Boot to play simple beeps. This is also sometimes called
|
||||
Azalia which was the development code-name. It requires setup
|
||||
information in the device tree (see intel-hda.txt).
|
||||
|
||||
config SOUND_IVYBRIDGE
|
||||
bool "Intel Ivybridge sound support"
|
||||
depends on SOUND
|
||||
select SOUND_INTEL_HDA
|
||||
help
|
||||
Enable sound output on supported Intel Ivybridge-based boards. This
|
||||
driver uses Intel's High-definition Audio (HDA) architecture,
|
||||
sometimes called Azalia. The audio codec is detected using a
|
||||
semi-automatic mechanism.
|
||||
|
||||
config I2S_TEGRA
|
||||
bool "Enable I2S support for Nvidia Tegra SoCs"
|
||||
depends on I2S
|
||||
select TEGRA124_DMA
|
||||
help
|
||||
Nvidia Tegra SoCs support several I2S interfaces for sending audio
|
||||
data to an audio codec. This option enables support for this,
|
||||
using one of the available audio codec drivers.
|
||||
|
||||
config SOUND_MAX98088
|
||||
bool "Support Maxim max98088 audio codec"
|
||||
depends on I2S
|
||||
help
|
||||
Enable the max98088 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_MAX98090
|
||||
bool "Support Maxim max98090 audio codec"
|
||||
depends on I2S
|
||||
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
|
||||
help
|
||||
Enable the max98095 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_RT5677
|
||||
bool "Support Realtek RT5677 audio codec"
|
||||
depends on SOUND
|
||||
help
|
||||
Enable the Realtek RT5677 audio codec. This is an I2S device used on
|
||||
some Chromebooks from around 2015 ('auron'). It is configured using
|
||||
an I2C interface and supports multiple sound inputs and outputs,
|
||||
including digital microphones.
|
||||
|
||||
config SOUND_SANDBOX
|
||||
bool "Support sandbox emulated audio codec"
|
||||
depends on SANDBOX && SOUND
|
||||
help
|
||||
U-Boot sandbox can emulate a sound device using SDL, playing the
|
||||
sound on the host machine. This option implements the sound_init()
|
||||
and sound_play() functions for sandbox. Note that you must install
|
||||
the SDL libraries for this to work.
|
||||
|
||||
config SOUND_WM8994
|
||||
bool "Support Wolfson Micro wm8994 audio codec"
|
||||
depends on I2S_SAMSUNG
|
||||
help
|
||||
Enable the wm8994 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.
|
||||
|
||||
endmenu
|
||||
@@ -0,0 +1,23 @@
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
#
|
||||
# Copyright (C) 2012 Samsung Electronics
|
||||
# R. Chandrasekar <rcsekar@samsung.com>
|
||||
|
||||
obj-$(CONFIG_SOUND) += sound.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_ROCKCHIP) += rockchip_i2s.o rockchip_sound.o
|
||||
obj-$(CONFIG_I2S_SAMSUNG) += samsung_sound.o
|
||||
obj-$(CONFIG_I2S_TEGRA) += tegra_ahub.o tegra_i2s.o tegra_sound.o
|
||||
obj-$(CONFIG_SOUND_WM8994) += wm8994.o
|
||||
obj-$(CONFIG_SOUND_MAX98088) += max98088.o maxim_codec.o
|
||||
obj-$(CONFIG_SOUND_MAX98090) += max98090.o maxim_codec.o
|
||||
obj-$(CONFIG_SOUND_MAX98095) += max98095.o maxim_codec.o
|
||||
obj-$(CONFIG_SOUND_INTEL_HDA) += hda_codec.o
|
||||
obj-$(CONFIG_SOUND_I8254) += i8254_beep.o
|
||||
obj-$(CONFIG_SOUND_RT5677) += rt5677.o
|
||||
obj-$(CONFIG_INTEL_BROADWELL) += broadwell_i2s.o broadwell_sound.o
|
||||
obj-$(CONFIG_SOUND_IVYBRIDGE) += ivybridge_sound.o
|
||||
@@ -0,0 +1,307 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Intel Broadwell I2S driver
|
||||
*
|
||||
* Copyright 2019 Google LLC
|
||||
*
|
||||
* Modified from dc i2s/broadwell/broadwell.c
|
||||
*/
|
||||
|
||||
#define LOG_CATEGORY UCLASS_I2S
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <i2s.h>
|
||||
#include <time.h>
|
||||
#include <asm/io.h>
|
||||
#include "broadwell_i2s.h"
|
||||
|
||||
enum {
|
||||
BDW_SHIM_START_ADDRESS = 0xfb000,
|
||||
BDW_SSP0_START_ADDRESS = 0xfc000,
|
||||
BDW_SSP1_START_ADDRESS = 0xfd000,
|
||||
};
|
||||
|
||||
struct broadwell_i2s_priv {
|
||||
enum frame_sync_rel_timing_t rel_timing;
|
||||
enum frame_sync_pol_t sfrm_polarity;
|
||||
enum end_transfer_state_t end_transfer_state;
|
||||
enum clock_mode_t sclk_mode;
|
||||
uint sclk_dummy_stop; /* 0-31 */
|
||||
uint sclk_frame_width; /* 1-38 */
|
||||
struct i2s_shim_regs *shim;
|
||||
struct broadwell_i2s_regs *regs;
|
||||
};
|
||||
|
||||
static void init_shim_csr(struct broadwell_i2s_priv *priv)
|
||||
{
|
||||
/*
|
||||
* Select SSP clock
|
||||
* Turn off low power clock
|
||||
* Set PIO mode
|
||||
* Stall DSP core
|
||||
*/
|
||||
clrsetbits_le32(&priv->shim->csr,
|
||||
SHIM_CS_S0IOCS | SHIM_CS_LPCS | SHIM_CS_DCS_MASK,
|
||||
SHIM_CS_S1IOCS | SHIM_CS_SBCS_SSP1_24MHZ |
|
||||
SHIM_CS_SBCS_SSP0_24MHZ | SHIM_CS_SDPM_PIO_SSP1 |
|
||||
SHIM_CS_SDPM_PIO_SSP0 | SHIM_CS_STALL |
|
||||
SHIM_CS_DCS_DSP32_AF32);
|
||||
}
|
||||
|
||||
static void init_shim_clkctl(struct i2s_uc_priv *uc_priv,
|
||||
struct broadwell_i2s_priv *priv)
|
||||
{
|
||||
u32 clkctl = readl(&priv->shim->clkctl);
|
||||
|
||||
/* Set 24Mhz mclk, prevent local clock gating, enable SSP0 clock */
|
||||
clkctl &= SHIM_CLKCTL_RESERVED;
|
||||
clkctl |= SHIM_CLKCTL_MCLK_24MHZ | SHIM_CLKCTL_DCPLCG;
|
||||
|
||||
/* Enable requested SSP interface */
|
||||
if (uc_priv->id)
|
||||
clkctl |= SHIM_CLKCTL_SCOE_SSP1 | SHIM_CLKCTL_SFLCGB_SSP1_CGD;
|
||||
else
|
||||
clkctl |= SHIM_CLKCTL_SCOE_SSP0 | SHIM_CLKCTL_SFLCGB_SSP0_CGD;
|
||||
|
||||
writel(clkctl, &priv->shim->clkctl);
|
||||
}
|
||||
|
||||
static void init_sscr0(struct i2s_uc_priv *uc_priv,
|
||||
struct broadwell_i2s_priv *priv)
|
||||
{
|
||||
u32 sscr0;
|
||||
uint scale;
|
||||
|
||||
/* Set data size based on BPS */
|
||||
if (uc_priv->bitspersample > 16)
|
||||
sscr0 = (uc_priv->bitspersample - 16 - 1) << SSP_SSC0_DSS_SHIFT
|
||||
| SSP_SSC0_EDSS;
|
||||
else
|
||||
sscr0 = (uc_priv->bitspersample - 1) << SSP_SSC0_DSS_SHIFT;
|
||||
|
||||
/* Set network mode, Stereo PSP frame format */
|
||||
sscr0 |= SSP_SSC0_MODE_NETWORK |
|
||||
SSP_SSC0_FRDC_STEREO |
|
||||
SSP_SSC0_FRF_PSP |
|
||||
SSP_SSC0_TIM |
|
||||
SSP_SSC0_RIM |
|
||||
SSP_SSC0_ECS_PCH |
|
||||
SSP_SSC0_NCS_PCH |
|
||||
SSP_SSC0_ACS_PCH;
|
||||
|
||||
/* Scale 24MHz MCLK */
|
||||
scale = uc_priv->audio_pll_clk / uc_priv->samplingrate / uc_priv->bfs;
|
||||
sscr0 |= scale << SSP_SSC0_SCR_SHIFT;
|
||||
|
||||
writel(sscr0, &priv->regs->sscr0);
|
||||
}
|
||||
|
||||
static void init_sscr1(struct broadwell_i2s_priv *priv)
|
||||
{
|
||||
u32 sscr1 = readl(&priv->regs->sscr1);
|
||||
|
||||
sscr1 &= SSP_SSC1_RESERVED;
|
||||
|
||||
/* Set as I2S master */
|
||||
sscr1 |= SSP_SSC1_SCLKDIR_MASTER | SSP_SSC1_SCLKDIR_MASTER;
|
||||
|
||||
/* Enable TXD tristate behavior for PCH */
|
||||
sscr1 |= SSP_SSC1_TTELP | SSP_SSC1_TTE;
|
||||
|
||||
/* Disable DMA Tx/Rx service request */
|
||||
sscr1 |= SSP_SSC1_TSRE | SSP_SSC1_RSRE;
|
||||
|
||||
/* Clock on during transfer */
|
||||
sscr1 |= SSP_SSC1_SCFR;
|
||||
|
||||
/* Set FIFO thresholds */
|
||||
sscr1 |= SSP_FIFO_SIZE << SSP_SSC1_RFT_SHIFT;
|
||||
sscr1 |= SSP_FIFO_SIZE << SSP_SSC1_TFT_SHIFT;
|
||||
|
||||
/* Disable interrupts */
|
||||
sscr1 &= ~(SSP_SSC1_EBCEI | SSP_SSC1_TINTE | SSP_SSC1_PINTE);
|
||||
sscr1 &= ~(SSP_SSC1_LBM | SSP_SSC1_RWOT);
|
||||
|
||||
writel(sscr1, &priv->regs->sscr1);
|
||||
}
|
||||
|
||||
static void init_sspsp(struct broadwell_i2s_priv *priv)
|
||||
{
|
||||
u32 sspsp = readl(&priv->regs->sspsp);
|
||||
|
||||
sspsp &= SSP_PSP_RESERVED;
|
||||
sspsp |= priv->sclk_mode << SSP_PSP_SCMODE_SHIFT;
|
||||
sspsp |= (priv->sclk_dummy_stop << SSP_PSP_DMYSTOP_SHIFT) &
|
||||
SSP_PSP_DMYSTOP_MASK;
|
||||
sspsp |= (priv->sclk_dummy_stop >> 2 << SSP_PSP_EDYMSTOP_SHIFT) &
|
||||
SSP_PSP_EDMYSTOP_MASK;
|
||||
sspsp |= priv->sclk_frame_width << SSP_PSP_SFRMWDTH_SHIFT;
|
||||
|
||||
/* Frame Sync Relative Timing */
|
||||
if (priv->rel_timing == NEXT_FRMS_AFTER_END_OF_T4)
|
||||
sspsp |= SSP_PSP_FSRT;
|
||||
else
|
||||
sspsp &= ~SSP_PSP_FSRT;
|
||||
|
||||
/* Serial Frame Polarity */
|
||||
if (priv->sfrm_polarity == SSP_FRMS_ACTIVE_HIGH)
|
||||
sspsp |= SSP_PSP_SFRMP;
|
||||
else
|
||||
sspsp &= ~SSP_PSP_SFRMP;
|
||||
|
||||
/* End Data Transfer State */
|
||||
if (priv->end_transfer_state == SSP_END_TRANSFER_STATE_LOW)
|
||||
sspsp &= ~SSP_PSP_ETDS;
|
||||
else
|
||||
sspsp |= SSP_PSP_ETDS;
|
||||
|
||||
writel(sspsp, &priv->regs->sspsp);
|
||||
}
|
||||
|
||||
static void init_ssp_time_slot(struct broadwell_i2s_priv *priv)
|
||||
{
|
||||
writel(3, &priv->regs->sstsa);
|
||||
writel(3, &priv->regs->ssrsa);
|
||||
}
|
||||
|
||||
static int bdw_i2s_init(struct udevice *dev)
|
||||
{
|
||||
struct i2s_uc_priv *uc_priv = dev_get_uclass_priv(dev);
|
||||
struct broadwell_i2s_priv *priv = dev_get_priv(dev);
|
||||
|
||||
init_shim_csr(priv);
|
||||
init_shim_clkctl(uc_priv, priv);
|
||||
init_sscr0(uc_priv, priv);
|
||||
init_sscr1(priv);
|
||||
init_sspsp(priv);
|
||||
init_ssp_time_slot(priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void bdw_i2s_enable(struct broadwell_i2s_priv *priv)
|
||||
{
|
||||
setbits_le32(&priv->regs->sscr0, SSP_SSC0_SSE);
|
||||
setbits_le32(&priv->regs->sstsa, SSP_SSTSA_EN);
|
||||
}
|
||||
|
||||
static void bdw_i2s_disable(struct broadwell_i2s_priv *priv)
|
||||
{
|
||||
clrbits_le32(&priv->regs->sstsa, SSP_SSTSA_EN);
|
||||
clrbits_le32(&priv->regs->sstsa, SSP_SSTSA_EN);
|
||||
}
|
||||
|
||||
static int broadwell_i2s_tx_data(struct udevice *dev, void *data,
|
||||
uint data_size)
|
||||
{
|
||||
struct broadwell_i2s_priv *priv = dev_get_priv(dev);
|
||||
u32 *ptr = data;
|
||||
|
||||
log_debug("data=%p, data_size=%x\n", data, data_size);
|
||||
if (data_size < SSP_FIFO_SIZE) {
|
||||
log_err("Invalid I2S data size\n");
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
/* Enable I2S interface */
|
||||
bdw_i2s_enable(priv);
|
||||
|
||||
/* Transfer data */
|
||||
while (data_size > 0) {
|
||||
ulong start = timer_get_us() + 100000;
|
||||
|
||||
/* Write data if transmit FIFO has room */
|
||||
if (readl(&priv->regs->sssr) & SSP_SSS_TNF) {
|
||||
writel(*ptr++, &priv->regs->ssdr);
|
||||
data_size -= sizeof(*ptr);
|
||||
} else {
|
||||
if ((long)(timer_get_us() - start) > 0) {
|
||||
/* Disable I2S interface */
|
||||
bdw_i2s_disable(priv);
|
||||
log_debug("I2S Transfer Timeout\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Disable I2S interface */
|
||||
bdw_i2s_disable(priv);
|
||||
log_debug("done\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int broadwell_i2s_probe(struct udevice *dev)
|
||||
{
|
||||
struct i2s_uc_priv *uc_priv = dev_get_uclass_priv(dev);
|
||||
struct broadwell_i2s_priv *priv = dev_get_priv(dev);
|
||||
struct udevice *adsp = dev_get_parent(dev);
|
||||
u32 bar0, offset;
|
||||
int ret;
|
||||
|
||||
bar0 = dm_pci_read_bar32(adsp, 0);
|
||||
if (!bar0) {
|
||||
log_debug("Cannot read adsp bar0\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
offset = dev_read_addr_index(dev, 0);
|
||||
if (offset == FDT_ADDR_T_NONE) {
|
||||
log_debug("Cannot read address index 0\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
uc_priv->base_address = bar0 + offset;
|
||||
|
||||
/*
|
||||
* Hard-code these values. If other settings are required we can add
|
||||
* this to the device tree.
|
||||
*/
|
||||
uc_priv->rfs = 64;
|
||||
uc_priv->bfs = 32;
|
||||
uc_priv->audio_pll_clk = 24 * 1000 * 1000;
|
||||
uc_priv->samplingrate = 48000;
|
||||
uc_priv->bitspersample = 16;
|
||||
uc_priv->channels = 2;
|
||||
uc_priv->id = 0;
|
||||
|
||||
priv->shim = (struct i2s_shim_regs *)uc_priv->base_address;
|
||||
priv->sfrm_polarity = SSP_FRMS_ACTIVE_LOW;
|
||||
priv->end_transfer_state = SSP_END_TRANSFER_STATE_LOW;
|
||||
priv->sclk_mode = SCLK_MODE_DDF_DSR_ISL;
|
||||
priv->rel_timing = NEXT_FRMS_WITH_LSB_PREVIOUS_FRM;
|
||||
priv->sclk_dummy_stop = 0;
|
||||
priv->sclk_frame_width = 31;
|
||||
|
||||
offset = dev_read_addr_index(dev, 1 + uc_priv->id);
|
||||
if (offset == FDT_ADDR_T_NONE) {
|
||||
log_debug("Cannot read address index %d\n", 1 + uc_priv->id);
|
||||
return -EINVAL;
|
||||
}
|
||||
log_debug("bar0=%x, uc_priv->base_address=%x, offset=%x\n", bar0,
|
||||
uc_priv->base_address, offset);
|
||||
priv->regs = (struct broadwell_i2s_regs *)(bar0 + offset);
|
||||
|
||||
ret = bdw_i2s_init(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2s_ops broadwell_i2s_ops = {
|
||||
.tx_data = broadwell_i2s_tx_data,
|
||||
};
|
||||
|
||||
static const struct udevice_id broadwell_i2s_ids[] = {
|
||||
{ .compatible = "intel,broadwell-i2s" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(broadwell_i2s) = {
|
||||
.name = "broadwell_i2s",
|
||||
.id = UCLASS_I2S,
|
||||
.of_match = broadwell_i2s_ids,
|
||||
.probe = broadwell_i2s_probe,
|
||||
.ops = &broadwell_i2s_ops,
|
||||
.priv_auto_alloc_size = sizeof(struct broadwell_i2s_priv),
|
||||
};
|
||||
@@ -0,0 +1,301 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* Intel Broadwell I2S driver
|
||||
*
|
||||
* Copyright 2019 Google LLC
|
||||
*
|
||||
* Modified from dc i2s/broadwell/broadwell.h
|
||||
*/
|
||||
|
||||
#ifndef __BROADWELL_I2S_H__
|
||||
#define __BROADWELL_I2S_H__
|
||||
|
||||
enum {
|
||||
SSP_FIFO_SIZE = 7,
|
||||
};
|
||||
|
||||
enum frame_sync_rel_timing_t {
|
||||
NEXT_FRMS_AFTER_END_OF_T4 = 0,
|
||||
NEXT_FRMS_WITH_LSB_PREVIOUS_FRM,
|
||||
};
|
||||
|
||||
enum frame_sync_pol_t {
|
||||
SSP_FRMS_ACTIVE_LOW = 0,
|
||||
SSP_FRMS_ACTIVE_HIGH,
|
||||
};
|
||||
|
||||
enum end_transfer_state_t {
|
||||
SSP_END_TRANSFER_STATE_LOW = 0,
|
||||
SSP_END_TRANSFER_STATE_PEVIOUS_BIT,
|
||||
};
|
||||
|
||||
enum clock_mode_t {
|
||||
/* Data driven (falling), data sampled (rising), idle state (low) */
|
||||
SCLK_MODE_DDF_DSR_ISL,
|
||||
/* Data driven (rising), data sampled (falling), idle state (low) */
|
||||
SCLK_MODE_DDR_DSF_ISL,
|
||||
/* Data driven (rising), data sampled (falling), idle state (high) */
|
||||
SCLK_MODE_DDR_DSF_ISH,
|
||||
/* Data driven (falling), data sampled (rising), idle state (high) */
|
||||
SCLK_MODE_DDF_DSR_ISH,
|
||||
};
|
||||
|
||||
struct i2s_shim_regs {
|
||||
u32 csr; /* 0x00 */
|
||||
u32 reserved0[29]; /* 0x14 - 0x77 */
|
||||
u32 clkctl; /* 0x78 */
|
||||
u32 reserved1; /* 0x7c */
|
||||
u32 cs2; /* 0x80 */
|
||||
};
|
||||
|
||||
struct broadwell_i2s_regs {
|
||||
u32 sscr0; /* 0x00 */
|
||||
u32 sscr1; /* 0x04 */
|
||||
u32 sssr; /* 0x08 */
|
||||
u32 ssitr; /* 0x0c */
|
||||
u32 ssdr; /* 0x10 */
|
||||
u32 reserved0[5]; /* 0x14 - 0x27 */
|
||||
u32 ssto; /* 0x28 */
|
||||
u32 sspsp; /* 0x2c */
|
||||
u32 sstsa; /* 0x30 */
|
||||
u32 ssrsa; /* 0x34 */
|
||||
u32 sstss; /* 0x38 */
|
||||
u32 sscr2; /* 0x40 */
|
||||
u32 sspsp2; /* 0x44 */
|
||||
};
|
||||
|
||||
/* SHIM Configuration & Status */
|
||||
enum {
|
||||
/* Low Power Clock Select */
|
||||
SHIM_CS_LPCS = 1 << 31,
|
||||
/* SSP Force Clock Running */
|
||||
SHIM_CS_SFCR_SSP1 = 1 << 28,
|
||||
SHIM_CS_SFCR_SSP0 = 1 << 27,
|
||||
/* SSP1 IO Clock Select */
|
||||
SHIM_CS_S1IOCS = 1 << 23,
|
||||
/* SSP0 IO Clock Select */
|
||||
SHIM_CS_S0IOCS = 1 << 21,
|
||||
/* Parity Check Enable */
|
||||
SHIM_CS_PCE = 1 << 15,
|
||||
/* SSP DMA or PIO Mode */
|
||||
SHIM_CS_SDPM_PIO_SSP1 = 1 << 12,
|
||||
SHIM_CS_SDPM_DMA_SSP1 = 0 << 12,
|
||||
SHIM_CS_SDPM_PIO_SSP0 = 1 << 11,
|
||||
SHIM_CS_SDPM_DMA_SSP0 = 0 << 11,
|
||||
/* Run / Stall */
|
||||
SHIM_CS_STALL = 1 << 10,
|
||||
/* DSP Clock Select */
|
||||
SHIM_CS_DCS_DSP320_AF80 = 0 << 4,
|
||||
SHIM_CS_DCS_DSP160_AF80 = 1 << 4,
|
||||
SHIM_CS_DCS_DSP80_AF80 = 2 << 4,
|
||||
SHIM_CS_DCS_DSP320_AF160 = 4 << 4,
|
||||
SHIM_CS_DCS_DSP160_AF160 = 5 << 4,
|
||||
SHIM_CS_DCS_DSP32_AF32 = 6 << 4,
|
||||
SHIM_CS_DCS_MASK = 7 << 4,
|
||||
/* SSP Base Clock Select */
|
||||
SHIM_CS_SBCS_SSP0_24MHZ = 1 << 3,
|
||||
SHIM_CS_SBCS_SSP0_32MHZ = 0 << 3,
|
||||
SHIM_CS_SBCS_SSP1_24MHZ = 1 << 2,
|
||||
SHIM_CS_SBCS_SSP1_32MHZ = 0 << 2,
|
||||
/* DSP Core Reset */
|
||||
SHIM_CS_RST = 1 << 1,
|
||||
};
|
||||
|
||||
/* SHIM Clock Control */
|
||||
enum {
|
||||
/* Clock Frequency Change In Progress */
|
||||
SHIM_CLKCTL_CFCIP = 1 << 31,
|
||||
/* SSP MCLK Output Select */
|
||||
SHIM_CLKCTL_MCLK_MASK = 0x3,
|
||||
SHIM_CLKCTL_MCLK_SHIFT = 24,
|
||||
SHIM_CLKCTL_MCLK_DISABLED = 0 << 24,
|
||||
SHIM_CLKCTL_MCLK_6MHZ = 1 << 24,
|
||||
SHIM_CLKCTL_MCLK_12MHZ = 2 << 24,
|
||||
SHIM_CLKCTL_MCLK_24MHZ = 3 << 24,
|
||||
/* DSP Core Prevent Local Clock Gating */
|
||||
SHIM_CLKCTL_DCPLCG = 1 << 18,
|
||||
/* SSP Clock Output Enable */
|
||||
SHIM_CLKCTL_SCOE_SSP1 = 1 << 17,
|
||||
SHIM_CLKCTL_SCOE_SSP0 = 1 << 16,
|
||||
/* DMA Engine Force Local Clock Gating */
|
||||
SHIM_CLKCTL_DEFLCGB_DMA1_CGE = 0 << 6,
|
||||
SHIM_CLKCTL_DEFLCGB_DMA1_CGD = 1 << 6,
|
||||
SHIM_CLKCTL_DEFLCGB_DMA0_CGE = 0 << 5,
|
||||
SHIM_CLKCTL_DEFLCGB_DMA0_CGD = 1 << 5,
|
||||
/* SSP Force Local Clock Gating */
|
||||
SHIM_CLKCTL_SFLCGB_SSP1_CGE = 0 << 1,
|
||||
SHIM_CLKCTL_SFLCGB_SSP1_CGD = 1 << 1,
|
||||
SHIM_CLKCTL_SFLCGB_SSP0_CGE = 0 << 0,
|
||||
SHIM_CLKCTL_SFLCGB_SSP0_CGD = 1 << 0,
|
||||
|
||||
/* Reserved bits: 30:26, 23:19, 15:7, 4:2 */
|
||||
SHIM_CLKCTL_RESERVED = 0x1f << 26 | 0x1f << 19 | 0x1ff << 7 | 0x7 << 2,
|
||||
};
|
||||
|
||||
/* SSP Status */
|
||||
enum {
|
||||
/* Bit Count Error */
|
||||
SSP_SSS_BCE = 1 << 23,
|
||||
/* Clock Sync Statu s*/
|
||||
SSP_SSS_CSS = 1 << 22,
|
||||
/* Transmit FIFO Underrun */
|
||||
SSP_SSS_TUR = 1 << 21,
|
||||
/* End Of Chain */
|
||||
SSP_SSS_EOC = 1 << 20,
|
||||
/* Receiver Time-out Interrupt */
|
||||
SSP_SSS_TINT = 1 << 19,
|
||||
/* Peripheral Trailing Byte Interrupt */
|
||||
SSP_SSS_PINT = 1 << 18,
|
||||
/* Received FIFO Level */
|
||||
SSP_RFL_MASK = 0xf,
|
||||
SSP_RFL_SHIFT = 12,
|
||||
/* Transmit FIFO Level */
|
||||
SSP_TFL_MASK = 0xf,
|
||||
SSP_TFL_SHIFT = 8,
|
||||
/* Receive FIFO Overrun */
|
||||
SSP_SSS_ROR = 1 << 7,
|
||||
/* Receive FIFO Service Request */
|
||||
SSP_SSS_RFS = 1 << 6,
|
||||
/* Transmit FIFO Service Request */
|
||||
SSP_SSS_TFS = 1 << 5,
|
||||
/* SSP Busy */
|
||||
SSP_SSS_BSY = 1 << 4,
|
||||
/* Receive FIFO Not Empty */
|
||||
SSP_SSS_RNE = 1 << 3,
|
||||
/* Transmit FIFO Not Full */
|
||||
SSP_SSS_TNF = 1 << 2,
|
||||
};
|
||||
|
||||
/* SSP Control 0 */
|
||||
enum {
|
||||
/* Mode */
|
||||
SSP_SSC0_MODE_NORMAL = 0 << 31,
|
||||
SSP_SSC0_MODE_NETWORK = 1 << 31,
|
||||
/* Audio Clock Select */
|
||||
SSP_SSC0_ACS_PCH = 0 << 30,
|
||||
/* Frame Rate Divider Control (0-7) */
|
||||
SSP_SSC0_FRDC_MASK = 0x7,
|
||||
SSP_SSC0_FRDC_SHIFT = 24,
|
||||
SSP_SSC0_FRDC_STEREO = 1 << 24,
|
||||
/* Transmit FIFO Underrun Interrupt Mask */
|
||||
SSP_SSC0_TIM = 1 << 23,
|
||||
/* Receive FIFO Underrun Interrupt Mask */
|
||||
SSP_SSC0_RIM = 1 << 22,
|
||||
/* Network Clock Select */
|
||||
SSP_SSC0_NCS_PCH = 0 << 21,
|
||||
/* Extended Data Size Select */
|
||||
SSP_SSC0_EDSS = 1 << 20,
|
||||
/* Serial Clock Rate (0-4095) */
|
||||
SSP_SSC0_SCR_SHIFT = 8,
|
||||
SSP_SSC0_SCR_MASK = 0xfff << SSP_SSC0_SCR_SHIFT,
|
||||
/* Synchronous Serial Port Enable */
|
||||
SSP_SSC0_SSE = 1 << 7,
|
||||
/* External Clock Select */
|
||||
SSP_SSC0_ECS_PCH = 0 << 6,
|
||||
/* Frame Format */
|
||||
SSP_SSC0_FRF_MOTOROLA_SPI = 0 << 4,
|
||||
SSP_SSC0_FRF_TI_SSP = 1 << 4,
|
||||
SSP_SSC0_FRF_NS_MICROWIRE = 2 << 4,
|
||||
SSP_SSC0_FRF_PSP = 3 << 4,
|
||||
/* Data Size Select */
|
||||
SSP_SSC0_DSS_SHIFT = 0,
|
||||
SSP_SSC0_DSS_MASK = 0xf << SSP_SSC0_DSS_SHIFT,
|
||||
};
|
||||
|
||||
/* SSP Control 1 */
|
||||
enum {
|
||||
/* TXD Tristate Enable on Last Phase */
|
||||
SSP_SSC1_TTELP = 1 << 31,
|
||||
/* TXD Tristate Enable */
|
||||
SSP_SSC1_TTE = 1 << 30,
|
||||
/* Enable Bit Count Error Interrupt */
|
||||
SSP_SSC1_EBCEI = 1 << 29,
|
||||
/* Slave Clock Running */
|
||||
SSP_SSC1_SCFR = 1 << 28,
|
||||
/* Enable Clock Request A */
|
||||
SSP_SSC1_ECRA = 1 << 27,
|
||||
/* Enable Clock Request B */
|
||||
SSP_SSC1_ECRB = 1 << 26,
|
||||
/* SSPCLK Direction */
|
||||
SSP_SSC1_SCLKDIR_SLAVE = 1 << 25,
|
||||
SSP_SSC1_SCLKDIR_MASTER = 0 << 25,
|
||||
/* SSPFRM Direction */
|
||||
SSP_SSC1_SFRMDIR_SLAVE = 1 << 24,
|
||||
SSP_SSC1_SFRMDIR_MASTER = 0 << 24,
|
||||
/* Receive without Transmit */
|
||||
SSP_SSC1_RWOT = 1 << 23,
|
||||
/* Trailing Byte */
|
||||
SSP_SSC1_TRAIL = 1 << 22,
|
||||
/* DMA Tx Service Request Enable */
|
||||
SSP_SSC1_TSRE = 1 << 21,
|
||||
/* DMA Rx Service Request Enable */
|
||||
SSP_SSC1_RSRE = 1 << 20,
|
||||
/* Receiver Timeout Interrupt Enable */
|
||||
SSP_SSC1_TINTE = 1 << 19,
|
||||
/* Periph. Trailing Byte Int. Enable */
|
||||
SSP_SSC1_PINTE = 1 << 18,
|
||||
/* Invert Frame Signal */
|
||||
SSP_SSC1_IFS = 1 << 16,
|
||||
/* Select FIFO for EFWR: test mode */
|
||||
SSP_SSC1_STRF = 1 << 15,
|
||||
/* Enable FIFO Write/Read: test mode */
|
||||
SSP_SSC1_EFWR = 1 << 14,
|
||||
/* Receive FIFO Trigger Threshold */
|
||||
SSP_SSC1_RFT_SHIFT = 10,
|
||||
SSP_SSC1_RFT_MASK = 0xf << SSP_SSC1_RFT_SHIFT,
|
||||
/* Transmit FIFO Trigger Threshold */
|
||||
SSP_SSC1_TFT_SHIFT = 6,
|
||||
SSP_SSC1_TFT_MASK = 0xf << SSP_SSC1_TFT_SHIFT,
|
||||
/* Microwire Transmit Data Size */
|
||||
SSP_SSC1_MWDS = 1 << 5,
|
||||
/* Motorola SPI SSPSCLK Phase Setting*/
|
||||
SSP_SSC1_SPH = 1 << 4,
|
||||
/* Motorola SPI SSPSCLK Polarity */
|
||||
SSP_SSC1_SPO = 1 << 3,
|
||||
/* Loopback mode: test mode */
|
||||
SSP_SSC1_LBM = 1 << 2,
|
||||
/* Transmit FIFO Interrupt Enable */
|
||||
SSP_SSC1_TIE = 1 << 1,
|
||||
/* Receive FIFO Interrupt Enable */
|
||||
SSP_SSC1_RIE = 1 << 0,
|
||||
|
||||
SSP_SSC1_RESERVED = 17 << 1,
|
||||
};
|
||||
|
||||
/* SSP Programmable Serial Protocol */
|
||||
enum {
|
||||
/* Extended Dummy Stop (0-31) */
|
||||
SSP_PSP_EDYMSTOP_SHIFT = 26,
|
||||
SSP_PSP_EDMYSTOP_MASK = 0x7 << SSP_PSP_EDYMSTOP_SHIFT,
|
||||
/* Frame Sync Relative Timing */
|
||||
SSP_PSP_FSRT = 1 << 25,
|
||||
/* Dummy Stop low bits */
|
||||
SSP_PSP_DMYSTOP_SHIFT = 23,
|
||||
SSP_PSP_DMYSTOP_MASK = 0x3 << SSP_PSP_DMYSTOP_SHIFT,
|
||||
/* Serial Frame Width */
|
||||
SSP_PSP_SFRMWDTH_SHIFT = 16,
|
||||
SSP_PSP_SFRMWDTH_MASK = 0x3f << SSP_PSP_SFRMWDTH_SHIFT,
|
||||
/* Serial Frame Delay */
|
||||
SSP_PSP_SFRMDLY_MASK = 0x7f,
|
||||
SSP_PSP_SFRMDLY_SHIFT = 9,
|
||||
/* Start Delay */
|
||||
SSP_PSP_STRTDLY_MASK = 0x7,
|
||||
SSP_PSP_STRTDLY_SHIFT = 4,
|
||||
/* End of Transfer Data State */
|
||||
SSP_PSP_ETDS = 1 << 3,
|
||||
/* Serial Frame Polarity */
|
||||
SSP_PSP_SFRMP = 1 << 2,
|
||||
/* Serial Clock Mode */
|
||||
SSP_PSP_SCMODE_SHIFT = 0,
|
||||
SSP_PSP_SCMODE_MASK = 0x3 << SSP_PSP_SCMODE_SHIFT,
|
||||
|
||||
SSP_PSP_RESERVED = 1 << 22,
|
||||
};
|
||||
|
||||
/* SSP TX Time Slot Active */
|
||||
enum {
|
||||
SSP_SSTSA_EN = 1 << 8,
|
||||
SSP_SSTSA_MASK = 0xff,
|
||||
};
|
||||
|
||||
#endif /* __BROADWELL_I2S_H__ */
|
||||
@@ -0,0 +1,65 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Sound for broadwell
|
||||
*
|
||||
* Copyright 2019 Google LLC
|
||||
* Written by Simon Glass <sjg@chromium.org>
|
||||
*/
|
||||
|
||||
#define LOG_CATEGORY UCLASS_SOUND
|
||||
|
||||
#include <common.h>
|
||||
#include <audio_codec.h>
|
||||
#include <dm.h>
|
||||
#include <i2s.h>
|
||||
#include <sound.h>
|
||||
|
||||
static int broadwell_sound_probe(struct udevice *dev)
|
||||
{
|
||||
return sound_find_codec_i2s(dev);
|
||||
}
|
||||
|
||||
static int broadwell_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 broadwell_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 const struct sound_ops broadwell_sound_ops = {
|
||||
.setup = broadwell_sound_setup,
|
||||
.play = broadwell_sound_play,
|
||||
};
|
||||
|
||||
static const struct udevice_id broadwell_sound_ids[] = {
|
||||
{ .compatible = "google,samus-sound" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(broadwell_sound_drv) = {
|
||||
.name = "broadwell_sound",
|
||||
.id = UCLASS_SOUND,
|
||||
.of_match = broadwell_sound_ids,
|
||||
.probe = broadwell_sound_probe,
|
||||
.ops = &broadwell_sound_ops,
|
||||
};
|
||||
@@ -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",
|
||||
};
|
||||
@@ -0,0 +1,556 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Implementation of per-board codec beeping
|
||||
* Copyright (c) 2011 The Chromium OS Authors.
|
||||
* Copyright 2018 Google LLC
|
||||
*/
|
||||
|
||||
#define LOG_CATEGORY UCLASS_SOUND
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <hda_codec.h>
|
||||
#include <pci.h>
|
||||
#include <sound.h>
|
||||
#include <asm/io.h>
|
||||
#include <dt-bindings/sound/azalia.h>
|
||||
|
||||
/**
|
||||
* struct hda_regs - HDA registers
|
||||
*
|
||||
* https://wiki.osdev.org/Intel_High_Definition_Audio
|
||||
* https://www.intel.com/content/www/us/en/standards/high-definition-audio-specification.html
|
||||
*/
|
||||
struct hda_regs {
|
||||
u16 gcap;
|
||||
u8 vmin;
|
||||
u8 vmaj;
|
||||
u16 outpay;
|
||||
u16 inpay;
|
||||
u32 gctl;
|
||||
u16 wakeen;
|
||||
u16 statests;
|
||||
u8 reserved[0x50];
|
||||
u32 cmd; /* 0x60 */
|
||||
u32 resp;
|
||||
u32 icii;
|
||||
};
|
||||
|
||||
enum {
|
||||
HDA_ICII_BUSY = BIT(0),
|
||||
HDA_ICII_VALID = BIT(1),
|
||||
|
||||
/* Common node IDs */
|
||||
HDA_ROOT_NODE = 0x00,
|
||||
|
||||
/* HDA verbs fields */
|
||||
HDA_VERB_NID_S = 20,
|
||||
HDA_VERB_VERB_S = 8,
|
||||
HDA_VERB_PARAM_S = 0,
|
||||
|
||||
HDA_VERB_GET_PARAMS = 0xf00,
|
||||
HDA_VERB_SET_BEEP = 0x70a,
|
||||
|
||||
/* GET_PARAMS parameter IDs */
|
||||
GET_PARAMS_NODE_COUNT = 0x04,
|
||||
GET_PARAMS_AUDIO_GROUP_CAPS = 0x08,
|
||||
GET_PARAMS_AUDIO_WIDGET_CAPS = 0x09,
|
||||
|
||||
/* Sub-node fields */
|
||||
NUM_SUB_NODES_S = 0,
|
||||
NUM_SUB_NODES_M = 0xff << NUM_SUB_NODES_S,
|
||||
FIRST_SUB_NODE_S = 16,
|
||||
FIRST_SUB_NODE_M = 0xff << FIRST_SUB_NODE_S,
|
||||
|
||||
/* Get Audio Function Group Capabilities fields */
|
||||
AUDIO_GROUP_CAPS_BEEP_GEN = 0x10000,
|
||||
|
||||
/* Get Audio Widget Capabilities fields */
|
||||
AUDIO_WIDGET_TYPE_BEEP = 0x7,
|
||||
AUDIO_WIDGET_TYPE_S = 20,
|
||||
AUDIO_WIDGET_TYPE_M = 0xf << AUDIO_WIDGET_TYPE_S,
|
||||
|
||||
BEEP_FREQ_BASE = 12000,
|
||||
};
|
||||
|
||||
static inline uint hda_verb(uint nid, uint verb, uint param)
|
||||
{
|
||||
return nid << HDA_VERB_NID_S | verb << HDA_VERB_VERB_S |
|
||||
param << HDA_VERB_PARAM_S;
|
||||
}
|
||||
|
||||
int hda_wait_for_ready(struct hda_regs *regs)
|
||||
{
|
||||
int timeout = 1000; /* Use a 1msec timeout */
|
||||
|
||||
while (timeout--) {
|
||||
u32 reg32 = readl(®s->icii);
|
||||
|
||||
if (!(reg32 & HDA_ICII_BUSY))
|
||||
return 0;
|
||||
udelay(1);
|
||||
}
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static int wait_for_response(struct hda_regs *regs, uint *response)
|
||||
{
|
||||
int timeout = 1000;
|
||||
u32 reg32;
|
||||
|
||||
/* Send the verb to the codec */
|
||||
setbits_le32(®s->icii, HDA_ICII_BUSY | HDA_ICII_VALID);
|
||||
|
||||
/* Use a 1msec timeout */
|
||||
while (timeout--) {
|
||||
reg32 = readl(®s->icii);
|
||||
if ((reg32 & (HDA_ICII_VALID | HDA_ICII_BUSY)) ==
|
||||
HDA_ICII_VALID) {
|
||||
if (response)
|
||||
*response = readl(®s->resp);
|
||||
return 0;
|
||||
}
|
||||
udelay(1);
|
||||
}
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
int hda_wait_for_valid(struct hda_regs *regs)
|
||||
{
|
||||
return wait_for_response(regs, NULL);
|
||||
}
|
||||
|
||||
static int set_bits(void *port, u32 mask, u32 val)
|
||||
{
|
||||
u32 reg32;
|
||||
int count;
|
||||
|
||||
/* Write (val & mask) to port */
|
||||
clrsetbits_le32(port, mask, val);
|
||||
|
||||
/* Wait for readback of register to match what was just written to it */
|
||||
count = 50;
|
||||
do {
|
||||
/* Wait 1ms based on BKDG wait time */
|
||||
mdelay(1);
|
||||
reg32 = readl(port) & mask;
|
||||
} while (reg32 != val && --count);
|
||||
|
||||
/* Timeout occurred */
|
||||
if (!count)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hda_codec_detect(struct hda_regs *regs)
|
||||
{
|
||||
uint reg8;
|
||||
|
||||
/* Set Bit 0 to 1 to exit reset state (BAR + 0x8)[0] */
|
||||
if (set_bits(®s->gctl, 1, 1))
|
||||
goto no_codec;
|
||||
|
||||
/* Write back the value once reset bit is set */
|
||||
writew(readw(®s->gcap), ®s->gcap);
|
||||
|
||||
/* Read in Codec location */
|
||||
reg8 = readb(®s->statests) & 0xf;
|
||||
if (!reg8)
|
||||
goto no_codec;
|
||||
|
||||
return reg8;
|
||||
|
||||
no_codec:
|
||||
/* Codec Not found - put HDA back in reset */
|
||||
set_bits(®s->gctl, 1, 0);
|
||||
log_debug("No codec\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int find_verb_data(struct udevice *dev, uint id, ofnode *nodep)
|
||||
{
|
||||
ofnode parent = dev_read_subnode(dev, "codecs");
|
||||
ofnode node;
|
||||
u32 vendor_id, device_id;
|
||||
|
||||
ofnode_for_each_subnode(node, parent) {
|
||||
if (ofnode_read_u32(node, "vendor-id", &vendor_id) ||
|
||||
ofnode_read_u32(node, "device-id", &device_id)) {
|
||||
log_debug("Cannot get IDs for '%s'\n",
|
||||
ofnode_get_name(node));
|
||||
return -EINVAL;
|
||||
}
|
||||
if (id != (vendor_id << 16 | device_id)) {
|
||||
log_debug("Skip codec node '%s' for %08x\n",
|
||||
ofnode_get_name(node), id);
|
||||
continue;
|
||||
}
|
||||
|
||||
log_debug("Found codec node '%s' for %08x\n",
|
||||
ofnode_get_name(node), id);
|
||||
*nodep = node;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
static int send_verbs(ofnode node, const char *prop_name, struct hda_regs *regs)
|
||||
{
|
||||
int ret, verb_size, i;
|
||||
const u32 *verb;
|
||||
|
||||
verb = ofnode_get_property(node, prop_name, &verb_size);
|
||||
if (verb_size < 0) {
|
||||
log_debug("No verb data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
log_debug("verb_size: %d\n", verb_size);
|
||||
|
||||
for (i = 0; i < verb_size / sizeof(*verb); i++) {
|
||||
ret = hda_wait_for_ready(regs);
|
||||
if (ret) {
|
||||
log_debug(" codec ready timeout\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
writel(fdt32_to_cpu(verb[i]), ®s->cmd);
|
||||
|
||||
ret = hda_wait_for_valid(regs);
|
||||
if (ret) {
|
||||
log_debug(" codec valid timeout\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int codec_init(struct udevice *dev, struct hda_regs *regs, uint addr)
|
||||
{
|
||||
ofnode node;
|
||||
uint id;
|
||||
int ret;
|
||||
|
||||
log_debug("Initializing codec #%d\n", addr);
|
||||
ret = hda_wait_for_ready(regs);
|
||||
if (ret) {
|
||||
log_debug(" codec not ready\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Read the codec's vendor ID */
|
||||
writel(addr << AZALIA_CODEC_SHIFT |
|
||||
AZALIA_OPCODE_READ_PARAM << AZALIA_VERB_SHIFT |
|
||||
AZALIA_PARAM_VENDOR_ID, ®s->cmd);
|
||||
ret = hda_wait_for_valid(regs);
|
||||
if (ret) {
|
||||
log_debug(" codec not valid\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
id = readl(®s->resp);
|
||||
log_debug("codec vid/did: %08x\n", id);
|
||||
ret = find_verb_data(dev, id, &node);
|
||||
if (ret) {
|
||||
log_debug("No verb (err=%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
ret = send_verbs(node, "verbs", regs);
|
||||
if (ret) {
|
||||
log_debug("failed to send verbs (err=%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
log_debug("verb loaded\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hda_codecs_init(struct udevice *dev, struct hda_regs *regs, u32 codec_mask)
|
||||
{
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
for (i = 3; i >= 0; i--) {
|
||||
if (codec_mask & (1 << i)) {
|
||||
ret = codec_init(dev, regs, i);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = send_verbs(dev_ofnode(dev), "beep-verbs", regs);
|
||||
if (ret) {
|
||||
log_debug("failed to send beep verbs (err=%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
log_debug("beep verbs loaded\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* exec_verb() - Write a verb to the codec
|
||||
*
|
||||
* @regs: HDA registers
|
||||
* @val: Command to write
|
||||
* @response: Set to response from codec
|
||||
* @return 0 if OK, -ve on error
|
||||
*/
|
||||
static int exec_verb(struct hda_regs *regs, uint val, uint *response)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = hda_wait_for_ready(regs);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
writel(val, ®s->cmd);
|
||||
|
||||
return wait_for_response(regs, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* get_subnode_info() - Get subnode information
|
||||
*
|
||||
* @regs: HDA registers
|
||||
* @nid: Parent node ID to check
|
||||
* @num_sub_nodesp: Returns number of subnodes
|
||||
* @start_sub_node_nidp: Returns start subnode number
|
||||
* @return 0 if OK, -ve on error
|
||||
*/
|
||||
static int get_subnode_info(struct hda_regs *regs, uint nid,
|
||||
uint *num_sub_nodesp, uint *start_sub_node_nidp)
|
||||
{
|
||||
uint response;
|
||||
int ret;
|
||||
|
||||
ret = exec_verb(regs, hda_verb(nid, HDA_VERB_GET_PARAMS,
|
||||
GET_PARAMS_NODE_COUNT),
|
||||
&response);
|
||||
if (ret < 0) {
|
||||
printf("Audio: Error reading sub-node info %d\n", nid);
|
||||
return ret;
|
||||
}
|
||||
|
||||
*num_sub_nodesp = (response & NUM_SUB_NODES_M) >> NUM_SUB_NODES_S;
|
||||
*start_sub_node_nidp = (response & FIRST_SUB_NODE_M) >>
|
||||
FIRST_SUB_NODE_S;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* find_beep_node_in_group() - Finds the beeping node
|
||||
*
|
||||
* Searches the audio group for a node that supports beeping
|
||||
*
|
||||
* @regs: HDA registers
|
||||
* @group_nid: Group node ID to check
|
||||
* @return 0 if OK, -ve on error
|
||||
*/
|
||||
static uint find_beep_node_in_group(struct hda_regs *regs, uint group_nid)
|
||||
{
|
||||
uint node_count = 0;
|
||||
uint current_nid = 0;
|
||||
uint response;
|
||||
uint end_nid;
|
||||
int ret;
|
||||
|
||||
ret = get_subnode_info(regs, group_nid, &node_count, ¤t_nid);
|
||||
if (ret < 0)
|
||||
return 0;
|
||||
|
||||
end_nid = current_nid + node_count;
|
||||
while (current_nid < end_nid) {
|
||||
ret = exec_verb(regs,
|
||||
hda_verb(current_nid, HDA_VERB_GET_PARAMS,
|
||||
GET_PARAMS_AUDIO_WIDGET_CAPS),
|
||||
&response);
|
||||
if (ret < 0) {
|
||||
printf("Audio: Error reading widget caps\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((response & AUDIO_WIDGET_TYPE_M) >> AUDIO_WIDGET_TYPE_S ==
|
||||
AUDIO_WIDGET_TYPE_BEEP)
|
||||
return current_nid;
|
||||
|
||||
current_nid++;
|
||||
}
|
||||
|
||||
return 0; /* no beep node found */
|
||||
}
|
||||
|
||||
/**
|
||||
* audio_group_has_beep_node() - Check if group has a beep node
|
||||
*
|
||||
* Checks if the given audio group contains a beep generator
|
||||
* @regs: HDA registers
|
||||
* @nid: Node ID to check
|
||||
* @return 0 if OK, -ve on error
|
||||
*/
|
||||
static int audio_group_has_beep_node(struct hda_regs *regs, uint nid)
|
||||
{
|
||||
uint response;
|
||||
int ret;
|
||||
|
||||
ret = exec_verb(regs, hda_verb(nid, HDA_VERB_GET_PARAMS,
|
||||
GET_PARAMS_AUDIO_GROUP_CAPS),
|
||||
&response);
|
||||
if (ret < 0) {
|
||||
printf("Audio: Error reading audio group caps %d\n", nid);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return !!(response & AUDIO_GROUP_CAPS_BEEP_GEN);
|
||||
}
|
||||
|
||||
/**
|
||||
* get_hda_beep_nid() - Finds the node ID of the beep node
|
||||
*
|
||||
* Finds the nid of the beep node if it exists. Starts at the root node, for
|
||||
* each sub-node checks if the group contains a beep node. If the group
|
||||
* contains a beep node, polls each node in the group until it is found.
|
||||
*
|
||||
* If the device has a intel,beep-nid property, the value of that is used
|
||||
* instead.
|
||||
*
|
||||
* @dev: Sound device
|
||||
* @return Node ID >0 if found, -ve error code otherwise
|
||||
*/
|
||||
static int get_hda_beep_nid(struct udevice *dev)
|
||||
{
|
||||
struct hda_codec_priv *priv = dev_get_priv(dev);
|
||||
uint current_nid = 0;
|
||||
uint node_count = 0;
|
||||
uint end_nid;
|
||||
int ret;
|
||||
|
||||
/* If the field exists, use the beep nid set in the fdt */
|
||||
ret = dev_read_u32(dev, "intel,beep-nid", ¤t_nid);
|
||||
if (!ret)
|
||||
return current_nid;
|
||||
|
||||
ret = get_subnode_info(priv->regs, HDA_ROOT_NODE, &node_count,
|
||||
¤t_nid);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
end_nid = current_nid + node_count;
|
||||
while (current_nid < end_nid) {
|
||||
if (audio_group_has_beep_node(priv->regs, current_nid))
|
||||
return find_beep_node_in_group(priv->regs,
|
||||
current_nid);
|
||||
current_nid++;
|
||||
}
|
||||
/* no beep node found */
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
/**
|
||||
* set_beep_divisor() - Sets the beep divisor to set the pitch
|
||||
*
|
||||
* @priv: Device's private data
|
||||
* @divider: Divider value (0 to disable the beep)
|
||||
* @return 0 if OK, -ve on error
|
||||
*/
|
||||
static int set_beep_divisor(struct hda_codec_priv *priv, uint divider)
|
||||
{
|
||||
return exec_verb(priv->regs,
|
||||
hda_verb(priv->beep_nid, HDA_VERB_SET_BEEP, divider),
|
||||
NULL);
|
||||
}
|
||||
|
||||
int hda_codec_init(struct udevice *dev)
|
||||
{
|
||||
struct hda_codec_priv *priv = dev_get_priv(dev);
|
||||
ulong base_addr;
|
||||
|
||||
base_addr = dm_pci_read_bar32(dev, 0);
|
||||
log_debug("base = %08lx\n", base_addr);
|
||||
if (!base_addr)
|
||||
return -EINVAL;
|
||||
|
||||
priv->regs = (struct hda_regs *)base_addr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hda_codec_finish_init(struct udevice *dev)
|
||||
{
|
||||
struct hda_codec_priv *priv = dev_get_priv(dev);
|
||||
int ret;
|
||||
|
||||
ret = get_hda_beep_nid(dev);
|
||||
if (ret <= 0) {
|
||||
log_warning("Could not find beep NID (err=%d)\n", ret);
|
||||
return ret ? ret : -ENOENT;
|
||||
}
|
||||
priv->beep_nid = ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hda_codec_start_beep(struct udevice *dev, int frequency_hz)
|
||||
{
|
||||
struct hda_codec_priv *priv = dev_get_priv(dev);
|
||||
uint divider_val;
|
||||
|
||||
if (!priv->beep_nid) {
|
||||
log_err("Failed to find a beep-capable node\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
if (!frequency_hz)
|
||||
divider_val = 0; /* off */
|
||||
else if (frequency_hz > BEEP_FREQ_BASE)
|
||||
divider_val = 1;
|
||||
else if (frequency_hz < BEEP_FREQ_BASE / 0xff)
|
||||
divider_val = 0xff;
|
||||
else
|
||||
divider_val = 0xff & (BEEP_FREQ_BASE / frequency_hz);
|
||||
|
||||
return set_beep_divisor(priv, divider_val);
|
||||
}
|
||||
|
||||
int hda_codec_stop_beep(struct udevice *dev)
|
||||
{
|
||||
struct hda_codec_priv *priv = dev_get_priv(dev);
|
||||
|
||||
return set_beep_divisor(priv, 0);
|
||||
}
|
||||
|
||||
static const struct sound_ops hda_codec_ops = {
|
||||
.setup = hda_codec_finish_init,
|
||||
.start_beep = hda_codec_start_beep,
|
||||
.stop_beep = hda_codec_stop_beep,
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(hda_codec) = {
|
||||
.name = "hda_codec",
|
||||
.id = UCLASS_SOUND,
|
||||
.ops = &hda_codec_ops,
|
||||
.priv_auto_alloc_size = sizeof(struct hda_codec_priv),
|
||||
.probe = hda_codec_init,
|
||||
};
|
||||
|
||||
static struct pci_device_id hda_supported[] = {
|
||||
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_COUGARPOINT_HDA},
|
||||
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PANTHERPOINT_HDA},
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL,
|
||||
PCI_DEVICE_ID_INTEL_WILDCATPOINT_HDA) },
|
||||
|
||||
/*
|
||||
* Note this driver is not necessarily generic, but it attempts to
|
||||
* support any codec in the hd-audio class
|
||||
*/
|
||||
{ PCI_DEVICE_CLASS(PCI_CLASS_MULTIMEDIA_HD_AUDIO, 0xffffff) },
|
||||
};
|
||||
|
||||
U_BOOT_PCI_DEVICE(hda_codec, hda_supported);
|
||||
@@ -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),
|
||||
};
|
||||
@@ -0,0 +1,38 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright 2018 Google LLC
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <sound.h>
|
||||
#include <asm/i8254.h>
|
||||
|
||||
int i8254_start_beep(struct udevice *dev, int frequency_hz)
|
||||
{
|
||||
return i8254_enable_beep(frequency_hz);
|
||||
}
|
||||
|
||||
int i8254_stop_beep(struct udevice *dev)
|
||||
{
|
||||
i8254_disable_beep();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct sound_ops i8254_ops = {
|
||||
.start_beep = i8254_start_beep,
|
||||
.stop_beep = i8254_stop_beep,
|
||||
};
|
||||
|
||||
static const struct udevice_id i8254_ids[] = {
|
||||
{ .compatible = "i8254,beeper" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(i8254_drv) = {
|
||||
.name = "i8254_drv",
|
||||
.id = UCLASS_SOUND,
|
||||
.of_match = i8254_ids,
|
||||
.ops = &i8254_ops,
|
||||
};
|
||||
@@ -0,0 +1,137 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Intel HDA audio (Azalia) for ivybridge
|
||||
*
|
||||
* Originally from coreboot file bd82x6x/azalia.c
|
||||
*
|
||||
* Copyright (C) 2008 Advanced Micro Devices, Inc.
|
||||
* Copyright (C) 2008-2009 coresystems GmbH
|
||||
* Copyright (C) 2011 The ChromiumOS Authors.
|
||||
* Copyright 2018 Google LLC
|
||||
*/
|
||||
|
||||
#define LOG_CATEGORY UCLASS_SOUND
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <hda_codec.h>
|
||||
#include <pch.h>
|
||||
#include <sound.h>
|
||||
|
||||
static int bd82x6x_azalia_probe(struct udevice *dev)
|
||||
{
|
||||
struct pci_child_platdata *plat;
|
||||
struct hda_codec_priv *priv;
|
||||
struct udevice *pch;
|
||||
u32 codec_mask;
|
||||
int conf;
|
||||
int ret;
|
||||
|
||||
/* Only init after relocation */
|
||||
if (!(gd->flags & GD_FLG_RELOC))
|
||||
return 0;
|
||||
|
||||
ret = hda_codec_init(dev);
|
||||
if (ret) {
|
||||
log_debug("Cannot set up HDA codec (err=%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
priv = dev_get_priv(dev);
|
||||
|
||||
ret = uclass_first_device_err(UCLASS_PCH, &pch);
|
||||
log_debug("PCH %p %s\n", pch, pch->name);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
conf = pch_ioctl(pch, PCH_REQ_HDA_CONFIG, NULL, 0);
|
||||
log_debug("conf = %x\n", conf);
|
||||
if (conf >= 0) {
|
||||
dm_pci_clrset_config32(dev, 0x120, 7 << 24 | 0xfe,
|
||||
1 << 24 | /* 2 << 24 for server */
|
||||
conf);
|
||||
|
||||
dm_pci_clrset_config16(dev, 0x78, 0, 1 << 1);
|
||||
} else {
|
||||
log_debug("V1CTL disabled\n");
|
||||
}
|
||||
dm_pci_clrset_config32(dev, 0x114, 0xfe, 0);
|
||||
|
||||
/* Set VCi enable bit */
|
||||
dm_pci_clrset_config32(dev, 0x120, 0, 1U << 31);
|
||||
|
||||
/* Enable HDMI codec */
|
||||
dm_pci_clrset_config32(dev, 0xc4, 0, 1 << 1);
|
||||
dm_pci_clrset_config8(dev, 0x43, 0, 1 << 6);
|
||||
|
||||
/* Additional programming steps */
|
||||
dm_pci_clrset_config32(dev, 0xc4, 0, 1 << 13);
|
||||
dm_pci_clrset_config32(dev, 0xc4, 0, 1 << 10);
|
||||
dm_pci_clrset_config32(dev, 0xd0, 1U << 31, 0);
|
||||
|
||||
/* Additional step on Panther Point */
|
||||
plat = dev_get_parent_platdata(dev);
|
||||
if (plat->device == PCI_DEVICE_ID_INTEL_PANTHERPOINT_HDA)
|
||||
dm_pci_clrset_config32(dev, 0xc4, 0, 1 << 17);
|
||||
|
||||
dm_pci_write_config8(dev, 0x3c, 0xa); /* unused? */
|
||||
|
||||
/* Audio Control: Select Azalia mode */
|
||||
dm_pci_clrset_config8(dev, 0x40, 0, 1);
|
||||
dm_pci_clrset_config8(dev, 0x4d, 1 << 7, 0); /* Docking not supported */
|
||||
codec_mask = hda_codec_detect(priv->regs);
|
||||
log_debug("codec_mask = %02x\n", codec_mask);
|
||||
|
||||
if (codec_mask) {
|
||||
ret = hda_codecs_init(dev, priv->regs, codec_mask);
|
||||
if (ret) {
|
||||
log_err("Codec init failed (err=%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/* Enable dynamic clock gating */
|
||||
dm_pci_clrset_config8(dev, 0x43, 7, BIT(2) | BIT(0));
|
||||
|
||||
ret = hda_codec_finish_init(dev);
|
||||
if (ret) {
|
||||
log_debug("Cannot set up HDA codec (err=%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bd82x6x_azalia_setup(struct udevice *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bd82x6x_azalia_start_beep(struct udevice *dev, int frequency_hz)
|
||||
{
|
||||
return hda_codec_start_beep(dev, frequency_hz);
|
||||
}
|
||||
|
||||
int bd82x6x_azalia_stop_beep(struct udevice *dev)
|
||||
{
|
||||
return hda_codec_stop_beep(dev);
|
||||
}
|
||||
|
||||
static const struct sound_ops bd82x6x_azalia_ops = {
|
||||
.setup = bd82x6x_azalia_setup,
|
||||
.start_beep = bd82x6x_azalia_start_beep,
|
||||
.stop_beep = bd82x6x_azalia_stop_beep,
|
||||
};
|
||||
|
||||
static const struct udevice_id bd82x6x_azalia_ids[] = {
|
||||
{ .compatible = "intel,hd-audio" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(bd82x6x_azalia_drv) = {
|
||||
.name = "bd82x6x-hda",
|
||||
.id = UCLASS_SOUND,
|
||||
.of_match = bd82x6x_azalia_ids,
|
||||
.probe = bd82x6x_azalia_probe,
|
||||
.ops = &bd82x6x_azalia_ops,
|
||||
.priv_auto_alloc_size = sizeof(struct hda_codec_priv),
|
||||
};
|
||||
@@ -0,0 +1,424 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* max98088.c -- MAX98088 ALSA SoC Audio driver
|
||||
*
|
||||
* Copyright 2010 Maxim Integrated Products
|
||||
*
|
||||
* Modified for U-Boot by Chih-Chung Chang (chihchung@chromium.org),
|
||||
* following the changes made in max98095.c
|
||||
*/
|
||||
|
||||
#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 "maxim_codec.h"
|
||||
#include "max98088.h"
|
||||
|
||||
/* codec mclk clock divider coefficients. Index 0 is reserved. */
|
||||
static const int rate_table[] = {0, 8000, 11025, 16000, 22050, 24000, 32000,
|
||||
44100, 48000, 88200, 96000};
|
||||
|
||||
/*
|
||||
* codec mclk clock divider coefficients based on sampling rate
|
||||
*
|
||||
* @param rate sampling rate
|
||||
* @param value address of indexvalue to be stored
|
||||
*
|
||||
* @return 0 for success or negative error code.
|
||||
*/
|
||||
static int rate_value(int rate, u8 *value)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 1; i < ARRAY_SIZE(rate_table); i++) {
|
||||
if (rate_table[i] >= rate) {
|
||||
*value = i;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
*value = 1;
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets hw params for max98088
|
||||
*
|
||||
* @priv: max98088 information pointer
|
||||
* @rate: Sampling rate
|
||||
* @bits_per_sample: Bits per sample
|
||||
*
|
||||
* @return -EIO for error, 0 for success.
|
||||
*/
|
||||
int max98088_hw_params(struct maxim_priv *priv, unsigned int rate,
|
||||
unsigned int bits_per_sample)
|
||||
{
|
||||
int error;
|
||||
u8 regval;
|
||||
|
||||
switch (bits_per_sample) {
|
||||
case 16:
|
||||
error = maxim_bic_or(priv, M98088_REG_DAI1_FORMAT,
|
||||
M98088_DAI_WS, 0);
|
||||
break;
|
||||
case 24:
|
||||
error = maxim_bic_or(priv, M98088_REG_DAI1_FORMAT,
|
||||
M98088_DAI_WS, M98088_DAI_WS);
|
||||
break;
|
||||
default:
|
||||
debug("%s: Illegal bits per sample %d.\n",
|
||||
__func__, bits_per_sample);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
error |= maxim_bic_or(priv, M98088_REG_PWR_SYS, M98088_SHDNRUN, 0);
|
||||
|
||||
if (rate_value(rate, ®val)) {
|
||||
debug("%s: Failed to set sample rate to %d.\n",
|
||||
__func__, rate);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
error |= maxim_bic_or(priv, M98088_REG_DAI1_CLKMODE,
|
||||
M98088_CLKMODE_MASK, regval << 4);
|
||||
priv->rate = rate;
|
||||
|
||||
/* Update sample rate mode */
|
||||
if (rate < 50000)
|
||||
error |= maxim_bic_or(priv, M98088_REG_DAI1_FILTERS,
|
||||
M98088_DAI_DHF, 0);
|
||||
else
|
||||
error |= maxim_bic_or(priv, M98088_REG_DAI1_FILTERS,
|
||||
M98088_DAI_DHF, M98088_DAI_DHF);
|
||||
|
||||
error |= maxim_bic_or(priv, M98088_REG_PWR_SYS, M98088_SHDNRUN,
|
||||
M98088_SHDNRUN);
|
||||
|
||||
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: max98088 information
|
||||
* @freq: Sampling frequency in Hz
|
||||
*
|
||||
* @return -EIO for error, 0 for success.
|
||||
*/
|
||||
int max98088_set_sysclk(struct maxim_priv *priv, unsigned int freq)
|
||||
{
|
||||
int error = 0;
|
||||
u8 pwr;
|
||||
|
||||
/* 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 30MHz)..
|
||||
*/
|
||||
if (freq >= 10000000 && freq < 20000000) {
|
||||
error = maxim_i2c_write(priv, M98088_REG_SYS_CLK, 0x10);
|
||||
} else if ((freq >= 20000000) && (freq < 30000000)) {
|
||||
error = maxim_i2c_write(priv, M98088_REG_SYS_CLK, 0x20);
|
||||
} else {
|
||||
debug("%s: Invalid master clock frequency\n", __func__);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
error |= maxim_i2c_read(priv, M98088_REG_PWR_SYS, &pwr);
|
||||
if (pwr & M98088_SHDNRUN) {
|
||||
error |= maxim_bic_or(priv, M98088_REG_PWR_SYS,
|
||||
M98088_SHDNRUN, 0);
|
||||
error |= maxim_bic_or(priv, M98088_REG_PWR_SYS,
|
||||
M98088_SHDNRUN, M98088_SHDNRUN);
|
||||
}
|
||||
|
||||
debug("%s: Clock at %uHz\n", __func__, freq);
|
||||
if (error < 0)
|
||||
return -EIO;
|
||||
|
||||
priv->sysclk = freq;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets Max98090 I2S format
|
||||
*
|
||||
* @priv: max98088 information
|
||||
* @fmt: i2S format - supports a subset of the options defined in i2s.h.
|
||||
*
|
||||
* @return -EIO for error, 0 for success.
|
||||
*/
|
||||
int max98088_set_fmt(struct maxim_priv *priv, int fmt)
|
||||
{
|
||||
u8 reg15val;
|
||||
u8 reg14val = 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:
|
||||
/* Slave mode PLL */
|
||||
error |= maxim_i2c_write(priv, M98088_REG_DAI1_CLKCFG_HI,
|
||||
0x80);
|
||||
error |= maxim_i2c_write(priv, M98088_REG_DAI1_CLKCFG_LO,
|
||||
0x00);
|
||||
break;
|
||||
case SND_SOC_DAIFMT_CBM_CFM:
|
||||
/* Set to master mode */
|
||||
reg14val |= M98088_DAI_MAS;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_CBS_CFM:
|
||||
case SND_SOC_DAIFMT_CBM_CFS:
|
||||
default:
|
||||
debug("%s: Clock mode unsupported\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
||||
case SND_SOC_DAIFMT_I2S:
|
||||
reg14val |= M98088_DAI_DLY;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_LEFT_J:
|
||||
break;
|
||||
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:
|
||||
reg14val |= M98088_DAI_WCI;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_IB_NF:
|
||||
reg14val |= M98088_DAI_BCI;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_IB_IF:
|
||||
reg14val |= M98088_DAI_BCI | M98088_DAI_WCI;
|
||||
break;
|
||||
default:
|
||||
debug("%s: Unrecognized inversion settings.\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
error |= maxim_bic_or(priv, M98088_REG_DAI1_FORMAT,
|
||||
M98088_DAI_MAS | M98088_DAI_DLY | M98088_DAI_BCI |
|
||||
M98088_DAI_WCI, reg14val);
|
||||
reg15val = M98088_DAI_BSEL64;
|
||||
error |= maxim_i2c_write(priv, M98088_REG_DAI1_CLOCK, reg15val);
|
||||
|
||||
if (error < 0) {
|
||||
debug("%s: Error setting i2s format.\n", __func__);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* max98088_reset() - reset the audio codec
|
||||
*
|
||||
* @priv: max98088 information
|
||||
* @return -EIO for error, 0 for success.
|
||||
*/
|
||||
static int max98088_reset(struct maxim_priv *priv)
|
||||
{
|
||||
int ret, i;
|
||||
u8 val;
|
||||
|
||||
/*
|
||||
* Reset to hardware default for registers, as there is not a soft
|
||||
* reset hardware control register.
|
||||
*/
|
||||
for (i = M98088_REG_IRQ_ENABLE; i <= M98088_REG_PWR_SYS; i++) {
|
||||
switch (i) {
|
||||
case M98088_REG_BIAS_CNTL:
|
||||
val = 0xf0;
|
||||
break;
|
||||
case M98088_REG_DAC_BIAS2:
|
||||
val = 0x0f;
|
||||
break;
|
||||
default:
|
||||
val = 0;
|
||||
}
|
||||
ret = maxim_i2c_write(priv, i, val);
|
||||
if (ret < 0) {
|
||||
debug("%s: Failed to reset: %d\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* max98088_device_init() - Initialise max98088 codec device
|
||||
*
|
||||
* @priv: max98088 information
|
||||
*
|
||||
* @return -EIO for error, 0 for success.
|
||||
*/
|
||||
static int max98088_device_init(struct maxim_priv *priv)
|
||||
{
|
||||
unsigned char id;
|
||||
int error = 0;
|
||||
|
||||
/* reset the codec, the DSP core, and disable all interrupts */
|
||||
error = max98088_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, M98088_REG_REV_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 max98088_setup_interface(struct maxim_priv *priv)
|
||||
{
|
||||
int error;
|
||||
|
||||
/* Reading interrupt status to clear them */
|
||||
error = maxim_i2c_write(priv, M98088_REG_PWR_SYS, M98088_PWRSV);
|
||||
error |= maxim_i2c_write(priv, M98088_REG_IRQ_ENABLE, 0x00);
|
||||
|
||||
/*
|
||||
* initialize registers to hardware default configuring audio
|
||||
* interface2 to DAI1
|
||||
*/
|
||||
error |= maxim_i2c_write(priv, M98088_REG_MIX_DAC,
|
||||
M98088_DAI1L_TO_DACL | M98088_DAI1R_TO_DACR);
|
||||
error |= maxim_i2c_write(priv, M98088_REG_BIAS_CNTL, 0xF0);
|
||||
error |= maxim_i2c_write(priv, M98088_REG_DAC_BIAS2, 0x0F);
|
||||
error |= maxim_i2c_write(priv, M98088_REG_DAI1_IOCFG,
|
||||
M98088_S2NORMAL | M98088_SDATA);
|
||||
|
||||
/*
|
||||
* route DACL and DACR output to headphone and speakers
|
||||
* Ordering: DACL, DACR, DACL, DACR
|
||||
*/
|
||||
error |= maxim_i2c_write(priv, M98088_REG_MIX_SPK_LEFT, 1);
|
||||
error |= maxim_i2c_write(priv, M98088_REG_MIX_SPK_RIGHT, 1);
|
||||
error |= maxim_i2c_write(priv, M98088_REG_MIX_HP_LEFT, 1);
|
||||
error |= maxim_i2c_write(priv, M98088_REG_MIX_HP_RIGHT, 1);
|
||||
|
||||
/* set volume: -12db */
|
||||
error |= maxim_i2c_write(priv, M98088_REG_LVL_SPK_L, 0x0f);
|
||||
error |= maxim_i2c_write(priv, M98088_REG_LVL_SPK_R, 0x0f);
|
||||
|
||||
/* set volume: -22db */
|
||||
error |= maxim_i2c_write(priv, M98088_REG_LVL_HP_L, 0x0d);
|
||||
error |= maxim_i2c_write(priv, M98088_REG_LVL_HP_R, 0x0d);
|
||||
|
||||
/* power enable */
|
||||
error |= maxim_i2c_write(priv, M98088_REG_PWR_EN_OUT,
|
||||
M98088_HPLEN | M98088_HPREN | M98088_SPLEN |
|
||||
M98088_SPREN | M98088_DALEN | M98088_DAREN);
|
||||
if (error < 0)
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max98088_do_init(struct maxim_priv *priv, int sampling_rate,
|
||||
int mclk_freq, int bits_per_sample)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
ret = max98088_setup_interface(priv);
|
||||
if (ret < 0) {
|
||||
debug("%s: max98088 setup interface failed\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = max98088_set_sysclk(priv, mclk_freq);
|
||||
if (ret < 0) {
|
||||
debug("%s: max98088 codec set sys clock failed\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = max98088_hw_params(priv, sampling_rate, bits_per_sample);
|
||||
|
||||
if (ret == 0) {
|
||||
ret = max98088_set_fmt(priv, SND_SOC_DAIFMT_I2S |
|
||||
SND_SOC_DAIFMT_NB_NF |
|
||||
SND_SOC_DAIFMT_CBS_CFS);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int max98088_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 max98088_do_init(priv, rate, mclk_freq, bits_per_sample);
|
||||
}
|
||||
|
||||
static int max98088_probe(struct udevice *dev)
|
||||
{
|
||||
struct maxim_priv *priv = dev_get_priv(dev);
|
||||
int ret;
|
||||
|
||||
priv->dev = dev;
|
||||
ret = max98088_device_init(priv);
|
||||
if (ret < 0) {
|
||||
debug("%s: max98088 codec chip init failed\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct audio_codec_ops max98088_ops = {
|
||||
.set_params = max98088_set_params,
|
||||
};
|
||||
|
||||
static const struct udevice_id max98088_ids[] = {
|
||||
{ .compatible = "maxim,max98088" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(max98088) = {
|
||||
.name = "max98088",
|
||||
.id = UCLASS_AUDIO_CODEC,
|
||||
.of_match = max98088_ids,
|
||||
.probe = max98088_probe,
|
||||
.ops = &max98088_ops,
|
||||
.priv_auto_alloc_size = sizeof(struct maxim_priv),
|
||||
};
|
||||
@@ -0,0 +1,192 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* max98088.h -- MAX98088 ALSA SoC Audio driver
|
||||
*
|
||||
* Copyright 2010 Maxim Integrated Products
|
||||
*/
|
||||
|
||||
#ifndef _MAX98088_H
|
||||
#define _MAX98088_H
|
||||
|
||||
/* MAX98088 Registers Definition */
|
||||
#define M98088_REG_IRQ_STATUS 0x00
|
||||
#define M98088_REG_MIC_STATUS 0x01
|
||||
#define M98088_REG_JACK_STAUS 0x02
|
||||
#define M98088_REG_BATTERY_VOLTAGE 0x03
|
||||
#define M98088_REG_IRQ_ENABLE 0x0f
|
||||
#define M98088_REG_SYS_CLK 0X10
|
||||
#define M98088_REG_DAI1_CLKMODE 0x11
|
||||
#define M98088_REG_DAI1_CLKCFG_HI 0x12
|
||||
#define M98088_REG_DAI1_CLKCFG_LO 0x13
|
||||
#define M98088_REG_DAI1_FORMAT 0x14
|
||||
#define M98088_REG_DAI1_CLOCK 0x15
|
||||
#define M98088_REG_DAI1_IOCFG 0x16
|
||||
#define M98088_REG_DAI1_TDM 0X17
|
||||
#define M98088_REG_DAI1_FILTERS 0x18
|
||||
#define M98088_REG_DAI2_CLKMODE 0x19
|
||||
#define M98088_REG_DAI2_CLKCFG_HI 0x1a
|
||||
#define M98088_REG_DAI2_CLKCFG_LO 0x1b
|
||||
#define M98088_REG_DAI2_FORMAT 0x1c
|
||||
#define M98088_REG_DAI2_CLOCK 0x1d
|
||||
#define M98088_REG_DAI2_IOCFG 0x1e
|
||||
#define M98088_REG_DAI2_TDM 0X1f
|
||||
#define M98088_REG_DAI2_FILTERS 0x20
|
||||
#define M98088_REG_SRC 0X21
|
||||
#define M98088_REG_MIX_DAC 0X22
|
||||
#define M98088_REG_MIX_ADC_LEFT 0x23
|
||||
#define M98088_REG_MIX_ADC_RIGHT 0x24
|
||||
#define M98088_REG_MIX_HP_LEFT 0x25
|
||||
#define M98088_REG_MIX_HP_RIGHT 0x26
|
||||
#define M98088_REG_MIX_HP_CNTL 0x27
|
||||
#define M98088_REG_MIX_REC_LEFT 0x28
|
||||
#define M98088_REG_MIX_REC_RIGHT 0x29
|
||||
#define M98088_REG_MIC_REC_CNTL 0x2a
|
||||
#define M98088_REG_MIX_SPK_LEFT 0x2b
|
||||
#define M98088_REG_MIX_SPK_RIGHT 0x2c
|
||||
#define M98088_REG_MIX_SPK_CNTL 0x2d
|
||||
#define M98088_REG_LVL_SIDETONE 0x2e
|
||||
#define M98088_REG_LVL_DAI1_PLAY 0x2f
|
||||
#define M98088_REG_LVL_DAI1_PLAY_EQ 0x30
|
||||
#define M98088_REG_LVL_DAI2_PLAY 0x31
|
||||
#define M98088_REG_LVL_DAI2_PLAY_EQ 0x32
|
||||
#define M98088_REG_LVL_ADC_L 0X33
|
||||
#define M98088_REG_LVL_ADC_R 0X34
|
||||
#define M98088_REG_LVL_MIC1 0X35
|
||||
#define M98088_REG_LVL_MIC2 0X36
|
||||
#define M98088_REG_LVL_INA 0X37
|
||||
#define M98088_REG_LVL_INB 0X38
|
||||
#define M98088_REG_LVL_HP_L 0X39
|
||||
#define M98088_REG_LVL_HP_R 0X3a
|
||||
#define M98088_REG_LVL_REC_L 0X3b
|
||||
#define M98088_REG_LVL_REC_R 0X3c
|
||||
#define M98088_REG_LVL_SPK_L 0X3d
|
||||
#define M98088_REG_LVL_SPK_R 0X3e
|
||||
#define M98088_REG_MICAGC_CFG 0x3f
|
||||
#define M98088_REG_MICAGC_THRESH 0x40
|
||||
#define M98088_REG_SPKDHP 0X41
|
||||
#define M98088_REG_SPKDHP_THRESH 0x42
|
||||
#define M98088_REG_SPKALC_COMP 0x43
|
||||
#define M98088_REG_PWRLMT_CFG 0x44
|
||||
#define M98088_REG_PWRLMT_TIME 0x45
|
||||
#define M98088_REG_THDLMT_CFG 0x46
|
||||
#define M98088_REG_CFG_AUDIO_IN 0x47
|
||||
#define M98088_REG_CFG_MIC 0X48
|
||||
#define M98088_REG_CFG_LEVEL 0X49
|
||||
#define M98088_REG_CFG_BYPASS 0x4a
|
||||
#define M98088_REG_CFG_JACKDET 0x4b
|
||||
#define M98088_REG_PWR_EN_IN 0X4c
|
||||
#define M98088_REG_PWR_EN_OUT 0x4d
|
||||
#define M98088_REG_BIAS_CNTL 0X4e
|
||||
#define M98088_REG_DAC_BIAS1 0X4f
|
||||
#define M98088_REG_DAC_BIAS2 0X50
|
||||
#define M98088_REG_PWR_SYS 0X51
|
||||
#define M98088_REG_DAI1_EQ_BASE 0x52
|
||||
#define M98088_REG_DAI2_EQ_BASE 0x84
|
||||
#define M98088_REG_DAI1_BIQUAD_BASE 0xb6
|
||||
#define M98088_REG_DAI2_BIQUAD_BASE 0xc0
|
||||
#define M98088_REG_REV_ID 0xff
|
||||
|
||||
#define M98088_REG_CNT (0xff + 1)
|
||||
|
||||
/* MAX98088 Registers Bit Fields */
|
||||
|
||||
/* M98088_REG_11_DAI1_CLKMODE, M98088_REG_19_DAI2_CLKMODE */
|
||||
#define M98088_CLKMODE_MASK 0xFF
|
||||
|
||||
/* M98088_REG_14_DAI1_FORMAT, M98088_REG_1C_DAI2_FORMAT */
|
||||
#define M98088_DAI_MAS BIT(7)
|
||||
#define M98088_DAI_WCI BIT(6)
|
||||
#define M98088_DAI_BCI BIT(5)
|
||||
#define M98088_DAI_DLY BIT(4)
|
||||
#define M98088_DAI_TDM BIT(2)
|
||||
#define M98088_DAI_FSW BIT(1)
|
||||
#define M98088_DAI_WS BIT(0)
|
||||
|
||||
/* M98088_REG_15_DAI1_CLOCK, M98088_REG_1D_DAI2_CLOCK */
|
||||
#define M98088_DAI_BSEL64 BIT(0)
|
||||
#define M98088_DAI_OSR64 BIT(6)
|
||||
|
||||
/* M98088_REG_16_DAI1_IOCFG, M98088_REG_1E_DAI2_IOCFG */
|
||||
#define M98088_S1NORMAL BIT(6)
|
||||
#define M98088_S2NORMAL (2 << 6)
|
||||
#define M98088_SDATA (3 << 0)
|
||||
|
||||
/* M98088_REG_18_DAI1_FILTERS, M98088_REG_20_DAI2_FILTERS */
|
||||
#define M98088_DAI_DHF BIT(3)
|
||||
|
||||
/* M98088_REG_22_MIX_DAC */
|
||||
#define M98088_DAI1L_TO_DACL BIT(7)
|
||||
#define M98088_DAI1R_TO_DACL BIT(6)
|
||||
#define M98088_DAI2L_TO_DACL BIT(5)
|
||||
#define M98088_DAI2R_TO_DACL BIT(4)
|
||||
#define M98088_DAI1L_TO_DACR BIT(3)
|
||||
#define M98088_DAI1R_TO_DACR BIT(2)
|
||||
#define M98088_DAI2L_TO_DACR BIT(1)
|
||||
#define M98088_DAI2R_TO_DACR BIT(0)
|
||||
|
||||
/* M98088_REG_2A_MIC_REC_CNTL */
|
||||
#define M98088_REC_LINEMODE BIT(7)
|
||||
#define M98088_REC_LINEMODE_MASK BIT(7)
|
||||
|
||||
/* M98088_REG_2D_MIX_SPK_CNTL */
|
||||
#define M98088_MIX_SPKR_GAIN_MASK (3 << 2)
|
||||
#define M98088_MIX_SPKR_GAIN_SHIFT 2
|
||||
#define M98088_MIX_SPKL_GAIN_MASK (3 << 0)
|
||||
#define M98088_MIX_SPKL_GAIN_SHIFT 0
|
||||
|
||||
/* M98088_REG_2F_LVL_DAI1_PLAY, M98088_REG_31_LVL_DAI2_PLAY */
|
||||
#define M98088_DAI_MUTE BIT(7)
|
||||
#define M98088_DAI_MUTE_MASK BIT(7)
|
||||
#define M98088_DAI_VOICE_GAIN_MASK (3 << 4)
|
||||
#define M98088_DAI_ATTENUATION_MASK (0xf << 0)
|
||||
#define M98088_DAI_ATTENUATION_SHIFT 0
|
||||
|
||||
/* M98088_REG_35_LVL_MIC1, M98088_REG_36_LVL_MIC2 */
|
||||
#define M98088_MICPRE_MASK (3 << 5)
|
||||
#define M98088_MICPRE_SHIFT 5
|
||||
|
||||
/* M98088_REG_3A_LVL_HP_R */
|
||||
#define M98088_HP_MUTE BIT(7)
|
||||
|
||||
/* M98088_REG_3C_LVL_REC_R */
|
||||
#define M98088_REC_MUTE BIT(7)
|
||||
|
||||
/* M98088_REG_3E_LVL_SPK_R */
|
||||
#define M98088_SP_MUTE BIT(7)
|
||||
|
||||
/* M98088_REG_48_CFG_MIC */
|
||||
#define M98088_EXTMIC_MASK (3 << 0)
|
||||
#define M98088_DIGMIC_L BIT(5)
|
||||
#define M98088_DIGMIC_R BIT(4)
|
||||
|
||||
/* M98088_REG_49_CFG_LEVEL */
|
||||
#define M98088_VSEN BIT(6)
|
||||
#define M98088_ZDEN BIT(5)
|
||||
#define M98088_EQ2EN BIT(1)
|
||||
#define M98088_EQ1EN BIT(0)
|
||||
|
||||
/* M98088_REG_4C_PWR_EN_IN */
|
||||
#define M98088_INAEN BIT(7)
|
||||
#define M98088_INBEN BIT(6)
|
||||
#define M98088_MBEN BIT(3)
|
||||
#define M98088_ADLEN BIT(1)
|
||||
#define M98088_ADREN BIT(0)
|
||||
|
||||
/* M98088_REG_4D_PWR_EN_OUT */
|
||||
#define M98088_HPLEN BIT(7)
|
||||
#define M98088_HPREN BIT(6)
|
||||
#define M98088_HPEN (BIT(7) | BIT(6))
|
||||
#define M98088_SPLEN BIT(5)
|
||||
#define M98088_SPREN BIT(4)
|
||||
#define M98088_RECEN BIT(3)
|
||||
#define M98088_DALEN BIT(1)
|
||||
#define M98088_DAREN BIT(0)
|
||||
|
||||
/* M98088_REG_51_PWR_SYS */
|
||||
#define M98088_SHDNRUN BIT(7)
|
||||
#define M98088_PERFMODE BIT(3)
|
||||
#define M98088_HPPLYBACK BIT(2)
|
||||
#define M98088_PWRSV8K BIT(1)
|
||||
#define M98088_PWRSV BIT(0)
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,370 @@
|
||||
// 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 "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;
|
||||
|
||||
/* 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),
|
||||
};
|
||||
@@ -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
|
||||
@@ -0,0 +1,465 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* max98095.c -- MAX98095 ALSA SoC Audio driver
|
||||
*
|
||||
* Copyright 2011 Maxim Integrated Products
|
||||
*
|
||||
* Modified for U-Boot by R. Chandrasekar (rcsekar@samsung.com)
|
||||
*/
|
||||
|
||||
#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 "i2s.h"
|
||||
#include "max98095.h"
|
||||
|
||||
/* Index 0 is reserved. */
|
||||
int rate_table[] = {0, 8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000,
|
||||
88200, 96000};
|
||||
|
||||
/*
|
||||
* codec mclk clock divider coefficients based on sampling rate
|
||||
*
|
||||
* @param rate sampling rate
|
||||
* @param value address of indexvalue to be stored
|
||||
*
|
||||
* @return 0 for success or negative error code.
|
||||
*/
|
||||
static int rate_value(int rate, u8 *value)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 1; i < ARRAY_SIZE(rate_table); i++) {
|
||||
if (rate_table[i] >= rate) {
|
||||
*value = i;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
*value = 1;
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets hw params for max98095
|
||||
*
|
||||
* @param priv max98095 information pointer
|
||||
* @param rate Sampling rate
|
||||
* @param bits_per_sample Bits per sample
|
||||
*
|
||||
* @return 0 for success or negative error code.
|
||||
*/
|
||||
static int max98095_hw_params(struct maxim_priv *priv,
|
||||
enum en_max_audio_interface aif_id,
|
||||
unsigned int rate, unsigned int bits_per_sample)
|
||||
{
|
||||
u8 regval;
|
||||
int error;
|
||||
unsigned short M98095_DAI_CLKMODE;
|
||||
unsigned short M98095_DAI_FORMAT;
|
||||
unsigned short M98095_DAI_FILTERS;
|
||||
|
||||
if (aif_id == AIF1) {
|
||||
M98095_DAI_CLKMODE = M98095_027_DAI1_CLKMODE;
|
||||
M98095_DAI_FORMAT = M98095_02A_DAI1_FORMAT;
|
||||
M98095_DAI_FILTERS = M98095_02E_DAI1_FILTERS;
|
||||
} else {
|
||||
M98095_DAI_CLKMODE = M98095_031_DAI2_CLKMODE;
|
||||
M98095_DAI_FORMAT = M98095_034_DAI2_FORMAT;
|
||||
M98095_DAI_FILTERS = M98095_038_DAI2_FILTERS;
|
||||
}
|
||||
|
||||
switch (bits_per_sample) {
|
||||
case 16:
|
||||
error = maxim_bic_or(priv, M98095_DAI_FORMAT, M98095_DAI_WS, 0);
|
||||
break;
|
||||
case 24:
|
||||
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 -EINVAL;
|
||||
}
|
||||
|
||||
if (rate_value(rate, ®val)) {
|
||||
debug("%s: Failed to set sample rate to %d.\n",
|
||||
__func__, rate);
|
||||
return -EINVAL;
|
||||
}
|
||||
priv->rate = rate;
|
||||
|
||||
error |= maxim_bic_or(priv, M98095_DAI_CLKMODE, M98095_CLKMODE_MASK,
|
||||
regval);
|
||||
|
||||
/* Update sample rate mode */
|
||||
if (rate < 50000)
|
||||
error |= maxim_bic_or(priv, M98095_DAI_FILTERS,
|
||||
M98095_DAI_DHF, 0);
|
||||
else
|
||||
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 -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Configures Audio interface system clock for the given frequency
|
||||
*
|
||||
* @param priv max98095 information
|
||||
* @param freq Sampling frequency in Hz
|
||||
*
|
||||
* @return 0 for success or negative error code.
|
||||
*/
|
||||
static int max98095_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, M98095_026_SYS_CLK, 0x10);
|
||||
} else if ((freq >= 20000000) && (freq < 40000000)) {
|
||||
error = maxim_i2c_write(priv, M98095_026_SYS_CLK, 0x20);
|
||||
} else if ((freq >= 40000000) && (freq < 60000000)) {
|
||||
error = maxim_i2c_write(priv, M98095_026_SYS_CLK, 0x30);
|
||||
} else {
|
||||
debug("%s: Invalid master clock frequency\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
debug("%s: Clock at %uHz\n", __func__, freq);
|
||||
|
||||
if (error < 0)
|
||||
return -EIO;
|
||||
|
||||
priv->sysclk = freq;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets Max98095 I2S format
|
||||
*
|
||||
* @param priv max98095 information
|
||||
* @param fmt i2S format - supports a subset of the options defined
|
||||
* in i2s.h.
|
||||
*
|
||||
* @return 0 for success or negative error code.
|
||||
*/
|
||||
static int max98095_set_fmt(struct maxim_priv *priv, int fmt,
|
||||
enum en_max_audio_interface aif_id)
|
||||
{
|
||||
u8 regval = 0;
|
||||
int error = 0;
|
||||
unsigned short M98095_DAI_CLKCFG_HI;
|
||||
unsigned short M98095_DAI_CLKCFG_LO;
|
||||
unsigned short M98095_DAI_FORMAT;
|
||||
unsigned short M98095_DAI_CLOCK;
|
||||
|
||||
if (fmt == priv->fmt)
|
||||
return 0;
|
||||
|
||||
priv->fmt = fmt;
|
||||
|
||||
if (aif_id == AIF1) {
|
||||
M98095_DAI_CLKCFG_HI = M98095_028_DAI1_CLKCFG_HI;
|
||||
M98095_DAI_CLKCFG_LO = M98095_029_DAI1_CLKCFG_LO;
|
||||
M98095_DAI_FORMAT = M98095_02A_DAI1_FORMAT;
|
||||
M98095_DAI_CLOCK = M98095_02B_DAI1_CLOCK;
|
||||
} else {
|
||||
M98095_DAI_CLKCFG_HI = M98095_032_DAI2_CLKCFG_HI;
|
||||
M98095_DAI_CLKCFG_LO = M98095_033_DAI2_CLKCFG_LO;
|
||||
M98095_DAI_FORMAT = M98095_034_DAI2_FORMAT;
|
||||
M98095_DAI_CLOCK = M98095_035_DAI2_CLOCK;
|
||||
}
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
|
||||
case SND_SOC_DAIFMT_CBS_CFS:
|
||||
/* Slave mode PLL */
|
||||
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 */
|
||||
regval |= M98095_DAI_MAS;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_CBS_CFM:
|
||||
case SND_SOC_DAIFMT_CBM_CFS:
|
||||
default:
|
||||
debug("%s: Clock mode unsupported\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
||||
case SND_SOC_DAIFMT_I2S:
|
||||
regval |= M98095_DAI_DLY;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_LEFT_J:
|
||||
break;
|
||||
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 |= M98095_DAI_WCI;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_IB_NF:
|
||||
regval |= M98095_DAI_BCI;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_IB_IF:
|
||||
regval |= M98095_DAI_BCI | M98095_DAI_WCI;
|
||||
break;
|
||||
default:
|
||||
debug("%s: Unrecognized inversion settings.\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
error |= maxim_bic_or(priv, M98095_DAI_FORMAT,
|
||||
M98095_DAI_MAS | M98095_DAI_DLY |
|
||||
M98095_DAI_BCI | M98095_DAI_WCI, regval);
|
||||
|
||||
error |= maxim_i2c_write(priv, M98095_DAI_CLOCK, M98095_DAI_BSEL64);
|
||||
|
||||
if (error < 0) {
|
||||
debug("%s: Error setting i2s format.\n", __func__);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* resets the audio codec
|
||||
*
|
||||
* @param priv Private data for driver
|
||||
* @return 0 for success or negative error code.
|
||||
*/
|
||||
static int max98095_reset(struct maxim_priv *priv)
|
||||
{
|
||||
int i, ret;
|
||||
|
||||
/*
|
||||
* Gracefully reset the DSP core and the codec hardware in a proper
|
||||
* sequence.
|
||||
*/
|
||||
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 = maxim_i2c_write(priv, M98095_097_PWR_SYS, 0);
|
||||
if (ret != 0) {
|
||||
debug("%s: Failed to reset codec: %d\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reset to hardware default for registers, as there is not a soft
|
||||
* reset hardware control register.
|
||||
*/
|
||||
for (i = M98095_010_HOST_INT_CFG; i < M98095_REG_MAX_CACHED; i++) {
|
||||
ret = maxim_i2c_write(priv, i, 0);
|
||||
if (ret < 0) {
|
||||
debug("%s: Failed to reset: %d\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Intialise max98095 codec device
|
||||
*
|
||||
* @param priv max98095 information
|
||||
* @return 0 for success or negative error code.
|
||||
*/
|
||||
static int max98095_device_init(struct maxim_priv *priv)
|
||||
{
|
||||
unsigned char id;
|
||||
int ret;
|
||||
|
||||
/* reset the codec, the DSP core, and disable all interrupts */
|
||||
ret = max98095_reset(priv);
|
||||
if (ret != 0) {
|
||||
debug("Reset\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* initialize private data */
|
||||
priv->sysclk = -1U;
|
||||
priv->rate = -1U;
|
||||
priv->fmt = -1U;
|
||||
|
||||
ret = maxim_i2c_read(priv, M98095_0FF_REV_ID, &id);
|
||||
if (ret < 0) {
|
||||
debug("%s: Failure reading hardware revision: %d\n",
|
||||
__func__, id);
|
||||
return ret;
|
||||
}
|
||||
debug("%s: Hardware revision: %c\n", __func__, (id - 0x40) + 'A');
|
||||
|
||||
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 |= maxim_i2c_write(priv, M98095_048_MIX_DAC_LR,
|
||||
M98095_DAI1L_TO_DACL |
|
||||
M98095_DAI1R_TO_DACR);
|
||||
else
|
||||
error |= maxim_i2c_write(priv, M98095_048_MIX_DAC_LR,
|
||||
M98095_DAI2M_TO_DACL |
|
||||
M98095_DAI2M_TO_DACR);
|
||||
|
||||
error |= maxim_i2c_write(priv, M98095_092_PWR_EN_OUT,
|
||||
M98095_SPK_SPREADSPECTRUM);
|
||||
error |= maxim_i2c_write(priv, M98095_04E_CFG_HP, M98095_HPNORMAL);
|
||||
if (aif_id == AIF1)
|
||||
error |= maxim_i2c_write(priv, M98095_02C_DAI1_IOCFG,
|
||||
M98095_S1NORMAL | M98095_SDATA);
|
||||
else
|
||||
error |= maxim_i2c_write(priv, M98095_036_DAI2_IOCFG,
|
||||
M98095_S2NORMAL | M98095_SDATA);
|
||||
|
||||
/* take the codec out of the shut down */
|
||||
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 |= maxim_i2c_write(priv, M98095_091_PWR_EN_OUT, 0xF3);
|
||||
|
||||
/* set Volume */
|
||||
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 |= maxim_i2c_write(priv, M98095_093_BIAS_CTRL, 0x30);
|
||||
if (aif_id == AIF1)
|
||||
error |= maxim_i2c_write(priv, M98095_096_PWR_DAC_CK, 0x01);
|
||||
else
|
||||
error |= maxim_i2c_write(priv, M98095_096_PWR_DAC_CK, 0x07);
|
||||
|
||||
if (error < 0)
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
ret = max98095_setup_interface(priv, aif_id);
|
||||
if (ret < 0) {
|
||||
debug("%s: max98095 setup interface failed\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
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(priv, aif_id, sampling_rate,
|
||||
bits_per_sample);
|
||||
|
||||
if (ret == 0) {
|
||||
ret = max98095_set_fmt(priv, SND_SOC_DAIFMT_I2S |
|
||||
SND_SOC_DAIFMT_NB_NF |
|
||||
SND_SOC_DAIFMT_CBS_CFS,
|
||||
aif_id);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int max98095_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 max98095_do_init(priv, interface, rate, mclk_freq,
|
||||
bits_per_sample);
|
||||
}
|
||||
|
||||
static int max98095_probe(struct udevice *dev)
|
||||
{
|
||||
struct maxim_priv *priv = dev_get_priv(dev);
|
||||
int ret;
|
||||
|
||||
priv->dev = dev;
|
||||
ret = max98095_device_init(priv);
|
||||
if (ret < 0) {
|
||||
debug("%s: max98095 codec chip init failed\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct audio_codec_ops max98095_ops = {
|
||||
.set_params = max98095_set_params,
|
||||
};
|
||||
|
||||
static const struct udevice_id max98095_ids[] = {
|
||||
{ .compatible = "maxim,max98095" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(max98095) = {
|
||||
.name = "max98095",
|
||||
.id = UCLASS_AUDIO_CODEC,
|
||||
.of_match = max98095_ids,
|
||||
.probe = max98095_probe,
|
||||
.ops = &max98095_ops,
|
||||
.priv_auto_alloc_size = sizeof(struct maxim_priv),
|
||||
};
|
||||
@@ -0,0 +1,316 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* max98095.h -- MAX98095 ALSA SoC Audio driver
|
||||
*
|
||||
* Copyright 2011 Maxim Integrated Products
|
||||
*/
|
||||
|
||||
#ifndef _MAX98095_H
|
||||
#define _MAX98095_H
|
||||
|
||||
#include "maxim_codec.h"
|
||||
|
||||
/* Available audio interface ports in wm8994 codec */
|
||||
enum en_max_audio_interface {
|
||||
AIF1,
|
||||
AIF2,
|
||||
};
|
||||
|
||||
/*
|
||||
* MAX98095 Registers Definition
|
||||
*/
|
||||
|
||||
#define M98095_000_HOST_DATA 0x00
|
||||
#define M98095_001_HOST_INT_STS 0x01
|
||||
#define M98095_002_HOST_RSP_STS 0x02
|
||||
#define M98095_003_HOST_CMD_STS 0x03
|
||||
#define M98095_004_CODEC_STS 0x04
|
||||
#define M98095_005_DAI1_ALC_STS 0x05
|
||||
#define M98095_006_DAI2_ALC_STS 0x06
|
||||
#define M98095_007_JACK_AUTO_STS 0x07
|
||||
#define M98095_008_JACK_MANUAL_STS 0x08
|
||||
#define M98095_009_JACK_VBAT_STS 0x09
|
||||
#define M98095_00A_ACC_ADC_STS 0x0A
|
||||
#define M98095_00B_MIC_NG_AGC_STS 0x0B
|
||||
#define M98095_00C_SPK_L_VOLT_STS 0x0C
|
||||
#define M98095_00D_SPK_R_VOLT_STS 0x0D
|
||||
#define M98095_00E_TEMP_SENSOR_STS 0x0E
|
||||
#define M98095_00F_HOST_CFG 0x0F
|
||||
#define M98095_010_HOST_INT_CFG 0x10
|
||||
#define M98095_011_HOST_INT_EN 0x11
|
||||
#define M98095_012_CODEC_INT_EN 0x12
|
||||
#define M98095_013_JACK_INT_EN 0x13
|
||||
#define M98095_014_JACK_INT_EN 0x14
|
||||
#define M98095_015_DEC 0x15
|
||||
#define M98095_016_RESERVED 0x16
|
||||
#define M98095_017_RESERVED 0x17
|
||||
#define M98095_018_KEYCODE3 0x18
|
||||
#define M98095_019_KEYCODE2 0x19
|
||||
#define M98095_01A_KEYCODE1 0x1A
|
||||
#define M98095_01B_KEYCODE0 0x1B
|
||||
#define M98095_01C_OEMCODE1 0x1C
|
||||
#define M98095_01D_OEMCODE0 0x1D
|
||||
#define M98095_01E_XCFG1 0x1E
|
||||
#define M98095_01F_XCFG2 0x1F
|
||||
#define M98095_020_XCFG3 0x20
|
||||
#define M98095_021_XCFG4 0x21
|
||||
#define M98095_022_XCFG5 0x22
|
||||
#define M98095_023_XCFG6 0x23
|
||||
#define M98095_024_XGPIO 0x24
|
||||
#define M98095_025_XCLKCFG 0x25
|
||||
#define M98095_026_SYS_CLK 0x26
|
||||
#define M98095_027_DAI1_CLKMODE 0x27
|
||||
#define M98095_028_DAI1_CLKCFG_HI 0x28
|
||||
#define M98095_029_DAI1_CLKCFG_LO 0x29
|
||||
#define M98095_02A_DAI1_FORMAT 0x2A
|
||||
#define M98095_02B_DAI1_CLOCK 0x2B
|
||||
#define M98095_02C_DAI1_IOCFG 0x2C
|
||||
#define M98095_02D_DAI1_TDM 0x2D
|
||||
#define M98095_02E_DAI1_FILTERS 0x2E
|
||||
#define M98095_02F_DAI1_LVL1 0x2F
|
||||
#define M98095_030_DAI1_LVL2 0x30
|
||||
#define M98095_031_DAI2_CLKMODE 0x31
|
||||
#define M98095_032_DAI2_CLKCFG_HI 0x32
|
||||
#define M98095_033_DAI2_CLKCFG_LO 0x33
|
||||
#define M98095_034_DAI2_FORMAT 0x34
|
||||
#define M98095_035_DAI2_CLOCK 0x35
|
||||
#define M98095_036_DAI2_IOCFG 0x36
|
||||
#define M98095_037_DAI2_TDM 0x37
|
||||
#define M98095_038_DAI2_FILTERS 0x38
|
||||
#define M98095_039_DAI2_LVL1 0x39
|
||||
#define M98095_03A_DAI2_LVL2 0x3A
|
||||
#define M98095_03B_DAI3_CLKMODE 0x3B
|
||||
#define M98095_03C_DAI3_CLKCFG_HI 0x3C
|
||||
#define M98095_03D_DAI3_CLKCFG_LO 0x3D
|
||||
#define M98095_03E_DAI3_FORMAT 0x3E
|
||||
#define M98095_03F_DAI3_CLOCK 0x3F
|
||||
#define M98095_040_DAI3_IOCFG 0x40
|
||||
#define M98095_041_DAI3_TDM 0x41
|
||||
#define M98095_042_DAI3_FILTERS 0x42
|
||||
#define M98095_043_DAI3_LVL1 0x43
|
||||
#define M98095_044_DAI3_LVL2 0x44
|
||||
#define M98095_045_CFG_DSP 0x45
|
||||
#define M98095_046_DAC_CTRL1 0x46
|
||||
#define M98095_047_DAC_CTRL2 0x47
|
||||
#define M98095_048_MIX_DAC_LR 0x48
|
||||
#define M98095_049_MIX_DAC_M 0x49
|
||||
#define M98095_04A_MIX_ADC_LEFT 0x4A
|
||||
#define M98095_04B_MIX_ADC_RIGHT 0x4B
|
||||
#define M98095_04C_MIX_HP_LEFT 0x4C
|
||||
#define M98095_04D_MIX_HP_RIGHT 0x4D
|
||||
#define M98095_04E_CFG_HP 0x4E
|
||||
#define M98095_04F_MIX_RCV 0x4F
|
||||
#define M98095_050_MIX_SPK_LEFT 0x50
|
||||
#define M98095_051_MIX_SPK_RIGHT 0x51
|
||||
#define M98095_052_MIX_SPK_CFG 0x52
|
||||
#define M98095_053_MIX_LINEOUT1 0x53
|
||||
#define M98095_054_MIX_LINEOUT2 0x54
|
||||
#define M98095_055_MIX_LINEOUT_CFG 0x55
|
||||
#define M98095_056_LVL_SIDETONE_DAI12 0x56
|
||||
#define M98095_057_LVL_SIDETONE_DAI3 0x57
|
||||
#define M98095_058_LVL_DAI1_PLAY 0x58
|
||||
#define M98095_059_LVL_DAI1_EQ 0x59
|
||||
#define M98095_05A_LVL_DAI2_PLAY 0x5A
|
||||
#define M98095_05B_LVL_DAI2_EQ 0x5B
|
||||
#define M98095_05C_LVL_DAI3_PLAY 0x5C
|
||||
#define M98095_05D_LVL_ADC_L 0x5D
|
||||
#define M98095_05E_LVL_ADC_R 0x5E
|
||||
#define M98095_05F_LVL_MIC1 0x5F
|
||||
#define M98095_060_LVL_MIC2 0x60
|
||||
#define M98095_061_LVL_LINEIN 0x61
|
||||
#define M98095_062_LVL_LINEOUT1 0x62
|
||||
#define M98095_063_LVL_LINEOUT2 0x63
|
||||
#define M98095_064_LVL_HP_L 0x64
|
||||
#define M98095_065_LVL_HP_R 0x65
|
||||
#define M98095_066_LVL_RCV 0x66
|
||||
#define M98095_067_LVL_SPK_L 0x67
|
||||
#define M98095_068_LVL_SPK_R 0x68
|
||||
#define M98095_069_MICAGC_CFG 0x69
|
||||
#define M98095_06A_MICAGC_THRESH 0x6A
|
||||
#define M98095_06B_SPK_NOISEGATE 0x6B
|
||||
#define M98095_06C_DAI1_ALC1_TIME 0x6C
|
||||
#define M98095_06D_DAI1_ALC1_COMP 0x6D
|
||||
#define M98095_06E_DAI1_ALC1_EXPN 0x6E
|
||||
#define M98095_06F_DAI1_ALC1_GAIN 0x6F
|
||||
#define M98095_070_DAI1_ALC2_TIME 0x70
|
||||
#define M98095_071_DAI1_ALC2_COMP 0x71
|
||||
#define M98095_072_DAI1_ALC2_EXPN 0x72
|
||||
#define M98095_073_DAI1_ALC2_GAIN 0x73
|
||||
#define M98095_074_DAI1_ALC3_TIME 0x74
|
||||
#define M98095_075_DAI1_ALC3_COMP 0x75
|
||||
#define M98095_076_DAI1_ALC3_EXPN 0x76
|
||||
#define M98095_077_DAI1_ALC3_GAIN 0x77
|
||||
#define M98095_078_DAI2_ALC1_TIME 0x78
|
||||
#define M98095_079_DAI2_ALC1_COMP 0x79
|
||||
#define M98095_07A_DAI2_ALC1_EXPN 0x7A
|
||||
#define M98095_07B_DAI2_ALC1_GAIN 0x7B
|
||||
#define M98095_07C_DAI2_ALC2_TIME 0x7C
|
||||
#define M98095_07D_DAI2_ALC2_COMP 0x7D
|
||||
#define M98095_07E_DAI2_ALC2_EXPN 0x7E
|
||||
#define M98095_07F_DAI2_ALC2_GAIN 0x7F
|
||||
#define M98095_080_DAI2_ALC3_TIME 0x80
|
||||
#define M98095_081_DAI2_ALC3_COMP 0x81
|
||||
#define M98095_082_DAI2_ALC3_EXPN 0x82
|
||||
#define M98095_083_DAI2_ALC3_GAIN 0x83
|
||||
#define M98095_084_HP_NOISE_GATE 0x84
|
||||
#define M98095_085_AUX_ADC 0x85
|
||||
#define M98095_086_CFG_LINE 0x86
|
||||
#define M98095_087_CFG_MIC 0x87
|
||||
#define M98095_088_CFG_LEVEL 0x88
|
||||
#define M98095_089_JACK_DET_AUTO 0x89
|
||||
#define M98095_08A_JACK_DET_MANUAL 0x8A
|
||||
#define M98095_08B_JACK_KEYSCAN_DBC 0x8B
|
||||
#define M98095_08C_JACK_KEYSCAN_DLY 0x8C
|
||||
#define M98095_08D_JACK_KEY_THRESH 0x8D
|
||||
#define M98095_08E_JACK_DC_SLEW 0x8E
|
||||
#define M98095_08F_JACK_TEST_CFG 0x8F
|
||||
#define M98095_090_PWR_EN_IN 0x90
|
||||
#define M98095_091_PWR_EN_OUT 0x91
|
||||
#define M98095_092_PWR_EN_OUT 0x92
|
||||
#define M98095_093_BIAS_CTRL 0x93
|
||||
#define M98095_094_PWR_DAC_21 0x94
|
||||
#define M98095_095_PWR_DAC_03 0x95
|
||||
#define M98095_096_PWR_DAC_CK 0x96
|
||||
#define M98095_097_PWR_SYS 0x97
|
||||
|
||||
#define M98095_0FF_REV_ID 0xFF
|
||||
|
||||
#define M98095_REG_CNT (0xFF+1)
|
||||
#define M98095_REG_MAX_CACHED 0X97
|
||||
|
||||
/* MAX98095 Registers Bit Fields */
|
||||
|
||||
/* M98095_00F_HOST_CFG */
|
||||
#define M98095_SEG (1<<0)
|
||||
#define M98095_XTEN (1<<1)
|
||||
#define M98095_MDLLEN (1<<2)
|
||||
|
||||
/* M98095_027_DAI1_CLKMODE, M98095_031_DAI2_CLKMODE, M98095_03B_DAI3_CLKMODE */
|
||||
#define M98095_CLKMODE_MASK 0xFF
|
||||
|
||||
/* M98095_02A_DAI1_FORMAT, M98095_034_DAI2_FORMAT, M98095_03E_DAI3_FORMAT */
|
||||
#define M98095_DAI_MAS (1<<7)
|
||||
#define M98095_DAI_WCI (1<<6)
|
||||
#define M98095_DAI_BCI (1<<5)
|
||||
#define M98095_DAI_DLY (1<<4)
|
||||
#define M98095_DAI_TDM (1<<2)
|
||||
#define M98095_DAI_FSW (1<<1)
|
||||
#define M98095_DAI_WS (1<<0)
|
||||
|
||||
/* M98095_02B_DAI1_CLOCK, M98095_035_DAI2_CLOCK, M98095_03F_DAI3_CLOCK */
|
||||
#define M98095_DAI_BSEL64 (1<<0)
|
||||
#define M98095_DAI_DOSR_DIV2 (0<<5)
|
||||
#define M98095_DAI_DOSR_DIV4 (1<<5)
|
||||
|
||||
/* M98095_02C_DAI1_IOCFG, M98095_036_DAI2_IOCFG, M98095_040_DAI3_IOCFG */
|
||||
#define M98095_S1NORMAL (1<<6)
|
||||
#define M98095_S2NORMAL (2<<6)
|
||||
#define M98095_S3NORMAL (3<<6)
|
||||
#define M98095_SDATA (3<<0)
|
||||
|
||||
/* M98095_02E_DAI1_FILTERS, M98095_038_DAI2_FILTERS, M98095_042_DAI3_FILTERS */
|
||||
#define M98095_DAI_DHF (1<<3)
|
||||
|
||||
/* M98095_045_DSP_CFG */
|
||||
#define M98095_DSPNORMAL (5<<4)
|
||||
|
||||
/* M98095_048_MIX_DAC_LR */
|
||||
#define M98095_DAI1L_TO_DACR (1<<7)
|
||||
#define M98095_DAI1R_TO_DACR (1<<6)
|
||||
#define M98095_DAI2M_TO_DACR (1<<5)
|
||||
#define M98095_DAI1L_TO_DACL (1<<3)
|
||||
#define M98095_DAI1R_TO_DACL (1<<2)
|
||||
#define M98095_DAI2M_TO_DACL (1<<1)
|
||||
#define M98095_DAI3M_TO_DACL (1<<0)
|
||||
|
||||
/* M98095_049_MIX_DAC_M */
|
||||
#define M98095_DAI1L_TO_DACM (1<<3)
|
||||
#define M98095_DAI1R_TO_DACM (1<<2)
|
||||
#define M98095_DAI2M_TO_DACM (1<<1)
|
||||
#define M98095_DAI3M_TO_DACM (1<<0)
|
||||
|
||||
/* M98095_04E_MIX_HP_CFG */
|
||||
#define M98095_HPNORMAL (3<<4)
|
||||
|
||||
/* M98095_05F_LVL_MIC1, M98095_060_LVL_MIC2 */
|
||||
#define M98095_MICPRE_MASK (3<<5)
|
||||
#define M98095_MICPRE_SHIFT 5
|
||||
|
||||
/* M98095_064_LVL_HP_L, M98095_065_LVL_HP_R */
|
||||
#define M98095_HP_MUTE (1<<7)
|
||||
|
||||
/* M98095_066_LVL_RCV */
|
||||
#define M98095_REC_MUTE (1<<7)
|
||||
|
||||
/* M98095_067_LVL_SPK_L, M98095_068_LVL_SPK_R */
|
||||
#define M98095_SP_MUTE (1<<7)
|
||||
|
||||
/* M98095_087_CFG_MIC */
|
||||
#define M98095_MICSEL_MASK (3<<0)
|
||||
#define M98095_DIGMIC_L (1<<2)
|
||||
#define M98095_DIGMIC_R (1<<3)
|
||||
#define M98095_DIGMIC2L (1<<4)
|
||||
#define M98095_DIGMIC2R (1<<5)
|
||||
|
||||
/* M98095_088_CFG_LEVEL */
|
||||
#define M98095_VSEN (1<<6)
|
||||
#define M98095_ZDEN (1<<5)
|
||||
#define M98095_BQ2EN (1<<3)
|
||||
#define M98095_BQ1EN (1<<2)
|
||||
#define M98095_EQ2EN (1<<1)
|
||||
#define M98095_EQ1EN (1<<0)
|
||||
|
||||
/* M98095_090_PWR_EN_IN */
|
||||
#define M98095_INEN (1<<7)
|
||||
#define M98095_MB2EN (1<<3)
|
||||
#define M98095_MB1EN (1<<2)
|
||||
#define M98095_MBEN (3<<2)
|
||||
#define M98095_ADREN (1<<1)
|
||||
#define M98095_ADLEN (1<<0)
|
||||
|
||||
/* M98095_091_PWR_EN_OUT */
|
||||
#define M98095_HPLEN (1<<7)
|
||||
#define M98095_HPREN (1<<6)
|
||||
#define M98095_SPLEN (1<<5)
|
||||
#define M98095_SPREN (1<<4)
|
||||
#define M98095_RECEN (1<<3)
|
||||
#define M98095_DALEN (1<<1)
|
||||
#define M98095_DAREN (1<<0)
|
||||
|
||||
/* M98095_092_PWR_EN_OUT */
|
||||
#define M98095_SPK_FIXEDSPECTRUM (0<<4)
|
||||
#define M98095_SPK_SPREADSPECTRUM (1<<4)
|
||||
|
||||
/* M98095_097_PWR_SYS */
|
||||
#define M98095_SHDNRUN (1<<7)
|
||||
#define M98095_PERFMODE (1<<3)
|
||||
#define M98095_HPPLYBACK (1<<2)
|
||||
#define M98095_PWRSV8K (1<<1)
|
||||
#define M98095_PWRSV (1<<0)
|
||||
|
||||
#define M98095_COEFS_PER_BAND 5
|
||||
|
||||
/* Equalizer filter coefficients */
|
||||
#define M98095_110_DAI1_EQ_BASE 0x10
|
||||
#define M98095_142_DAI2_EQ_BASE 0x42
|
||||
|
||||
/* Biquad filter coefficients */
|
||||
#define M98095_174_DAI1_BQ_BASE 0x74
|
||||
#define M98095_17E_DAI2_BQ_BASE 0x7E
|
||||
|
||||
/* function prototype */
|
||||
|
||||
/*
|
||||
* intialise max98095 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 max98095_init(const void *blob, enum en_max_audio_interface aif_id,
|
||||
int sampling_rate, int mclk_freq, int bits_per_sample);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,84 @@
|
||||
// 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 "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;
|
||||
}
|
||||
@@ -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__ */
|
||||
@@ -0,0 +1,149 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright 2018 Google LLC
|
||||
* Copyright 2014 Rockchip Electronics Co., Ltd.
|
||||
* Taken from dc i2s/rockchip.c
|
||||
*/
|
||||
|
||||
#define LOG_CATEGORY UCLASS_I2S
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <i2s.h>
|
||||
#include <sound.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
struct rk_i2s_regs {
|
||||
u32 txcr; /* I2S_TXCR, 0x00 */
|
||||
u32 rxcr; /* I2S_RXCR, 0x04 */
|
||||
u32 ckr; /* I2S_CKR, 0x08 */
|
||||
u32 fifolr; /* I2S_FIFOLR, 0x0C */
|
||||
u32 dmacr; /* I2S_DMACR, 0x10 */
|
||||
u32 intcr; /* I2S_INTCR, 0x14 */
|
||||
u32 intsr; /* I2S_INTSR, 0x18 */
|
||||
u32 xfer; /* I2S_XFER, 0x1C */
|
||||
u32 clr; /* I2S_CLR, 0x20 */
|
||||
u32 txdr; /* I2S_TXDR, 0x24 */
|
||||
u32 rxdr; /* I2S_RXDR, 0x28 */
|
||||
};
|
||||
|
||||
enum {
|
||||
/* I2S_XFER */
|
||||
I2S_RX_TRAN_BIT = BIT(1),
|
||||
I2S_TX_TRAN_BIT = BIT(0),
|
||||
I2S_TRAN_MASK = 3 << 0,
|
||||
|
||||
/* I2S_TXCKR */
|
||||
I2S_MCLK_DIV_SHIFT = 16,
|
||||
I2S_MCLK_DIV_MASK = (0xff << I2S_MCLK_DIV_SHIFT),
|
||||
|
||||
I2S_RX_SCLK_DIV_SHIFT = 8,
|
||||
I2S_RX_SCLK_DIV_MASK = 0xff << I2S_RX_SCLK_DIV_SHIFT,
|
||||
I2S_TX_SCLK_DIV_SHIFT = 0,
|
||||
I2S_TX_SCLK_DIV_MASK = 0xff << I2S_TX_SCLK_DIV_SHIFT,
|
||||
|
||||
I2S_DATA_WIDTH_SHIFT = 0,
|
||||
I2S_DATA_WIDTH_MASK = 0x1f << I2S_DATA_WIDTH_SHIFT,
|
||||
};
|
||||
|
||||
static int rockchip_i2s_init(struct i2s_uc_priv *priv)
|
||||
{
|
||||
struct rk_i2s_regs *regs = (struct rk_i2s_regs *)priv->base_address;
|
||||
u32 bps = priv->bitspersample;
|
||||
u32 lrf = priv->rfs;
|
||||
u32 chn = priv->channels;
|
||||
u32 mode = 0;
|
||||
|
||||
clrbits_le32(®s->xfer, I2S_TX_TRAN_BIT);
|
||||
mode = readl(®s->txcr) & ~0x1f;
|
||||
switch (priv->bitspersample) {
|
||||
case 16:
|
||||
case 24:
|
||||
mode |= (priv->bitspersample - 1) << I2S_DATA_WIDTH_SHIFT;
|
||||
break;
|
||||
default:
|
||||
log_err("Invalid sample size input %d\n", priv->bitspersample);
|
||||
return -EINVAL;
|
||||
}
|
||||
writel(mode, ®s->txcr);
|
||||
|
||||
mode = readl(®s->ckr) & ~I2S_MCLK_DIV_MASK;
|
||||
mode |= (lrf / (bps * chn) - 1) << I2S_MCLK_DIV_SHIFT;
|
||||
|
||||
mode &= ~I2S_TX_SCLK_DIV_MASK;
|
||||
mode |= (priv->bitspersample * priv->channels - 1) <<
|
||||
I2S_TX_SCLK_DIV_SHIFT;
|
||||
writel(mode, ®s->ckr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int i2s_send_data(struct rk_i2s_regs *regs, u32 *data, uint length)
|
||||
{
|
||||
for (int i = 0; i < min(32u, length); i++)
|
||||
writel(*data++, ®s->txdr);
|
||||
|
||||
length -= min(32u, length);
|
||||
|
||||
/* enable both tx and rx */
|
||||
setbits_le32(®s->xfer, I2S_TRAN_MASK);
|
||||
while (length) {
|
||||
if ((readl(®s->fifolr) & 0x3f) < 0x20) {
|
||||
writel(*data++, ®s->txdr);
|
||||
length--;
|
||||
}
|
||||
}
|
||||
while (readl(®s->fifolr) & 0x3f)
|
||||
/* wait until FIFO empty */;
|
||||
clrbits_le32(®s->xfer, I2S_TRAN_MASK);
|
||||
writel(0, ®s->clr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rockchip_i2s_tx_data(struct udevice *dev, void *data, uint data_size)
|
||||
{
|
||||
struct i2s_uc_priv *priv = dev_get_uclass_priv(dev);
|
||||
struct rk_i2s_regs *regs = (struct rk_i2s_regs *)priv->base_address;
|
||||
|
||||
return i2s_send_data(regs, data, data_size / sizeof(u32));
|
||||
}
|
||||
|
||||
static int rockchip_i2s_probe(struct udevice *dev)
|
||||
{
|
||||
struct i2s_uc_priv *priv = dev_get_uclass_priv(dev);
|
||||
ulong base;
|
||||
|
||||
base = dev_read_addr(dev);
|
||||
if (base == FDT_ADDR_T_NONE) {
|
||||
log_debug("Missing i2s base\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
priv->base_address = base;
|
||||
priv->id = 1;
|
||||
priv->audio_pll_clk = 4800000;
|
||||
priv->samplingrate = 48000;
|
||||
priv->bitspersample = 16;
|
||||
priv->channels = 2;
|
||||
priv->rfs = 256;
|
||||
priv->bfs = 32;
|
||||
|
||||
return rockchip_i2s_init(priv);
|
||||
}
|
||||
|
||||
static const struct i2s_ops rockchip_i2s_ops = {
|
||||
.tx_data = rockchip_i2s_tx_data,
|
||||
};
|
||||
|
||||
static const struct udevice_id rockchip_i2s_ids[] = {
|
||||
{ .compatible = "rockchip,rk3288-i2s" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(rockchip_i2s) = {
|
||||
.name = "rockchip_i2s",
|
||||
.id = UCLASS_I2S,
|
||||
.of_match = rockchip_i2s_ids,
|
||||
.probe = rockchip_i2s_probe,
|
||||
.ops = &rockchip_i2s_ops,
|
||||
};
|
||||
@@ -0,0 +1,132 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright 2018 Google, LLC
|
||||
* Written by Simon Glass <sjg@chromium.org>
|
||||
*/
|
||||
|
||||
#define LOG_CATEGORY UCLASS_SOUND
|
||||
|
||||
#include <common.h>
|
||||
#include <audio_codec.h>
|
||||
#include <clk.h>
|
||||
#include <dm.h>
|
||||
#include <i2s.h>
|
||||
#include <misc.h>
|
||||
#include <sound.h>
|
||||
#include <asm/arch-rockchip/periph.h>
|
||||
#include <dm/pinctrl.h>
|
||||
|
||||
static int rockchip_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 rockchip_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 rockchip_sound_probe(struct udevice *dev)
|
||||
{
|
||||
struct sound_uc_priv *uc_priv = dev_get_uclass_priv(dev);
|
||||
struct ofnode_phandle_args args;
|
||||
struct udevice *pinctrl;
|
||||
struct clk clk;
|
||||
ofnode node;
|
||||
int ret;
|
||||
|
||||
node = ofnode_find_subnode(dev_ofnode(dev), "cpu");
|
||||
if (!ofnode_valid(node)) {
|
||||
log_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) {
|
||||
log_debug("Cannot find i2s phandle: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
ret = uclass_get_device_by_ofnode(UCLASS_I2S, args.node, &uc_priv->i2s);
|
||||
if (ret) {
|
||||
log_debug("Cannot find i2s: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
node = ofnode_find_subnode(dev_ofnode(dev), "codec");
|
||||
if (!ofnode_valid(node)) {
|
||||
log_debug("Failed to find /codec subnode\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
ret = ofnode_parse_phandle_with_args(node, "sound-dai",
|
||||
"#sound-dai-cells", 0, 0, &args);
|
||||
if (ret) {
|
||||
log_debug("Cannot find codec phandle: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
ret = uclass_get_device_by_ofnode(UCLASS_AUDIO_CODEC, args.node,
|
||||
&uc_priv->codec);
|
||||
if (ret) {
|
||||
log_debug("Cannot find audio codec: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
ret = clk_get_by_index(uc_priv->i2s, 1, &clk);
|
||||
if (ret) {
|
||||
log_debug("Cannot find clock: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
ret = clk_set_rate(&clk, 12288000);
|
||||
if (ret < 0) {
|
||||
log_debug("Cannot find clock: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
ret = uclass_get_device(UCLASS_PINCTRL, 0, &pinctrl);
|
||||
if (ret) {
|
||||
debug("%s: Cannot find pinctrl device\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
ret = pinctrl_request(pinctrl, PERIPH_ID_I2S, 0);
|
||||
if (ret) {
|
||||
debug("%s: Cannot select I2C pinctrl\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
log_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 rockchip_sound_ops = {
|
||||
.setup = rockchip_sound_setup,
|
||||
.play = rockchip_sound_play,
|
||||
};
|
||||
|
||||
static const struct udevice_id rockchip_sound_ids[] = {
|
||||
{ .compatible = "rockchip,audio-max98090-jerry" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(rockchip_sound) = {
|
||||
.name = "rockchip_sound",
|
||||
.id = UCLASS_SOUND,
|
||||
.of_match = rockchip_sound_ids,
|
||||
.probe = rockchip_sound_probe,
|
||||
.ops = &rockchip_sound_ops,
|
||||
};
|
||||
@@ -0,0 +1,334 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright 2019 Google LLC
|
||||
*/
|
||||
|
||||
#define LOG_CATEGORY UCLASS_SOUND
|
||||
|
||||
#include <common.h>
|
||||
#include <audio_codec.h>
|
||||
#include <dm.h>
|
||||
#include <i2c.h>
|
||||
#include "rt5677.h"
|
||||
|
||||
struct rt5677_priv {
|
||||
struct udevice *dev;
|
||||
};
|
||||
|
||||
/* RT5677 has 256 8-bit register addresses, and 16-bit register data */
|
||||
struct rt5677_init_reg {
|
||||
u8 reg;
|
||||
u16 val;
|
||||
};
|
||||
|
||||
static struct rt5677_init_reg init_list[] = {
|
||||
{RT5677_LOUT1, 0x0800},
|
||||
{RT5677_SIDETONE_CTRL, 0x0000},
|
||||
{RT5677_STO1_ADC_DIG_VOL, 0x3F3F},
|
||||
{RT5677_DAC1_DIG_VOL, 0x9090},
|
||||
{RT5677_STO2_ADC_MIXER, 0xA441},
|
||||
{RT5677_STO1_ADC_MIXER, 0x5480},
|
||||
{RT5677_STO1_DAC_MIXER, 0x8A8A},
|
||||
{RT5677_PWR_DIG1, 0x9800}, /* Power up I2S1 */
|
||||
{RT5677_PWR_ANLG1, 0xE9D5},
|
||||
{RT5677_PWR_ANLG2, 0x2CC0},
|
||||
{RT5677_PWR_DSP2, 0x0C00},
|
||||
{RT5677_I2S2_SDP, 0x0000},
|
||||
{RT5677_CLK_TREE_CTRL1, 0x1111},
|
||||
{RT5677_PLL1_CTRL1, 0x0000},
|
||||
{RT5677_PLL1_CTRL2, 0x0000},
|
||||
{RT5677_DIG_MISC, 0x0029},
|
||||
{RT5677_GEN_CTRL1, 0x00FF},
|
||||
{RT5677_GPIO_CTRL2, 0x0020},
|
||||
{RT5677_PWR_DIG2, 0x9024}, /* Power on ADC Stereo Filters */
|
||||
{RT5677_PDM_OUT_CTRL, 0x0088}, /* Unmute PDM, set stereo1 DAC */
|
||||
{RT5677_PDM_DATA_CTRL1, 0x0001}, /* Sysclk to PDM filter divider 2 */
|
||||
};
|
||||
|
||||
/**
|
||||
* rt5677_i2c_read() - Read a 16-bit register
|
||||
*
|
||||
* @priv: Private driver data
|
||||
* @reg: Register number to read
|
||||
* @returns data read or -ve on error
|
||||
*/
|
||||
static int rt5677_i2c_read(struct rt5677_priv *priv, uint reg)
|
||||
{
|
||||
u8 buf[2];
|
||||
int ret;
|
||||
|
||||
ret = dm_i2c_read(priv->dev, reg, buf, sizeof(u16));
|
||||
if (ret)
|
||||
return ret;
|
||||
return buf[0] << 8 | buf[1];
|
||||
}
|
||||
|
||||
/**
|
||||
* rt5677_i2c_write() - Write a 16-bit register
|
||||
*
|
||||
* @priv: Private driver data
|
||||
* @reg: Register number to read
|
||||
* @data: Data to write
|
||||
* @returns 0 if OK, -ve on error
|
||||
*/
|
||||
static int rt5677_i2c_write(struct rt5677_priv *priv, uint reg, uint data)
|
||||
{
|
||||
u8 buf[2];
|
||||
|
||||
buf[0] = (data >> 8) & 0xff;
|
||||
buf[1] = data & 0xff;
|
||||
|
||||
return dm_i2c_write(priv->dev, reg, buf, sizeof(u16));
|
||||
}
|
||||
|
||||
/**
|
||||
* rt5677_bic_or() - Set and clear bits of a codec register
|
||||
*
|
||||
* @priv: Private driver data
|
||||
* @reg: Register number to update
|
||||
* @bic: Mask of bits to clear
|
||||
* @set: Mask of bits to set
|
||||
* @returns 0 if OK, -ve on error
|
||||
*
|
||||
*/
|
||||
static int rt5677_bic_or(struct rt5677_priv *priv, uint reg, uint bic,
|
||||
uint set)
|
||||
{
|
||||
uint old, new_value;
|
||||
int ret;
|
||||
|
||||
old = rt5677_i2c_read(priv, reg);
|
||||
if (old < 0)
|
||||
return old;
|
||||
|
||||
new_value = (old & ~bic) | (set & bic);
|
||||
|
||||
if (old != new_value) {
|
||||
ret = rt5677_i2c_write(priv, reg, new_value);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* rt5677_reg_init() - Initialise codec regs w/static/base values
|
||||
*
|
||||
* @priv: Private driver data
|
||||
* @returns 0 if OK, -ve on error
|
||||
*/
|
||||
static int rt5677_reg_init(struct rt5677_priv *priv)
|
||||
{
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(init_list); i++) {
|
||||
ret = rt5677_i2c_write(priv, init_list[i].reg, init_list[i].val);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
static void debug_dump_5677_regs(struct rt5677_priv *priv, int swap)
|
||||
{
|
||||
uint i, reg_word;
|
||||
|
||||
/* Show all 16-bit codec regs */
|
||||
for (i = 0; i < RT5677_REG_CNT; i++) {
|
||||
if (i % 8 == 0)
|
||||
log_debug("\nMX%02x: ", i);
|
||||
|
||||
rt5677_i2c_read(priv, (u8)i, ®_word);
|
||||
if (swap)
|
||||
log_debug("%04x ", swap_bytes16(reg_word));
|
||||
else
|
||||
log_debug("%04x ", reg_word);
|
||||
}
|
||||
log_debug("\n");
|
||||
|
||||
/* Show all 16-bit 'private' codec regs */
|
||||
for (i = 0; i < RT5677_PR_REG_CNT; i++) {
|
||||
if (i % 8 == 0)
|
||||
log_debug("\nPR%02x: ", i);
|
||||
|
||||
rt5677_i2c_write(priv, RT5677_PRIV_INDEX, i);
|
||||
rt5677_i2c_read(priv, RT5677_PRIV_DATA, ®_word);
|
||||
if (swap)
|
||||
log_debug("%04x ", swap_bytes16(reg_word));
|
||||
else
|
||||
log_debug("%04x ", reg_word);
|
||||
}
|
||||
log_debug("\n");
|
||||
}
|
||||
#endif /* DEBUG */
|
||||
|
||||
static int rt5677_hw_params(struct rt5677_priv *priv, uint bits_per_sample)
|
||||
{
|
||||
int ret;
|
||||
|
||||
switch (bits_per_sample) {
|
||||
case 16:
|
||||
ret = rt5677_bic_or(priv, RT5677_I2S1_SDP, RT5677_I2S_DL_MASK,
|
||||
0);
|
||||
if (ret) {
|
||||
log_debug("Error updating I2S1 Interface Ctrl reg\n");
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
log_err("Illegal bits per sample %d\n", bits_per_sample);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* rt5677_set_fmt() - set rt5677 I2S format
|
||||
*
|
||||
* @priv: Private driver data
|
||||
* @returns 0 if OK, -ve on error
|
||||
*/
|
||||
static int rt5677_set_fmt(struct rt5677_priv *priv)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
/*
|
||||
* Set format here: Assumes I2S, NB_NF, CBS_CFS
|
||||
*
|
||||
* CBS_CFS (Codec Bit Slave/Codec Frame Slave)
|
||||
*/
|
||||
ret = rt5677_bic_or(priv, RT5677_I2S1_SDP, RT5677_I2S_MS_MASK,
|
||||
RT5677_I2S_MS_S);
|
||||
|
||||
/* NB_NF (Normal Bit/Normal Frame) */
|
||||
ret |= rt5677_bic_or(priv, RT5677_I2S1_SDP, RT5677_I2S_BP_MASK,
|
||||
RT5677_I2S_BP_NOR);
|
||||
|
||||
/* I2S mode */
|
||||
ret |= rt5677_bic_or(priv, RT5677_I2S1_SDP, RT5677_I2S_DF_MASK,
|
||||
RT5677_I2S_DF_I2S);
|
||||
|
||||
/* A44: I2S2 (going to speaker amp) is master */
|
||||
ret |= rt5677_bic_or(priv, RT5677_I2S2_SDP, RT5677_I2S_MS_MASK,
|
||||
RT5677_I2S_MS_M);
|
||||
|
||||
if (ret) {
|
||||
log_err("Error updating I2S1 Interface Ctrl reg\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* rt5677_reset() - reset the audio codec
|
||||
*
|
||||
* @priv: Private driver data
|
||||
* @returns 0 if OK, -ve on error
|
||||
*/
|
||||
static int rt5677_reset(struct rt5677_priv *priv)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Reset the codec registers to their defaults */
|
||||
ret = rt5677_i2c_write(priv, RT5677_RESET, RT5677_SW_RESET);
|
||||
if (ret) {
|
||||
log_err("Error resetting codec\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise rt5677 codec device
|
||||
*
|
||||
* @priv: Private driver data
|
||||
* @returns 0 if OK, -ve on error
|
||||
*/
|
||||
int rt5677_device_init(struct rt5677_priv *priv)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Read status reg */
|
||||
ret = rt5677_i2c_read(priv, RT5677_RESET);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
log_debug("reg 00h, Software Reset & Status = 0x%04x\n", ret);
|
||||
|
||||
/* Reset the codec/regs */
|
||||
ret = rt5677_reset(priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = rt5677_i2c_read(priv, RT5677_VENDOR_ID1);
|
||||
if (ret < 0) {
|
||||
log_err("Error reading vendor ID\n");
|
||||
return 1;
|
||||
}
|
||||
log_debug("Hardware ID: %0xX\n", ret);
|
||||
|
||||
ret = rt5677_i2c_read(priv, RT5677_VENDOR_ID2);
|
||||
if (ret < 0) {
|
||||
log_err("Error reading vendor rev\n");
|
||||
return 1;
|
||||
}
|
||||
log_debug("Hardware revision: %04x\n", ret);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rt5677_set_params(struct udevice *dev, int interface, int rate,
|
||||
int mclk_freq, int bits_per_sample,
|
||||
uint channels)
|
||||
{
|
||||
struct rt5677_priv *priv = dev_get_priv(dev);
|
||||
int ret;
|
||||
|
||||
/* Initialise codec regs w/static/base values, same as Linux driver */
|
||||
ret = rt5677_reg_init(priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = rt5677_hw_params(priv, bits_per_sample);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = rt5677_set_fmt(priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rt5677_probe(struct udevice *dev)
|
||||
{
|
||||
struct rt5677_priv *priv = dev_get_priv(dev);
|
||||
|
||||
priv->dev = dev;
|
||||
|
||||
return rt5677_device_init(priv);
|
||||
}
|
||||
|
||||
static const struct audio_codec_ops rt5677_ops = {
|
||||
.set_params = rt5677_set_params,
|
||||
};
|
||||
|
||||
static const struct udevice_id rt5677_ids[] = {
|
||||
{ .compatible = "realtek,rt5677" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(rt5677_drv) = {
|
||||
.name = "rt5677",
|
||||
.id = UCLASS_AUDIO_CODEC,
|
||||
.of_match = rt5677_ids,
|
||||
.ops = &rt5677_ops,
|
||||
.probe = rt5677_probe,
|
||||
.priv_auto_alloc_size = sizeof(struct rt5677_priv),
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,455 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2012 Samsung Electronics
|
||||
* 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>
|
||||
|
||||
#define FIC_TX2COUNT(x) (((x) >> 24) & 0xf)
|
||||
#define FIC_TX1COUNT(x) (((x) >> 16) & 0xf)
|
||||
#define FIC_TXCOUNT(x) (((x) >> 8) & 0xf)
|
||||
#define FIC_RXCOUNT(x) (((x) >> 0) & 0xf)
|
||||
#define FICS_TXCOUNT(x) (((x) >> 8) & 0x7f)
|
||||
|
||||
#define TIMEOUT_I2S_TX 100 /* i2s transfer timeout */
|
||||
|
||||
/*
|
||||
* Sets the frame size for I2S LR clock
|
||||
*
|
||||
* @param i2s_reg i2s register address
|
||||
* @param rfs Frame Size
|
||||
*/
|
||||
static void i2s_set_lr_framesize(struct i2s_reg *i2s_reg, unsigned int rfs)
|
||||
{
|
||||
unsigned int mod = readl(&i2s_reg->mod);
|
||||
|
||||
mod &= ~MOD_RCLK_MASK;
|
||||
|
||||
switch (rfs) {
|
||||
case 768:
|
||||
mod |= MOD_RCLK_768FS;
|
||||
break;
|
||||
case 512:
|
||||
mod |= MOD_RCLK_512FS;
|
||||
break;
|
||||
case 384:
|
||||
mod |= MOD_RCLK_384FS;
|
||||
break;
|
||||
default:
|
||||
mod |= MOD_RCLK_256FS;
|
||||
break;
|
||||
}
|
||||
|
||||
writel(mod, &i2s_reg->mod);
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets the i2s transfer control
|
||||
*
|
||||
* @param i2s_reg i2s register address
|
||||
* @param on 1 enable tx , 0 disable tx transfer
|
||||
*/
|
||||
static void i2s_txctrl(struct i2s_reg *i2s_reg, int on)
|
||||
{
|
||||
unsigned int con = readl(&i2s_reg->con);
|
||||
unsigned int mod = readl(&i2s_reg->mod) & ~MOD_MASK;
|
||||
|
||||
if (on) {
|
||||
con |= CON_ACTIVE;
|
||||
con &= ~CON_TXCH_PAUSE;
|
||||
} else {
|
||||
con |= CON_TXCH_PAUSE;
|
||||
con &= ~CON_ACTIVE;
|
||||
}
|
||||
|
||||
writel(mod, &i2s_reg->mod);
|
||||
writel(con, &i2s_reg->con);
|
||||
}
|
||||
|
||||
/*
|
||||
* set the bit clock frame size (in multiples of LRCLK)
|
||||
*
|
||||
* @param i2s_reg i2s register address
|
||||
* @param bfs bit Frame Size
|
||||
*/
|
||||
static void i2s_set_bitclk_framesize(struct i2s_reg *i2s_reg, unsigned bfs)
|
||||
{
|
||||
unsigned int mod = readl(&i2s_reg->mod);
|
||||
|
||||
mod &= ~MOD_BCLK_MASK;
|
||||
|
||||
switch (bfs) {
|
||||
case 48:
|
||||
mod |= MOD_BCLK_48FS;
|
||||
break;
|
||||
case 32:
|
||||
mod |= MOD_BCLK_32FS;
|
||||
break;
|
||||
case 24:
|
||||
mod |= MOD_BCLK_24FS;
|
||||
break;
|
||||
case 16:
|
||||
mod |= MOD_BCLK_16FS;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
writel(mod, &i2s_reg->mod);
|
||||
}
|
||||
|
||||
/*
|
||||
* flushes the i2stx fifo
|
||||
*
|
||||
* @param i2s_reg i2s register address
|
||||
* @param flush Tx fifo flush command (0x00 - do not flush
|
||||
* 0x80 - flush tx fifo)
|
||||
*/
|
||||
static void i2s_fifo(struct i2s_reg *i2s_reg, unsigned int flush)
|
||||
{
|
||||
/* Flush the FIFO */
|
||||
setbits_le32(&i2s_reg->fic, flush);
|
||||
clrbits_le32(&i2s_reg->fic, flush);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set System Clock direction
|
||||
*
|
||||
* @param i2s_reg i2s register address
|
||||
* @param dir Clock direction
|
||||
*
|
||||
* @return int value 0 for success, -1 in case of error
|
||||
*/
|
||||
static int i2s_set_sysclk_dir(struct i2s_reg *i2s_reg, int dir)
|
||||
{
|
||||
unsigned int mod = readl(&i2s_reg->mod);
|
||||
|
||||
if (dir == SND_SOC_CLOCK_IN)
|
||||
mod |= MOD_CDCLKCON;
|
||||
else
|
||||
mod &= ~MOD_CDCLKCON;
|
||||
|
||||
writel(mod, &i2s_reg->mod);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets I2S Clcok format
|
||||
*
|
||||
* @param fmt i2s clock properties
|
||||
* @param i2s_reg i2s register address
|
||||
*
|
||||
* @return int value 0 for success, -1 in case of error
|
||||
*/
|
||||
static int i2s_set_fmt(struct i2s_reg *i2s_reg, unsigned int fmt)
|
||||
{
|
||||
unsigned int mod = readl(&i2s_reg->mod);
|
||||
unsigned int tmp = 0;
|
||||
unsigned int ret = 0;
|
||||
|
||||
/* Format is priority */
|
||||
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
||||
case SND_SOC_DAIFMT_RIGHT_J:
|
||||
tmp |= MOD_LR_RLOW;
|
||||
tmp |= MOD_SDF_MSB;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_LEFT_J:
|
||||
tmp |= MOD_LR_RLOW;
|
||||
tmp |= MOD_SDF_LSB;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_I2S:
|
||||
tmp |= MOD_SDF_IIS;
|
||||
break;
|
||||
default:
|
||||
debug("%s: Invalid format priority [0x%x]\n", __func__,
|
||||
(fmt & SND_SOC_DAIFMT_FORMAT_MASK));
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
/*
|
||||
* INV flag is relative to the FORMAT flag - if set it simply
|
||||
* flips the polarity specified by the Standard
|
||||
*/
|
||||
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
|
||||
case SND_SOC_DAIFMT_NB_NF:
|
||||
break;
|
||||
case SND_SOC_DAIFMT_NB_IF:
|
||||
if (tmp & MOD_LR_RLOW)
|
||||
tmp &= ~MOD_LR_RLOW;
|
||||
else
|
||||
tmp |= MOD_LR_RLOW;
|
||||
break;
|
||||
default:
|
||||
debug("%s: Invalid clock ploarity input [0x%x]\n", __func__,
|
||||
(fmt & SND_SOC_DAIFMT_INV_MASK));
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
|
||||
case SND_SOC_DAIFMT_CBS_CFS:
|
||||
tmp |= MOD_SLAVE;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_CBM_CFM:
|
||||
/* Set default source clock in Master mode */
|
||||
ret = i2s_set_sysclk_dir(i2s_reg, SND_SOC_CLOCK_OUT);
|
||||
if (ret != 0) {
|
||||
debug("%s:set i2s clock direction failed\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
debug("%s: Invalid master selection [0x%x]\n", __func__,
|
||||
(fmt & SND_SOC_DAIFMT_MASTER_MASK));
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
mod &= ~(MOD_SDF_MASK | MOD_LR_RLOW | MOD_SLAVE);
|
||||
mod |= tmp;
|
||||
writel(mod, &i2s_reg->mod);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets the sample width in bits
|
||||
*
|
||||
* @param blc samplewidth (size of sample in bits)
|
||||
* @param i2s_reg i2s register address
|
||||
*
|
||||
* @return int value 0 for success, -1 in case of error
|
||||
*/
|
||||
static int i2s_set_samplesize(struct i2s_reg *i2s_reg, unsigned int blc)
|
||||
{
|
||||
unsigned int mod = readl(&i2s_reg->mod);
|
||||
|
||||
mod &= ~MOD_BLCP_MASK;
|
||||
mod &= ~MOD_BLC_MASK;
|
||||
|
||||
switch (blc) {
|
||||
case 8:
|
||||
mod |= MOD_BLCP_8BIT;
|
||||
mod |= MOD_BLC_8BIT;
|
||||
break;
|
||||
case 16:
|
||||
mod |= MOD_BLCP_16BIT;
|
||||
mod |= MOD_BLC_16BIT;
|
||||
break;
|
||||
case 24:
|
||||
mod |= MOD_BLCP_24BIT;
|
||||
mod |= MOD_BLC_24BIT;
|
||||
break;
|
||||
default:
|
||||
debug("%s: Invalid sample size input [0x%x]\n",
|
||||
__func__, blc);
|
||||
return -ERANGE;
|
||||
}
|
||||
writel(mod, &i2s_reg->mod);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if (data_size < FIFO_LENGTH) {
|
||||
debug("%s : Invalid data size\n", __func__);
|
||||
return -ENODATA; /* invalid pcm data size */
|
||||
}
|
||||
|
||||
/* fill the tx buffer before stating the tx transmit */
|
||||
for (i = 0, ptr = data; i < FIFO_LENGTH; i++)
|
||||
writel(*ptr++, &i2s_reg->txd);
|
||||
|
||||
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(*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 -ETIMEDOUT;
|
||||
}
|
||||
}
|
||||
}
|
||||
i2s_txctrl(i2s_reg, I2S_TX_OFF);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int i2s_tx_init(struct i2s_uc_priv *pi2s_tx)
|
||||
{
|
||||
int ret;
|
||||
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);
|
||||
|
||||
/* Set EPLL Clock */
|
||||
ret = set_epll_clk(pi2s_tx->samplingrate * pi2s_tx->rfs * 4);
|
||||
} else if (pi2s_tx->id == 1) {
|
||||
/* Initialize GPIO for I2S-1 */
|
||||
exynos_pinmux_config(PERIPH_ID_I2S1, 0);
|
||||
|
||||
/* Set EPLL Clock */
|
||||
ret = set_epll_clk(pi2s_tx->audio_pll_clk);
|
||||
} else {
|
||||
debug("%s: unsupported i2s-%d bus\n", __func__, pi2s_tx->id);
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
debug("%s: epll clock set rate failed\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Select Clk Source for Audio 0 or 1 */
|
||||
ret = set_i2s_clk_source(pi2s_tx->id);
|
||||
if (ret) {
|
||||
debug("%s: unsupported clock for i2s-%d\n", __func__,
|
||||
pi2s_tx->id);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (pi2s_tx->id == 0) {
|
||||
/*Reset the i2s module */
|
||||
writel(CON_RESET, &i2s_reg->con);
|
||||
|
||||
writel(MOD_OP_CLK | MOD_RCLKSRC, &i2s_reg->mod);
|
||||
/* set i2s prescaler */
|
||||
writel(PSREN | PSVAL, &i2s_reg->psr);
|
||||
} else {
|
||||
/* Set Prescaler to get MCLK */
|
||||
ret = set_i2s_clk_prescaler(pi2s_tx->audio_pll_clk,
|
||||
(pi2s_tx->samplingrate * (pi2s_tx->rfs)),
|
||||
pi2s_tx->id);
|
||||
}
|
||||
if (ret) {
|
||||
debug("%s: unsupported prescalar for i2s-%d\n", __func__,
|
||||
pi2s_tx->id);
|
||||
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);
|
||||
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 ret;
|
||||
}
|
||||
|
||||
i2s_set_bitclk_framesize(i2s_reg, pi2s_tx->bfs);
|
||||
/* disable i2s transfer flag and flush the fifo */
|
||||
i2s_txctrl(i2s_reg, I2S_TX_OFF);
|
||||
i2s_fifo(i2s_reg, FIC_TXFLUSH);
|
||||
} else {
|
||||
debug("%s: failed\n", __func__);
|
||||
}
|
||||
|
||||
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,
|
||||
};
|
||||
@@ -0,0 +1,108 @@
|
||||
// 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>
|
||||
#include <asm/arch/power.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);
|
||||
|
||||
/* Enable codec clock */
|
||||
set_xclkout();
|
||||
|
||||
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-max98088" },
|
||||
{ .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,
|
||||
};
|
||||
@@ -0,0 +1,247 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (c) 2013 Google, Inc
|
||||
*/
|
||||
|
||||
#define LOG_CATEGORY UCLASS_SOUND
|
||||
|
||||
#include <common.h>
|
||||
#include <audio_codec.h>
|
||||
#include <dm.h>
|
||||
#include <i2s.h>
|
||||
#include <sound.h>
|
||||
#include <asm/sdl.h>
|
||||
|
||||
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 */
|
||||
bool silent; /* Sound is silent, don't use SDL */
|
||||
};
|
||||
|
||||
struct sandbox_sound_priv {
|
||||
int setup_called;
|
||||
int sum; /* Use to sum the provided audio data */
|
||||
bool allow_beep; /* true to allow the start_beep() interface */
|
||||
int frequency_hz; /* Beep frequency if active, else 0 */
|
||||
};
|
||||
|
||||
void sandbox_get_codec_params(struct udevice *dev, int *interfacep, int *ratep,
|
||||
int *mclk_freqp, int *bits_per_samplep,
|
||||
uint *channelsp)
|
||||
{
|
||||
struct sandbox_codec_priv *priv = dev_get_priv(dev);
|
||||
|
||||
*interfacep = priv->interface;
|
||||
*ratep = priv->rate;
|
||||
*mclk_freqp = priv->mclk_freq;
|
||||
*bits_per_samplep = priv->bits_per_sample;
|
||||
*channelsp = priv->channels;
|
||||
}
|
||||
|
||||
int sandbox_get_i2s_sum(struct udevice *dev)
|
||||
{
|
||||
struct sandbox_i2s_priv *priv = dev_get_priv(dev);
|
||||
|
||||
return priv->sum;
|
||||
}
|
||||
|
||||
int sandbox_get_setup_called(struct udevice *dev)
|
||||
{
|
||||
struct sandbox_sound_priv *priv = dev_get_priv(dev);
|
||||
|
||||
return priv->setup_called;
|
||||
}
|
||||
|
||||
int sandbox_get_sound_sum(struct udevice *dev)
|
||||
{
|
||||
struct sandbox_sound_priv *priv = dev_get_priv(dev);
|
||||
|
||||
return priv->sum;
|
||||
}
|
||||
|
||||
void sandbox_set_allow_beep(struct udevice *dev, bool allow)
|
||||
{
|
||||
struct sandbox_sound_priv *priv = dev_get_priv(dev);
|
||||
|
||||
priv->allow_beep = allow;
|
||||
}
|
||||
|
||||
int sandbox_get_beep_frequency(struct udevice *dev)
|
||||
{
|
||||
struct sandbox_sound_priv *priv = dev_get_priv(dev);
|
||||
|
||||
return priv->frequency_hz;
|
||||
}
|
||||
|
||||
static int sandbox_codec_set_params(struct udevice *dev, int interface,
|
||||
int rate, int mclk_freq,
|
||||
int bits_per_sample, uint channels)
|
||||
{
|
||||
struct sandbox_codec_priv *priv = dev_get_priv(dev);
|
||||
|
||||
priv->interface = interface;
|
||||
priv->rate = rate;
|
||||
priv->mclk_freq = mclk_freq;
|
||||
priv->bits_per_sample = bits_per_sample;
|
||||
priv->channels = channels;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sandbox_i2s_tx_data(struct udevice *dev, void *data,
|
||||
uint data_size)
|
||||
{
|
||||
struct sandbox_i2s_priv *priv = dev_get_priv(dev);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < data_size; i++)
|
||||
priv->sum += ((uint8_t *)data)[i];
|
||||
|
||||
if (!priv->silent) {
|
||||
int ret;
|
||||
|
||||
ret = sandbox_sdl_sound_play(data, data_size);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sandbox_i2s_probe(struct udevice *dev)
|
||||
{
|
||||
struct i2s_uc_priv *uc_priv = dev_get_uclass_priv(dev);
|
||||
struct sandbox_i2s_priv *priv = dev_get_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;
|
||||
|
||||
priv->silent = dev_read_bool(dev, "sandbox,silent");
|
||||
|
||||
if (priv->silent) {
|
||||
log_warning("Sound is silenced\n");
|
||||
} else if (sandbox_sdl_sound_init(uc_priv->samplingrate,
|
||||
uc_priv->channels)) {
|
||||
/* Ignore any error here - we'll just have no sound */
|
||||
priv->silent = true;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
int sandbox_sound_start_beep(struct udevice *dev, int frequency_hz)
|
||||
{
|
||||
struct sandbox_sound_priv *priv = dev_get_priv(dev);
|
||||
|
||||
if (!priv->allow_beep)
|
||||
return -ENOSYS;
|
||||
priv->frequency_hz = frequency_hz;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sandbox_sound_stop_beep(struct udevice *dev)
|
||||
{
|
||||
struct sandbox_sound_priv *priv = dev_get_priv(dev);
|
||||
|
||||
if (!priv->allow_beep)
|
||||
return -ENOSYS;
|
||||
priv->frequency_hz = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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,
|
||||
.start_beep = sandbox_sound_start_beep,
|
||||
.stop_beep = sandbox_sound_stop_beep,
|
||||
};
|
||||
|
||||
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,
|
||||
};
|
||||
@@ -0,0 +1,159 @@
|
||||
// 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 0;
|
||||
|
||||
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_start_beep(struct udevice *dev, int frequency_hz)
|
||||
{
|
||||
struct sound_ops *ops = sound_get_ops(dev);
|
||||
|
||||
if (!ops->start_beep)
|
||||
return -ENOSYS;
|
||||
|
||||
return ops->start_beep(dev, frequency_hz);
|
||||
}
|
||||
|
||||
int sound_stop_beep(struct udevice *dev)
|
||||
{
|
||||
struct sound_ops *ops = sound_get_ops(dev);
|
||||
|
||||
if (!ops->stop_beep)
|
||||
return -ENOSYS;
|
||||
|
||||
return ops->stop_beep(dev);
|
||||
}
|
||||
|
||||
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;
|
||||
unsigned short *data;
|
||||
uint data_size;
|
||||
int ret;
|
||||
|
||||
ret = sound_setup(dev);
|
||||
if (ret && ret != -EALREADY)
|
||||
return ret;
|
||||
|
||||
/* Try using the beep interface if available */
|
||||
ret = sound_start_beep(dev, frequency_hz);
|
||||
if (ret != -ENOSYS) {
|
||||
if (ret)
|
||||
return ret;
|
||||
mdelay(msecs);
|
||||
ret = sound_stop_beep(dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Buffer length computation */
|
||||
i2s_uc_priv = dev_get_uclass_priv(uc_priv->i2s);
|
||||
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),
|
||||
};
|
||||
@@ -0,0 +1,37 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2012 Samsung Electronics
|
||||
* R. Chandrasekar <rcsekar@samsung.com>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <sound.h>
|
||||
|
||||
void sound_create_square_wave(uint sample_rate, unsigned short *data, int size,
|
||||
uint freq, uint channels)
|
||||
{
|
||||
const unsigned short amplitude = 16000; /* between 1 and 32767 */
|
||||
const int period = freq ? sample_rate / freq : 0;
|
||||
const int half = period / 2;
|
||||
|
||||
assert(freq);
|
||||
|
||||
/* Make sure we don't overflow our buffer */
|
||||
if (size % 2)
|
||||
size--;
|
||||
|
||||
while (size) {
|
||||
int i, j;
|
||||
|
||||
for (i = 0; size && i < half; i++) {
|
||||
size -= 2;
|
||||
for (j = 0; j < channels; j++)
|
||||
*data++ = amplitude;
|
||||
}
|
||||
for (i = 0; size && i < period - half; i++) {
|
||||
size -= 2;
|
||||
for (j = 0; j < channels; j++)
|
||||
*data++ = -amplitude;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,256 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+159
|
||||
/*
|
||||
* Take from dc tegra_ahub.c
|
||||
*
|
||||
* Copyright 2018 Google LLC
|
||||
*/
|
||||
|
||||
#define LOG_CATEGORY UCLASS_MISC
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <i2s.h>
|
||||
#include <misc.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/arch-tegra/tegra_ahub.h>
|
||||
#include <asm/arch-tegra/tegra_i2s.h>
|
||||
#include "tegra_i2s_priv.h"
|
||||
|
||||
struct tegra_ahub_priv {
|
||||
struct apbif_regs *apbif_regs;
|
||||
struct xbar_regs *xbar_regs;
|
||||
u32 full_mask;
|
||||
int capacity_words; /* FIFO capacity in words */
|
||||
|
||||
/*
|
||||
* This is unset intially, but is set by tegra_ahub_ioctl() called
|
||||
* from the misc_ioctl() in tegra_sound_probe()
|
||||
*/
|
||||
struct udevice *i2s;
|
||||
struct udevice *dma;
|
||||
};
|
||||
|
||||
static int tegra_ahub_xbar_enable_i2s(struct xbar_regs *regs, int i2s_id)
|
||||
{
|
||||
/*
|
||||
* Enables I2S as the receiver of APBIF by writing APBIF_TX0 (0x01) to
|
||||
* the rx0 register
|
||||
*/
|
||||
switch (i2s_id) {
|
||||
case 0:
|
||||
writel(1, ®s->i2s0_rx0);
|
||||
break;
|
||||
case 1:
|
||||
writel(1, ®s->i2s1_rx0);
|
||||
break;
|
||||
case 2:
|
||||
writel(1, ®s->i2s2_rx0);
|
||||
break;
|
||||
case 3:
|
||||
writel(1, ®s->i2s3_rx0);
|
||||
break;
|
||||
case 4:
|
||||
writel(1, ®s->i2s4_rx0);
|
||||
break;
|
||||
default:
|
||||
log_err("Invalid I2S component id: %d\n", i2s_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra_ahub_apbif_is_full(struct udevice *dev)
|
||||
{
|
||||
struct tegra_ahub_priv *priv = dev_get_priv(dev);
|
||||
|
||||
return readl(&priv->apbif_regs->apbdma_live_stat) & priv->full_mask;
|
||||
}
|
||||
|
||||
/**
|
||||
* tegra_ahub_wait_for_space() - Wait for space in the FIFO
|
||||
*
|
||||
* @return 0 if OK, -ETIMEDOUT if no space was available in time
|
||||
*/
|
||||
static int tegra_ahub_wait_for_space(struct udevice *dev)
|
||||
{
|
||||
int i = 100000;
|
||||
ulong start;
|
||||
|
||||
/* Busy-wait initially, since this should take almost no time */
|
||||
while (i--) {
|
||||
if (!tegra_ahub_apbif_is_full(dev))
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Failed, so do a slower loop for 100ms */
|
||||
start = get_timer(0);
|
||||
while (tegra_ahub_apbif_is_full(dev)) {
|
||||
if (get_timer(start) > 100)
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra_ahub_apbif_send(struct udevice *dev, int offset,
|
||||
const void *buf, int len)
|
||||
{
|
||||
struct tegra_ahub_priv *priv = dev_get_priv(dev);
|
||||
const u32 *data = (const u32 *)buf;
|
||||
ssize_t written = 0;
|
||||
|
||||
if (len % sizeof(*data)) {
|
||||
log_err("Data size (%zd) must be aligned to %zd.\n", len,
|
||||
sizeof(*data));
|
||||
return -EFAULT;
|
||||
}
|
||||
while (written < len) {
|
||||
int ret = tegra_ahub_wait_for_space(dev);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
writel(*data++, &priv->apbif_regs->channel0_txfifo);
|
||||
written += sizeof(*data);
|
||||
}
|
||||
|
||||
return written;
|
||||
}
|
||||
|
||||
static void tegra_ahub_apbif_set_cif(struct udevice *dev, u32 value)
|
||||
{
|
||||
struct tegra_ahub_priv *priv = dev_get_priv(dev);
|
||||
|
||||
writel(value, &priv->apbif_regs->channel0_cif_tx0_ctrl);
|
||||
}
|
||||
|
||||
static void tegra_ahub_apbif_enable_channel0(struct udevice *dev,
|
||||
int fifo_threshold)
|
||||
{
|
||||
struct tegra_ahub_priv *priv = dev_get_priv(dev);
|
||||
|
||||
u32 ctrl = TEGRA_AHUB_CHANNEL_CTRL_TX_PACK_EN |
|
||||
TEGRA_AHUB_CHANNEL_CTRL_TX_PACK_16 |
|
||||
TEGRA_AHUB_CHANNEL_CTRL_TX_EN;
|
||||
|
||||
fifo_threshold--; /* fifo_threshold starts from 1 */
|
||||
ctrl |= (fifo_threshold << TEGRA_AHUB_CHANNEL_CTRL_TX_THRESHOLD_SHIFT);
|
||||
writel(ctrl, &priv->apbif_regs->channel0_ctrl);
|
||||
}
|
||||
|
||||
static u32 tegra_ahub_get_cif(bool is_receive, uint channels,
|
||||
uint bits_per_sample, uint fifo_threshold)
|
||||
{
|
||||
uint audio_bits = (bits_per_sample >> 2) - 1;
|
||||
u32 val;
|
||||
|
||||
channels--; /* Channels in CIF starts from 1 */
|
||||
fifo_threshold--; /* FIFO threshold starts from 1 */
|
||||
/* Assume input and output are always using same channel / bits */
|
||||
val = channels << TEGRA_AUDIOCIF_CTRL_AUDIO_CHANNELS_SHIFT |
|
||||
channels << TEGRA_AUDIOCIF_CTRL_CLIENT_CHANNELS_SHIFT |
|
||||
audio_bits << TEGRA_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT |
|
||||
audio_bits << TEGRA_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT |
|
||||
fifo_threshold << TEGRA_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT |
|
||||
(is_receive ? TEGRA_AUDIOCIF_DIRECTION_RX <<
|
||||
TEGRA_AUDIOCIF_CTRL_DIRECTION_SHIFT : 0);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static int tegra_ahub_enable(struct udevice *dev)
|
||||
{
|
||||
struct tegra_ahub_priv *priv = dev_get_priv(dev);
|
||||
struct i2s_uc_priv *uc_priv = dev_get_uclass_priv(priv->i2s);
|
||||
u32 cif_ctrl = 0;
|
||||
int ret;
|
||||
|
||||
/* We use APBIF channel0 as a sender */
|
||||
priv->full_mask = TEGRA_AHUB_APBDMA_LIVE_STATUS_CH0_TX_CIF_FIFO_FULL;
|
||||
priv->capacity_words = 8;
|
||||
|
||||
/*
|
||||
* FIFO is inactive until (fifo_threshold) of words are sent. For
|
||||
* better performance, we want to set it to half of capacity.
|
||||
*/
|
||||
u32 fifo_threshold = priv->capacity_words / 2;
|
||||
|
||||
/*
|
||||
* Setup audio client interface (ACIF): APBIF (channel0) as sender and
|
||||
* I2S as receiver
|
||||
*/
|
||||
cif_ctrl = tegra_ahub_get_cif(true, uc_priv->channels,
|
||||
uc_priv->bitspersample, fifo_threshold);
|
||||
tegra_i2s_set_cif_tx_ctrl(priv->i2s, cif_ctrl);
|
||||
|
||||
cif_ctrl = tegra_ahub_get_cif(false, uc_priv->channels,
|
||||
uc_priv->bitspersample, fifo_threshold);
|
||||
tegra_ahub_apbif_set_cif(dev, cif_ctrl);
|
||||
tegra_ahub_apbif_enable_channel0(dev, fifo_threshold);
|
||||
|
||||
ret = tegra_ahub_xbar_enable_i2s(priv->xbar_regs, uc_priv->id);
|
||||
if (ret)
|
||||
return ret;
|
||||
log_debug("ahub: channels=%d, bitspersample=%d, cif_ctrl=%x, fifo_threshold=%d, id=%d\n",
|
||||
uc_priv->channels, uc_priv->bitspersample, cif_ctrl,
|
||||
fifo_threshold, uc_priv->id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra_ahub_ioctl(struct udevice *dev, unsigned long request,
|
||||
void *buf)
|
||||
{
|
||||
struct tegra_ahub_priv *priv = dev_get_priv(dev);
|
||||
|
||||
if (request != AHUB_MISCOP_SET_I2S)
|
||||
return -ENOSYS;
|
||||
|
||||
priv->i2s = *(struct udevice **)buf;
|
||||
log_debug("i2s set to '%s'\n", priv->i2s->name);
|
||||
|
||||
return tegra_ahub_enable(dev);
|
||||
}
|
||||
|
||||
static int tegra_ahub_probe(struct udevice *dev)
|
||||
{
|
||||
struct tegra_ahub_priv *priv = dev_get_priv(dev);
|
||||
ulong addr;
|
||||
|
||||
addr = dev_read_addr_index(dev, 0);
|
||||
if (addr == FDT_ADDR_T_NONE) {
|
||||
log_debug("Invalid apbif address\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
priv->apbif_regs = (struct apbif_regs *)addr;
|
||||
|
||||
addr = dev_read_addr_index(dev, 1);
|
||||
if (addr == FDT_ADDR_T_NONE) {
|
||||
log_debug("Invalid xbar address\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
priv->xbar_regs = (struct xbar_regs *)addr;
|
||||
log_debug("ahub apbif_regs=%p, xbar_regs=%p\n", priv->apbif_regs,
|
||||
priv->xbar_regs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct misc_ops tegra_ahub_ops = {
|
||||
.write = tegra_ahub_apbif_send,
|
||||
.ioctl = tegra_ahub_ioctl,
|
||||
};
|
||||
|
||||
static const struct udevice_id tegra_ahub_ids[] = {
|
||||
{ .compatible = "nvidia,tegra124-ahub" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(tegra_ahub) = {
|
||||
.name = "tegra_ahub",
|
||||
.id = UCLASS_MISC,
|
||||
.of_match = tegra_ahub_ids,
|
||||
.ops = &tegra_ahub_ops,
|
||||
.probe = tegra_ahub_probe,
|
||||
.priv_auto_alloc_size = sizeof(struct tegra_ahub_priv),
|
||||
};
|
||||
@@ -0,0 +1,123 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright 2018 Google LLC
|
||||
* Written by Simon Glass <sjg@chromium.org>
|
||||
*/
|
||||
#define LOG_CATEGORY UCLASS_I2S
|
||||
#define LOG_DEBUG
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <i2s.h>
|
||||
#include <misc.h>
|
||||
#include <sound.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/arch-tegra/tegra_i2s.h>
|
||||
#include "tegra_i2s_priv.h"
|
||||
|
||||
int tegra_i2s_set_cif_tx_ctrl(struct udevice *dev, u32 value)
|
||||
{
|
||||
struct i2s_uc_priv *priv = dev_get_uclass_priv(dev);
|
||||
struct i2s_ctlr *regs = (struct i2s_ctlr *)priv->base_address;
|
||||
|
||||
writel(value, ®s->cif_tx_ctrl);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tegra_i2s_transmit_enable(struct i2s_ctlr *regs, int on)
|
||||
{
|
||||
clrsetbits_le32(®s->ctrl, I2S_CTRL_XFER_EN_TX,
|
||||
on ? I2S_CTRL_XFER_EN_TX : 0);
|
||||
}
|
||||
|
||||
static int i2s_tx_init(struct i2s_uc_priv *pi2s_tx)
|
||||
{
|
||||
struct i2s_ctlr *regs = (struct i2s_ctlr *)pi2s_tx->base_address;
|
||||
u32 audio_bits = (pi2s_tx->bitspersample >> 2) - 1;
|
||||
u32 ctrl = readl(®s->ctrl);
|
||||
|
||||
/* Set format to LRCK / Left Low */
|
||||
ctrl &= ~(I2S_CTRL_FRAME_FORMAT_MASK | I2S_CTRL_LRCK_MASK);
|
||||
ctrl |= I2S_CTRL_FRAME_FORMAT_LRCK;
|
||||
ctrl |= I2S_CTRL_LRCK_L_LOW;
|
||||
|
||||
/* Disable all transmission until we are ready to transfer */
|
||||
ctrl &= ~(I2S_CTRL_XFER_EN_TX | I2S_CTRL_XFER_EN_RX);
|
||||
|
||||
/* Serve as master */
|
||||
ctrl |= I2S_CTRL_MASTER_ENABLE;
|
||||
|
||||
/* Configure audio bits size */
|
||||
ctrl &= ~I2S_CTRL_BIT_SIZE_MASK;
|
||||
ctrl |= audio_bits << I2S_CTRL_BIT_SIZE_SHIFT;
|
||||
writel(ctrl, ®s->ctrl);
|
||||
|
||||
/* Timing in LRCK mode: */
|
||||
writel(pi2s_tx->bitspersample, ®s->timing);
|
||||
|
||||
/* I2S mode has [TX/RX]_DATA_OFFSET both set to 1 */
|
||||
writel(((1 << I2S_OFFSET_RX_DATA_OFFSET_SHIFT) |
|
||||
(1 << I2S_OFFSET_TX_DATA_OFFSET_SHIFT)), ®s->offset);
|
||||
|
||||
/* FSYNC_WIDTH = 2 clocks wide, TOTAL_SLOTS = 2 slots per fsync */
|
||||
writel((2 - 1) << I2S_CH_CTRL_FSYNC_WIDTH_SHIFT, ®s->ch_ctrl);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra_i2s_tx_data(struct udevice *dev, void *data, uint data_size)
|
||||
{
|
||||
struct i2s_uc_priv *priv = dev_get_uclass_priv(dev);
|
||||
struct i2s_ctlr *regs = (struct i2s_ctlr *)priv->base_address;
|
||||
int ret;
|
||||
|
||||
tegra_i2s_transmit_enable(regs, 1);
|
||||
ret = misc_write(dev_get_parent(dev), 0, data, data_size);
|
||||
tegra_i2s_transmit_enable(regs, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
else if (ret < data_size)
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra_i2s_probe(struct udevice *dev)
|
||||
{
|
||||
struct i2s_uc_priv *priv = dev_get_uclass_priv(dev);
|
||||
ulong base;
|
||||
|
||||
base = dev_read_addr(dev);
|
||||
if (base == FDT_ADDR_T_NONE) {
|
||||
debug("%s: Missing i2s base\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
priv->base_address = base;
|
||||
priv->id = 1;
|
||||
priv->audio_pll_clk = 4800000;
|
||||
priv->samplingrate = 48000;
|
||||
priv->bitspersample = 16;
|
||||
priv->channels = 2;
|
||||
priv->rfs = 256;
|
||||
priv->bfs = 32;
|
||||
|
||||
return i2s_tx_init(priv);
|
||||
}
|
||||
|
||||
static const struct i2s_ops tegra_i2s_ops = {
|
||||
.tx_data = tegra_i2s_tx_data,
|
||||
};
|
||||
|
||||
static const struct udevice_id tegra_i2s_ids[] = {
|
||||
{ .compatible = "nvidia,tegra124-i2s" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(tegra_i2s) = {
|
||||
.name = "tegra_i2s",
|
||||
.id = UCLASS_I2S,
|
||||
.of_match = tegra_i2s_ids,
|
||||
.probe = tegra_i2s_probe,
|
||||
.ops = &tegra_i2s_ops,
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* Copyright 2018 Google LLC
|
||||
* Written by Simon Glass <sjg@chromium.org>
|
||||
*/
|
||||
|
||||
#ifndef __TEGRA_I2S_PRIV_H
|
||||
#define __TEGRA_I2S_PRIV_H
|
||||
|
||||
enum {
|
||||
/* Set i2s device (in buf) */
|
||||
AHUB_MISCOP_SET_I2S,
|
||||
};
|
||||
|
||||
/*
|
||||
* tegra_i2s_set_cif_tx_ctrl() - Set the I2C port to send to
|
||||
*
|
||||
* The CIF is not really part of I2S -- it's for Audio Hub to control
|
||||
* the interface between I2S and Audio Hub. However since it's put in
|
||||
* the I2S registers domain instead of the Audio Hub, we need to export
|
||||
* this as a function.
|
||||
*
|
||||
* @dev: I2S device
|
||||
* @value: Value to write to CIF_TX_CTRL register
|
||||
* @return 0
|
||||
*/
|
||||
int tegra_i2s_set_cif_tx_ctrl(struct udevice *dev, u32 value);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,100 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright 2018 Google, LLC
|
||||
* Written by Simon Glass <sjg@chromium.org>
|
||||
*/
|
||||
|
||||
#define LOG_CATEGORY UCLASS_I2S
|
||||
|
||||
#include <common.h>
|
||||
#include <audio_codec.h>
|
||||
#include <dm.h>
|
||||
#include <i2s.h>
|
||||
#include <misc.h>
|
||||
#include <sound.h>
|
||||
#include <asm/gpio.h>
|
||||
#include "tegra_i2s_priv.h"
|
||||
|
||||
static int tegra_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 tegra_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 tegra_sound_probe(struct udevice *dev)
|
||||
{
|
||||
struct sound_uc_priv *uc_priv = dev_get_uclass_priv(dev);
|
||||
struct gpio_desc en_gpio;
|
||||
struct udevice *ahub;
|
||||
int ret;
|
||||
|
||||
ret = gpio_request_by_name(dev, "codec-enable-gpio", 0, &en_gpio,
|
||||
GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE);
|
||||
|
||||
ret = uclass_get_device_by_phandle(UCLASS_AUDIO_CODEC, dev,
|
||||
"nvidia,audio-codec",
|
||||
&uc_priv->codec);
|
||||
if (ret) {
|
||||
log_debug("Failed to probe audio codec\n");
|
||||
return ret;
|
||||
}
|
||||
ret = uclass_get_device_by_phandle(UCLASS_I2S, dev,
|
||||
"nvidia,i2s-controller",
|
||||
&uc_priv->i2s);
|
||||
if (ret) {
|
||||
log_debug("Cannot find i2s: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Set up the audio hub, telling it the currect i2s to use */
|
||||
ahub = dev_get_parent(uc_priv->i2s);
|
||||
ret = misc_ioctl(ahub, AHUB_MISCOP_SET_I2S, &uc_priv->i2s);
|
||||
if (ret) {
|
||||
log_debug("Cannot set i2c: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
log_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 tegra_sound_ops = {
|
||||
.setup = tegra_sound_setup,
|
||||
.play = tegra_sound_play,
|
||||
};
|
||||
|
||||
static const struct udevice_id tegra_sound_ids[] = {
|
||||
{ .compatible = "nvidia,tegra-audio-max98090-nyan-big" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(tegra_sound) = {
|
||||
.name = "tegra_sound",
|
||||
.id = UCLASS_SOUND,
|
||||
.of_match = tegra_sound_ids,
|
||||
.probe = tegra_sound_probe,
|
||||
.ops = &tegra_sound_ops,
|
||||
};
|
||||
@@ -0,0 +1,876 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2012 Samsung Electronics
|
||||
* R. Chandrasekar <rcsekar@samsung.com>
|
||||
*/
|
||||
#include <common.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"
|
||||
|
||||
/* defines for wm8994 system clock selection */
|
||||
#define SEL_MCLK1 0x00
|
||||
#define SEL_MCLK2 0x08
|
||||
#define SEL_FLL1 0x10
|
||||
#define SEL_FLL2 0x18
|
||||
|
||||
/* fll config to configure fll */
|
||||
struct wm8994_fll_config {
|
||||
int src; /* Source */
|
||||
int in; /* Input frequency in Hz */
|
||||
int out; /* output frequency in Hz */
|
||||
};
|
||||
|
||||
/* codec private data */
|
||||
struct wm8994_priv {
|
||||
enum wm8994_type type; /* codec type of wolfson */
|
||||
int revision; /* Revision */
|
||||
int sysclk[WM8994_MAX_AIF]; /* System clock frequency in Hz */
|
||||
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 */
|
||||
static unsigned int src_rate[] = {
|
||||
8000, 11025, 12000, 16000, 22050, 24000,
|
||||
32000, 44100, 48000, 88200, 96000
|
||||
};
|
||||
|
||||
/* op clock divisions */
|
||||
static int opclk_divs[] = { 10, 20, 30, 40, 55, 60, 80, 120, 160 };
|
||||
|
||||
/* lr clock frame size ratio */
|
||||
static int fs_ratios[] = {
|
||||
64, 128, 192, 256, 348, 512, 768, 1024, 1408, 1536
|
||||
};
|
||||
|
||||
/* bit clock divisors */
|
||||
static int bclk_divs[] = {
|
||||
10, 15, 20, 30, 40, 50, 60, 80, 110, 120, 160, 220, 240, 320, 440, 480,
|
||||
640, 880, 960, 1280, 1760, 1920
|
||||
};
|
||||
|
||||
/*
|
||||
* 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(struct wm8994_priv *priv, unsigned int reg,
|
||||
unsigned short data)
|
||||
{
|
||||
unsigned char val[2];
|
||||
|
||||
val[0] = (unsigned char)((data >> 8) & 0xff);
|
||||
val[1] = (unsigned char)(data & 0xff);
|
||||
debug("Write Addr : 0x%04X, Data : 0x%04X\n", reg, data);
|
||||
|
||||
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(struct wm8994_priv *priv, unsigned int reg,
|
||||
unsigned short *data)
|
||||
{
|
||||
unsigned char val[2];
|
||||
int ret;
|
||||
|
||||
ret = dm_i2c_read(priv->dev, reg, val, 1);
|
||||
if (ret != 0) {
|
||||
debug("%s: Error while reading register %#04x\n",
|
||||
__func__, reg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
*data = val[0];
|
||||
*data <<= 8;
|
||||
*data |= val[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 1 if change in the register value,
|
||||
* 0 for no change or negative error code.
|
||||
*/
|
||||
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(priv, reg, &old) != 0)
|
||||
return -1;
|
||||
new = (old & ~mask) | (value & mask);
|
||||
change = (old != new) ? 1 : 0;
|
||||
if (change)
|
||||
ret = wm8994_i2c_write(priv, reg, new);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return change;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets i2s set format
|
||||
*
|
||||
* @param priv wm8994 information
|
||||
* @param aif_id Interface ID
|
||||
* @param fmt i2S format
|
||||
*
|
||||
* @return -1 for error and 0 Success.
|
||||
*/
|
||||
static int wm8994_set_fmt(struct wm8994_priv *priv, int aif_id, uint fmt)
|
||||
{
|
||||
int ms_reg;
|
||||
int aif_reg;
|
||||
int ms = 0;
|
||||
int aif = 0;
|
||||
int aif_clk = 0;
|
||||
int error = 0;
|
||||
|
||||
switch (aif_id) {
|
||||
case 1:
|
||||
ms_reg = WM8994_AIF1_MASTER_SLAVE;
|
||||
aif_reg = WM8994_AIF1_CONTROL_1;
|
||||
aif_clk = WM8994_AIF1_CLOCKING_1;
|
||||
break;
|
||||
case 2:
|
||||
ms_reg = WM8994_AIF2_MASTER_SLAVE;
|
||||
aif_reg = WM8994_AIF2_CONTROL_1;
|
||||
aif_clk = WM8994_AIF2_CLOCKING_1;
|
||||
break;
|
||||
default:
|
||||
debug("%s: Invalid audio interface selection\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
|
||||
case SND_SOC_DAIFMT_CBS_CFS:
|
||||
break;
|
||||
case SND_SOC_DAIFMT_CBM_CFM:
|
||||
ms = WM8994_AIF1_MSTR;
|
||||
break;
|
||||
default:
|
||||
debug("%s: Invalid i2s master selection\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
||||
case SND_SOC_DAIFMT_DSP_B:
|
||||
aif |= WM8994_AIF1_LRCLK_INV;
|
||||
case SND_SOC_DAIFMT_DSP_A:
|
||||
aif |= 0x18;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_I2S:
|
||||
aif |= 0x10;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_RIGHT_J:
|
||||
break;
|
||||
case SND_SOC_DAIFMT_LEFT_J:
|
||||
aif |= 0x8;
|
||||
break;
|
||||
default:
|
||||
debug("%s: Invalid i2s format selection\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
||||
case SND_SOC_DAIFMT_DSP_A:
|
||||
case SND_SOC_DAIFMT_DSP_B:
|
||||
/* frame inversion not valid for DSP modes */
|
||||
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
|
||||
case SND_SOC_DAIFMT_NB_NF:
|
||||
break;
|
||||
case SND_SOC_DAIFMT_IB_NF:
|
||||
aif |= WM8994_AIF1_BCLK_INV;
|
||||
break;
|
||||
default:
|
||||
debug("%s: Invalid i2s frame inverse selection\n",
|
||||
__func__);
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
|
||||
case SND_SOC_DAIFMT_I2S:
|
||||
case SND_SOC_DAIFMT_RIGHT_J:
|
||||
case SND_SOC_DAIFMT_LEFT_J:
|
||||
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
|
||||
case SND_SOC_DAIFMT_NB_NF:
|
||||
break;
|
||||
case SND_SOC_DAIFMT_IB_IF:
|
||||
aif |= WM8994_AIF1_BCLK_INV | WM8994_AIF1_LRCLK_INV;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_IB_NF:
|
||||
aif |= WM8994_AIF1_BCLK_INV;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_NB_IF:
|
||||
aif |= WM8994_AIF1_LRCLK_INV;
|
||||
break;
|
||||
default:
|
||||
debug("%s: Invalid i2s clock polarity selection\n",
|
||||
__func__);
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
debug("%s: Invalid i2s format selection\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
error = wm8994_bic_or(priv, aif_reg, WM8994_AIF1_BCLK_INV |
|
||||
WM8994_AIF1_LRCLK_INV_MASK |
|
||||
WM8994_AIF1_FMT_MASK, aif);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets hw params FOR WM8994
|
||||
*
|
||||
* @param priv wm8994 information pointer
|
||||
* @param aif_id Audio interface ID
|
||||
* @param sampling_rate Sampling rate
|
||||
* @param bits_per_sample Bits per sample
|
||||
* @param Channels Channels in the given audio input
|
||||
*
|
||||
* @return -1 for error and 0 Success.
|
||||
*/
|
||||
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;
|
||||
int bclk_reg;
|
||||
int bclk = 0;
|
||||
int rate_reg;
|
||||
int aif1 = 0;
|
||||
int aif2 = 0;
|
||||
int rate_val = 0;
|
||||
int id = aif_id - 1;
|
||||
int i, cur_val, best_val, bclk_rate, best;
|
||||
unsigned short reg_data;
|
||||
int ret = 0;
|
||||
|
||||
switch (aif_id) {
|
||||
case 1:
|
||||
aif1_reg = WM8994_AIF1_CONTROL_1;
|
||||
aif2_reg = WM8994_AIF1_CONTROL_2;
|
||||
bclk_reg = WM8994_AIF1_BCLK;
|
||||
rate_reg = WM8994_AIF1_RATE;
|
||||
break;
|
||||
case 2:
|
||||
aif1_reg = WM8994_AIF2_CONTROL_1;
|
||||
aif2_reg = WM8994_AIF2_CONTROL_2;
|
||||
bclk_reg = WM8994_AIF2_BCLK;
|
||||
rate_reg = WM8994_AIF2_RATE;
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
bclk_rate = sampling_rate * 32;
|
||||
switch (bits_per_sample) {
|
||||
case 16:
|
||||
bclk_rate *= 16;
|
||||
break;
|
||||
case 20:
|
||||
bclk_rate *= 20;
|
||||
aif1 |= 0x20;
|
||||
break;
|
||||
case 24:
|
||||
bclk_rate *= 24;
|
||||
aif1 |= 0x40;
|
||||
break;
|
||||
case 32:
|
||||
bclk_rate *= 32;
|
||||
aif1 |= 0x60;
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Try to find an appropriate sample rate; look for an exact match. */
|
||||
for (i = 0; i < ARRAY_SIZE(src_rate); i++)
|
||||
if (src_rate[i] == sampling_rate)
|
||||
break;
|
||||
|
||||
if (i == ARRAY_SIZE(src_rate)) {
|
||||
debug("%s: Could not get the best matching samplingrate\n",
|
||||
__func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
rate_val |= i << WM8994_AIF1_SR_SHIFT;
|
||||
|
||||
/* AIFCLK/fs ratio; look for a close match in either direction */
|
||||
best = 0;
|
||||
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 - priv->aifclk[id]);
|
||||
if (cur_val >= best_val)
|
||||
continue;
|
||||
best = i;
|
||||
best_val = cur_val;
|
||||
}
|
||||
|
||||
rate_val |= best;
|
||||
|
||||
/*
|
||||
* We may not get quite the right frequency if using
|
||||
* approximate clocks so look for the closest match that is
|
||||
* higher than the target (we need to ensure that there enough
|
||||
* BCLKs to clock out the samples).
|
||||
*/
|
||||
best = 0;
|
||||
for (i = 0; i < ARRAY_SIZE(bclk_divs); i++) {
|
||||
cur_val = (priv->aifclk[id] * 10 / bclk_divs[i]) - bclk_rate;
|
||||
if (cur_val < 0) /* BCLK table is sorted */
|
||||
break;
|
||||
best = i;
|
||||
}
|
||||
|
||||
if (i == ARRAY_SIZE(bclk_divs)) {
|
||||
debug("%s: Could not get the best matching bclk division\n",
|
||||
__func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
bclk_rate = priv->aifclk[id] * 10 / bclk_divs[best];
|
||||
bclk |= best << WM8994_AIF1_BCLK_DIV_SHIFT;
|
||||
|
||||
if (wm8994_i2c_read(priv, aif1_reg, ®_data) != 0) {
|
||||
debug("%s: AIF1 register read Failed\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((channels == 1) && ((reg_data & 0x18) == 0x18))
|
||||
aif2 |= WM8994_AIF1_MONO;
|
||||
|
||||
if (priv->aifclk[id] == 0) {
|
||||
debug("%s:Audio interface clock not set\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if (ret < 0) {
|
||||
debug("%s: codec register access error\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Configures Audio interface Clock
|
||||
*
|
||||
* @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 *priv, int aif)
|
||||
{
|
||||
int rate;
|
||||
int reg1 = 0;
|
||||
int offset;
|
||||
int ret;
|
||||
|
||||
/* AIF(1/0) register adress offset calculated */
|
||||
if (aif-1)
|
||||
offset = 4;
|
||||
else
|
||||
offset = 0;
|
||||
|
||||
switch (priv->sysclk[aif - 1]) {
|
||||
case WM8994_SYSCLK_MCLK1:
|
||||
reg1 |= SEL_MCLK1;
|
||||
rate = priv->mclk[0];
|
||||
break;
|
||||
|
||||
case WM8994_SYSCLK_MCLK2:
|
||||
reg1 |= SEL_MCLK2;
|
||||
rate = priv->mclk[1];
|
||||
break;
|
||||
|
||||
case WM8994_SYSCLK_FLL1:
|
||||
reg1 |= SEL_FLL1;
|
||||
rate = priv->fll[0].out;
|
||||
break;
|
||||
|
||||
case WM8994_SYSCLK_FLL2:
|
||||
reg1 |= SEL_FLL2;
|
||||
rate = priv->fll[1].out;
|
||||
break;
|
||||
|
||||
default:
|
||||
debug("%s: Invalid input clock selection [%d]\n",
|
||||
__func__, priv->sysclk[aif - 1]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* if input clock frequenct is more than 135Mhz then divide */
|
||||
if (rate >= WM8994_MAX_INPUT_CLK_FREQ) {
|
||||
rate /= 2;
|
||||
reg1 |= WM8994_AIF1CLK_DIV;
|
||||
}
|
||||
|
||||
priv->aifclk[aif - 1] = rate;
|
||||
|
||||
ret = wm8994_bic_or(priv, WM8994_AIF1_CLOCKING_1 + offset,
|
||||
WM8994_AIF1CLK_SRC_MASK | WM8994_AIF1CLK_DIV,
|
||||
reg1);
|
||||
|
||||
if (aif == WM8994_AIF1)
|
||||
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_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);
|
||||
|
||||
if (ret < 0) {
|
||||
debug("%s: codec register access error\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Configures Audio interface for the given frequency
|
||||
*
|
||||
* @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 *priv, int aif_id, int clk_id,
|
||||
unsigned int freq)
|
||||
{
|
||||
int i;
|
||||
int ret = 0;
|
||||
|
||||
priv->sysclk[aif_id - 1] = clk_id;
|
||||
|
||||
switch (clk_id) {
|
||||
case WM8994_SYSCLK_MCLK1:
|
||||
priv->mclk[0] = freq;
|
||||
if (aif_id == 2) {
|
||||
ret = wm8994_bic_or(priv, WM8994_AIF1_CLOCKING_2,
|
||||
WM8994_AIF2DAC_DIV_MASK, 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case WM8994_SYSCLK_MCLK2:
|
||||
/* TODO: Set GPIO AF */
|
||||
priv->mclk[1] = freq;
|
||||
break;
|
||||
|
||||
case WM8994_SYSCLK_FLL1:
|
||||
case WM8994_SYSCLK_FLL2:
|
||||
break;
|
||||
|
||||
case WM8994_SYSCLK_OPCLK:
|
||||
/*
|
||||
* Special case - a division (times 10) is given and
|
||||
* no effect on main clocking.
|
||||
*/
|
||||
if (freq) {
|
||||
for (i = 0; i < ARRAY_SIZE(opclk_divs); i++)
|
||||
if (opclk_divs[i] == freq)
|
||||
break;
|
||||
if (i == ARRAY_SIZE(opclk_divs)) {
|
||||
debug("%s frequency divisor not found\n",
|
||||
__func__);
|
||||
return -1;
|
||||
}
|
||||
ret = wm8994_bic_or(priv, WM8994_CLOCKING_2,
|
||||
WM8994_OPCLK_DIV_MASK, i);
|
||||
ret |= wm8994_bic_or(priv, WM8994_POWER_MANAGEMENT_2,
|
||||
WM8994_OPCLK_ENA,
|
||||
WM8994_OPCLK_ENA);
|
||||
} else {
|
||||
ret |= wm8994_bic_or(priv, WM8994_POWER_MANAGEMENT_2,
|
||||
WM8994_OPCLK_ENA, 0);
|
||||
}
|
||||
|
||||
default:
|
||||
debug("%s Invalid input clock selection [%d]\n",
|
||||
__func__, clk_id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret |= configure_aif_clock(priv, aif_id);
|
||||
|
||||
if (ret < 0) {
|
||||
debug("%s: codec register access error\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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(struct wm8994_priv *priv)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Unmute AIF2DAC */
|
||||
ret = wm8994_bic_or(priv, WM8994_AIF2_DAC_FILTERS_1,
|
||||
WM8994_AIF2DAC_MUTE_MASK, 0);
|
||||
|
||||
|
||||
ret |= wm8994_bic_or(priv, 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_RIGHT_VOLUME,
|
||||
WM8994_AIF2DAC_VU_MASK | WM8994_AIF2DACR_VOL_MASK,
|
||||
WM8994_AIF2DAC_VU | 0xff);
|
||||
|
||||
|
||||
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_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(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__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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(struct wm8994_priv *priv)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
/* Unmute AIF1DAC */
|
||||
ret |= wm8994_i2c_write(priv, WM8994_AIF1_DAC_FILTERS_1, 0x0000);
|
||||
|
||||
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_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(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__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Intialise wm8994 codec device
|
||||
*
|
||||
* @param priv wm8994 information
|
||||
*
|
||||
* @returns -1 for error and 0 Success.
|
||||
*/
|
||||
static int wm8994_device_init(struct wm8994_priv *priv)
|
||||
{
|
||||
const char *devname;
|
||||
unsigned short reg_data;
|
||||
int ret;
|
||||
|
||||
wm8994_i2c_write(priv, WM8994_SOFTWARE_RESET, WM8994_SW_RESET);
|
||||
|
||||
ret = wm8994_i2c_read(priv, WM8994_SOFTWARE_RESET, ®_data);
|
||||
if (ret < 0) {
|
||||
debug("Failed to read ID register\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (reg_data == WM8994_ID) {
|
||||
devname = "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);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
ret = wm8994_i2c_read(priv, WM8994_CHIP_REVISION, ®_data);
|
||||
if (ret < 0) {
|
||||
debug("Failed to read revision register: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
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_bic_or(priv, WM8994_POWER_MANAGEMENT_1,
|
||||
WM8994_VMID_SEL_MASK | WM8994_BIAS_ENA_MASK, 0x3);
|
||||
|
||||
/* Charge Pump Enable */
|
||||
ret |= wm8994_bic_or(priv, WM8994_CHARGE_PUMP_1, WM8994_CP_ENA_MASK,
|
||||
WM8994_CP_ENA);
|
||||
|
||||
/* Head Phone Power Enable */
|
||||
ret |= wm8994_bic_or(priv, WM8994_POWER_MANAGEMENT_1,
|
||||
WM8994_HPOUT1L_ENA_MASK, WM8994_HPOUT1L_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(priv, WM8994_POWER_MANAGEMENT_2,
|
||||
WM8994_TSHUT_ENA | WM8994_MIXINL_ENA |
|
||||
WM8994_MIXINR_ENA | WM8994_IN2L_ENA |
|
||||
WM8994_IN2R_ENA);
|
||||
|
||||
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(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_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_bic_or(priv, WM8994_ANALOGUE_HP_1,
|
||||
WM8994_HPOUT1L_DLY_MASK | WM8994_HPOUT1R_DLY_MASK,
|
||||
WM8994_HPOUT1L_DLY | WM8994_HPOUT1R_DLY);
|
||||
|
||||
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_bic_or(priv, WM8994_ANALOGUE_HP_1,
|
||||
WM8994_HPOUT1L_DLY_MASK |
|
||||
WM8994_HPOUT1R_DLY_MASK | WM8994_HPOUT1L_OUTP_MASK |
|
||||
WM8994_HPOUT1R_OUTP_MASK |
|
||||
WM8994_HPOUT1L_RMV_SHORT_MASK |
|
||||
WM8994_HPOUT1R_RMV_SHORT_MASK, WM8994_HPOUT1L_DLY |
|
||||
WM8994_HPOUT1R_DLY | WM8994_HPOUT1L_OUTP |
|
||||
WM8994_HPOUT1R_OUTP | WM8994_HPOUT1L_RMV_SHORT |
|
||||
WM8994_HPOUT1R_RMV_SHORT);
|
||||
|
||||
/* MIXER Config DAC1 to HP */
|
||||
ret |= wm8994_bic_or(priv, WM8994_OUTPUT_MIXER_1,
|
||||
WM8994_DAC1L_TO_HPOUT1L_MASK,
|
||||
WM8994_DAC1L_TO_HPOUT1L);
|
||||
|
||||
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(priv, WM8994_DAC1_LEFT_MIXER_ROUTING,
|
||||
WM8994_AIF1DAC1L_TO_DAC1L);
|
||||
|
||||
ret |= wm8994_i2c_write(priv, WM8994_DAC1_RIGHT_MIXER_ROUTING,
|
||||
WM8994_AIF1DAC1R_TO_DAC1R);
|
||||
|
||||
/* GPIO Settings for AIF1 */
|
||||
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(priv);
|
||||
} else if (aif_id == WM8994_AIF2) {
|
||||
/* Routing AIF2 to DAC1 */
|
||||
ret |= wm8994_bic_or(priv, WM8994_DAC1_LEFT_MIXER_ROUTING,
|
||||
WM8994_AIF2DACL_TO_DAC1L_MASK,
|
||||
WM8994_AIF2DACL_TO_DAC1L);
|
||||
|
||||
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_bic_or(priv, WM8994_GPIO_3, WM8994_GPIO_DIR_MASK |
|
||||
WM8994_GPIO_FUNCTION_MASK,
|
||||
WM8994_GPIO_DIR_OUTPUT);
|
||||
|
||||
/* LR CLK */
|
||||
ret |= wm8994_bic_or(priv, WM8994_GPIO_4, WM8994_GPIO_DIR_MASK |
|
||||
WM8994_GPIO_FUNCTION_MASK,
|
||||
WM8994_GPIO_DIR_OUTPUT);
|
||||
|
||||
/* DATA */
|
||||
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(priv);
|
||||
}
|
||||
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
debug("%s: Codec chip setup ok\n", __func__);
|
||||
return 0;
|
||||
err:
|
||||
debug("%s: Codec chip setup error\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
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 ret;
|
||||
|
||||
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(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(priv, aif_id, sampling_rate, bits_per_sample,
|
||||
channels);
|
||||
|
||||
if (ret == 0) {
|
||||
ret = wm8994_set_fmt(priv, aif_id, SND_SOC_DAIFMT_I2S |
|
||||
SND_SOC_DAIFMT_NB_NF |
|
||||
SND_SOC_DAIFMT_CBS_CFS);
|
||||
}
|
||||
|
||||
return 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),
|
||||
};
|
||||
@@ -0,0 +1,68 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* Copyright (C) 2012 Samsung Electronics
|
||||
* R. Chadrasekar <rcsekar@samsung.com>
|
||||
*/
|
||||
|
||||
#ifndef __WM8994_H__
|
||||
#define __WM8994_H__
|
||||
|
||||
/* Sources for AIF1/2 SYSCLK - use with set_dai_sysclk() */
|
||||
#define WM8994_SYSCLK_MCLK1 1
|
||||
#define WM8994_SYSCLK_MCLK2 2
|
||||
#define WM8994_SYSCLK_FLL1 3
|
||||
#define WM8994_SYSCLK_FLL2 4
|
||||
|
||||
/* Avilable audi interface ports in wm8994 codec */
|
||||
enum en_audio_interface {
|
||||
WM8994_AIF1,
|
||||
WM8994_AIF2,
|
||||
WM8994_AIF3
|
||||
};
|
||||
|
||||
/* OPCLK is also configured with set_dai_sysclk, specify division*10 as rate. */
|
||||
#define WM8994_SYSCLK_OPCLK 5
|
||||
|
||||
#define WM8994_FLL1 1
|
||||
#define WM8994_FLL2 2
|
||||
|
||||
#define WM8994_FLL_SRC_MCLK1 1
|
||||
#define WM8994_FLL_SRC_MCLK2 2
|
||||
#define WM8994_FLL_SRC_LRCLK 3
|
||||
#define WM8994_FLL_SRC_BCLK 4
|
||||
|
||||
/* maximum available digital interfac in the dac to configure */
|
||||
#define WM8994_MAX_AIF 2
|
||||
|
||||
#define WM8994_MAX_INPUT_CLK_FREQ 13500000
|
||||
#define WM8994_ID 0x8994
|
||||
|
||||
enum wm8994_vmid_mode {
|
||||
WM8994_VMID_NORMAL,
|
||||
WM8994_VMID_FORCE,
|
||||
};
|
||||
|
||||
/* wm 8994 family devices */
|
||||
enum wm8994_type {
|
||||
WM8994 = 0,
|
||||
WM8958 = 1,
|
||||
WM1811 = 2,
|
||||
};
|
||||
|
||||
/*
|
||||
* intialise wm8994 sound codec device for the given configuration
|
||||
*
|
||||
* @param blob FDT node for codec values
|
||||
* @param aif_id enum value of codec interface port in which
|
||||
* soc i2s is connected
|
||||
* @param sampling_rate Sampling rate ranges between from 8khz to 96khz
|
||||
* @param mclk_freq Master clock frequency.
|
||||
* @param bits_per_sample bits per Sample can be 16 or 24
|
||||
* @param channels Number of channnels, maximum 2
|
||||
*
|
||||
* @returns -1 for error and 0 Success.
|
||||
*/
|
||||
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);
|
||||
#endif /*__WM8994_H__ */
|
||||
@@ -0,0 +1,325 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* (C) Copyright 2012 Samsung Electronics
|
||||
*/
|
||||
|
||||
#ifndef __WM8994_REGISTERS_H__
|
||||
#define __WM8994_REGISTERS_H__
|
||||
|
||||
/*
|
||||
* Register values.
|
||||
*/
|
||||
#define WM8994_SOFTWARE_RESET 0x00
|
||||
#define WM8994_POWER_MANAGEMENT_1 0x01
|
||||
#define WM8994_POWER_MANAGEMENT_2 0x02
|
||||
#define WM8994_POWER_MANAGEMENT_4 0x04
|
||||
#define WM8994_POWER_MANAGEMENT_5 0x05
|
||||
#define WM8994_LEFT_OUTPUT_VOLUME 0x1C
|
||||
#define WM8994_RIGHT_OUTPUT_VOLUME 0x1D
|
||||
#define WM8994_OUTPUT_MIXER_1 0x2D
|
||||
#define WM8994_OUTPUT_MIXER_2 0x2E
|
||||
#define WM8994_CHARGE_PUMP_1 0x4C
|
||||
#define WM8994_DC_SERVO_1 0x54
|
||||
#define WM8994_ANALOGUE_HP_1 0x60
|
||||
#define WM8994_CHIP_REVISION 0x100
|
||||
#define WM8994_AIF1_CLOCKING_1 0x200
|
||||
#define WM8994_AIF1_CLOCKING_2 0x201
|
||||
#define WM8994_AIF2_CLOCKING_1 0x204
|
||||
#define WM8994_CLOCKING_1 0x208
|
||||
#define WM8994_CLOCKING_2 0x209
|
||||
#define WM8994_AIF1_RATE 0x210
|
||||
#define WM8994_AIF2_RATE 0x211
|
||||
#define WM8994_RATE_STATUS 0x212
|
||||
#define WM8994_AIF1_CONTROL_1 0x300
|
||||
#define WM8994_AIF1_CONTROL_2 0x301
|
||||
#define WM8994_AIF1_MASTER_SLAVE 0x302
|
||||
#define WM8994_AIF1_BCLK 0x303
|
||||
#define WM8994_AIF2_CONTROL_1 0x310
|
||||
#define WM8994_AIF2_CONTROL_2 0x311
|
||||
#define WM8994_AIF2_MASTER_SLAVE 0x312
|
||||
#define WM8994_AIF2_BCLK 0x313
|
||||
#define WM8994_AIF1_DAC_FILTERS_1 0x420
|
||||
#define WM8994_AIF2_DAC_LEFT_VOLUME 0x502
|
||||
#define WM8994_AIF2_DAC_RIGHT_VOLUME 0x503
|
||||
#define WM8994_AIF2_DAC_FILTERS_1 0x520
|
||||
#define WM8994_DAC1_LEFT_MIXER_ROUTING 0x601
|
||||
#define WM8994_DAC1_RIGHT_MIXER_ROUTING 0x602
|
||||
#define WM8994_DAC1_LEFT_VOLUME 0x610
|
||||
#define WM8994_DAC1_RIGHT_VOLUME 0x611
|
||||
#define WM8994_GPIO_1 0x700
|
||||
#define WM8994_GPIO_3 0x702
|
||||
#define WM8994_GPIO_4 0x703
|
||||
#define WM8994_GPIO_5 0x704
|
||||
|
||||
/*
|
||||
* Field Definitions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* R0 (0x00) - Software Reset
|
||||
*/
|
||||
/* SW_RESET */
|
||||
#define WM8994_SW_RESET 1
|
||||
/*
|
||||
* R1 (0x01) - Power Management (1)
|
||||
*/
|
||||
/* HPOUT1L_ENA */
|
||||
#define WM8994_HPOUT1L_ENA 0x0200
|
||||
/* HPOUT1L_ENA */
|
||||
#define WM8994_HPOUT1L_ENA_MASK 0x0200
|
||||
/* HPOUT1R_ENA */
|
||||
#define WM8994_HPOUT1R_ENA 0x0100
|
||||
/* HPOUT1R_ENA */
|
||||
#define WM8994_HPOUT1R_ENA_MASK 0x0100
|
||||
/* VMID_SEL - [2:1] */
|
||||
#define WM8994_VMID_SEL_MASK 0x0006
|
||||
/* BIAS_ENA */
|
||||
#define WM8994_BIAS_ENA 0x0001
|
||||
/* BIAS_ENA */
|
||||
#define WM8994_BIAS_ENA_MASK 0x0001
|
||||
|
||||
/*
|
||||
* R2 (0x02) - Power Management (2)
|
||||
*/
|
||||
/* OPCLK_ENA */
|
||||
#define WM8994_OPCLK_ENA 0x0800
|
||||
|
||||
#define WM8994_TSHUT_ENA 0x4000
|
||||
#define WM8994_MIXINL_ENA 0x0200
|
||||
#define WM8994_MIXINR_ENA 0x0100
|
||||
#define WM8994_IN2L_ENA 0x0080
|
||||
#define WM8994_IN2R_ENA 0x0020
|
||||
|
||||
/*
|
||||
* R5 (0x04) - Power Management (4)
|
||||
*/
|
||||
#define WM8994_ADCL_ENA 0x0001
|
||||
#define WM8994_ADCR_ENA 0x0002
|
||||
#define WM8994_AIF1ADC1R_ENA 0x0100
|
||||
#define WM8994_AIF1ADC1L_ENA 0x0200
|
||||
|
||||
/*
|
||||
* R5 (0x05) - Power Management (5)
|
||||
*/
|
||||
/* AIF2DACL_ENA */
|
||||
#define WM8994_AIF2DACL_ENA 0x2000
|
||||
#define WM8994_AIF2DACL_ENA_MASK 0x2000
|
||||
/* AIF2DACR_ENA */
|
||||
#define WM8994_AIF2DACR_ENA 0x1000
|
||||
#define WM8994_AIF2DACR_ENA_MASK 0x1000
|
||||
/* AIF1DACL_ENA */
|
||||
#define WM8994_AIF1DACL_ENA 0x0200
|
||||
#define WM8994_AIF1DACL_ENA_MASK 0x0200
|
||||
/* AIF1DACR_ENA */
|
||||
#define WM8994_AIF1DACR_ENA 0x0100
|
||||
#define WM8994_AIF1DACR_ENA_MASK 0x0100
|
||||
/* DAC1L_ENA */
|
||||
#define WM8994_DAC1L_ENA 0x0002
|
||||
#define WM8994_DAC1L_ENA_MASK 0x0002
|
||||
/* DAC1R_ENA */
|
||||
#define WM8994_DAC1R_ENA 0x0001
|
||||
#define WM8994_DAC1R_ENA_MASK 0x0001
|
||||
|
||||
/*
|
||||
* R45 (0x2D) - Output Mixer (1)
|
||||
*/
|
||||
/* DAC1L_TO_HPOUT1L */
|
||||
#define WM8994_DAC1L_TO_HPOUT1L 0x0100
|
||||
#define WM8994_DAC1L_TO_HPOUT1L_MASK 0x0100
|
||||
|
||||
/*
|
||||
* R46 (0x2E) - Output Mixer (2)
|
||||
*/
|
||||
/* DAC1R_TO_HPOUT1R */
|
||||
#define WM8994_DAC1R_TO_HPOUT1R 0x0100
|
||||
#define WM8994_DAC1R_TO_HPOUT1R_MASK 0x0100
|
||||
|
||||
/*
|
||||
* R76 (0x4C) - Charge Pump (1)
|
||||
*/
|
||||
/* CP_ENA */
|
||||
#define WM8994_CP_ENA 0x8000
|
||||
#define WM8994_CP_ENA_MASK 0x8000
|
||||
/*
|
||||
* R84 (0x54) - DC Servo (1)
|
||||
*/
|
||||
/* DCS_ENA_CHAN_1 */
|
||||
#define WM8994_DCS_ENA_CHAN_1 0x0002
|
||||
#define WM8994_DCS_ENA_CHAN_1_MASK 0x0002
|
||||
/* DCS_ENA_CHAN_0 */
|
||||
#define WM8994_DCS_ENA_CHAN_0 0x0001
|
||||
#define WM8994_DCS_ENA_CHAN_0_MASK 0x0001
|
||||
|
||||
/*
|
||||
* R96 (0x60) - Analogue HP (1)
|
||||
*/
|
||||
/* HPOUT1L_RMV_SHORT */
|
||||
#define WM8994_HPOUT1L_RMV_SHORT 0x0080
|
||||
#define WM8994_HPOUT1L_RMV_SHORT_MASK 0x0080
|
||||
/* HPOUT1L_OUTP */
|
||||
#define WM8994_HPOUT1L_OUTP 0x0040
|
||||
#define WM8994_HPOUT1L_OUTP_MASK 0x0040
|
||||
/* HPOUT1L_DLY */
|
||||
#define WM8994_HPOUT1L_DLY 0x0020
|
||||
#define WM8994_HPOUT1L_DLY_MASK 0x0020
|
||||
/* HPOUT1R_RMV_SHORT */
|
||||
#define WM8994_HPOUT1R_RMV_SHORT 0x0008
|
||||
#define WM8994_HPOUT1R_RMV_SHORT_MASK 0x0008
|
||||
/* HPOUT1R_OUTP */
|
||||
#define WM8994_HPOUT1R_OUTP 0x0004
|
||||
#define WM8994_HPOUT1R_OUTP_MASK 0x0004
|
||||
/* HPOUT1R_DLY */
|
||||
#define WM8994_HPOUT1R_DLY 0x0002
|
||||
#define WM8994_HPOUT1R_DLY_MASK 0x0002
|
||||
|
||||
/*
|
||||
* R512 (0x200) - AIF1 Clocking (1)
|
||||
*/
|
||||
/* AIF1CLK_SRC - [4:3] */
|
||||
#define WM8994_AIF1CLK_SRC_MASK 0x0018
|
||||
/* AIF1CLK_DIV */
|
||||
#define WM8994_AIF1CLK_DIV 0x0002
|
||||
/* AIF1CLK_ENA */
|
||||
#define WM8994_AIF1CLK_ENA 0x0001
|
||||
#define WM8994_AIF1CLK_ENA_MASK 0x0001
|
||||
|
||||
/*
|
||||
* R517 (0x205) - AIF2 Clocking (2)
|
||||
*/
|
||||
/* AIF2DAC_DIV - [5:3] */
|
||||
#define WM8994_AIF2DAC_DIV_MASK 0x0038
|
||||
|
||||
/*
|
||||
* R520 (0x208) - Clocking (1)
|
||||
*/
|
||||
/* AIF1DSPCLK_ENA */
|
||||
#define WM8994_AIF1DSPCLK_ENA 0x0008
|
||||
#define WM8994_AIF1DSPCLK_ENA_MASK 0x0008
|
||||
/* AIF2DSPCLK_ENA */
|
||||
#define WM8994_AIF2DSPCLK_ENA 0x0004
|
||||
#define WM8994_AIF2DSPCLK_ENA_MASK 0x0004
|
||||
/* SYSDSPCLK_ENA */
|
||||
#define WM8994_SYSDSPCLK_ENA 0x0002
|
||||
#define WM8994_SYSDSPCLK_ENA_MASK 0x0002
|
||||
/* SYSCLK_SRC */
|
||||
#define WM8994_SYSCLK_SRC 0x0001
|
||||
|
||||
/*
|
||||
* R521 (0x209) - Clocking (2)
|
||||
*/
|
||||
/* OPCLK_DIV - [2:0] */
|
||||
#define WM8994_OPCLK_DIV_MASK 0x0007
|
||||
|
||||
/*
|
||||
* R528 (0x210) - AIF1 Rate
|
||||
*/
|
||||
/* AIF1_SR - [7:4] */
|
||||
#define WM8994_AIF1_SR_MASK 0x00F0
|
||||
#define WM8994_AIF1_SR_SHIFT 4
|
||||
/* AIF1CLK_RATE - [3:0] */
|
||||
#define WM8994_AIF1CLK_RATE_MASK 0x000F
|
||||
|
||||
/*
|
||||
* R768 (0x300) - AIF1 Control (1)
|
||||
*/
|
||||
/* AIF1_BCLK_INV */
|
||||
#define WM8994_AIF1_BCLK_INV 0x0100
|
||||
/* AIF1_LRCLK_INV */
|
||||
#define WM8994_AIF1_LRCLK_INV 0x0080
|
||||
#define WM8994_AIF1_LRCLK_INV_MASK 0x0080
|
||||
/* AIF1_WL - [6:5] */
|
||||
#define WM8994_AIF1_WL_MASK 0x0060
|
||||
/* AIF1_FMT - [4:3] */
|
||||
#define WM8994_AIF1_FMT_MASK 0x0018
|
||||
|
||||
/*
|
||||
* R769 (0x301) - AIF1 Control (2)
|
||||
*/
|
||||
/* AIF1_MONO */
|
||||
#define WM8994_AIF1_MONO 0x0100
|
||||
|
||||
/*
|
||||
* R770 (0x302) - AIF1 Master/Slave
|
||||
*/
|
||||
/* AIF1_MSTR */
|
||||
#define WM8994_AIF1_MSTR 0x4000
|
||||
#define WM8994_AIF1_MSTR_MASK 0x4000
|
||||
|
||||
/*
|
||||
* R771 (0x303) - AIF1 BCLK
|
||||
*/
|
||||
/* AIF1_BCLK_DIV - [8:4] */
|
||||
#define WM8994_AIF1_BCLK_DIV_MASK 0x01F0
|
||||
#define WM8994_AIF1_BCLK_DIV_SHIFT 4
|
||||
|
||||
/*
|
||||
* R1282 (0x502) - AIF2 DAC Left Volume
|
||||
*/
|
||||
/* AIF2DAC_VU */
|
||||
#define WM8994_AIF2DAC_VU 0x0100
|
||||
#define WM8994_AIF2DAC_VU_MASK 0x0100
|
||||
/* AIF2DACL_VOL - [7:0] */
|
||||
#define WM8994_AIF2DACL_VOL_MASK 0x00FF
|
||||
|
||||
/*
|
||||
* R1283 (0x503) - AIF2 DAC Right Volume
|
||||
*/
|
||||
/* AIF2DACR_VOL - [7:0] */
|
||||
#define WM8994_AIF2DACR_VOL_MASK 0x00FF
|
||||
|
||||
/*
|
||||
* R1312 (0x520) - AIF2 DAC Filters (1)
|
||||
*/
|
||||
/* AIF2DAC_MUTE */
|
||||
#define WM8994_AIF2DAC_MUTE_MASK 0x0200
|
||||
|
||||
/*
|
||||
* R1537 (0x601) - DAC1 Left Mixer Routing
|
||||
*/
|
||||
/* AIF2DACL_TO_DAC1L */
|
||||
#define WM8994_AIF2DACL_TO_DAC1L 0x0004
|
||||
#define WM8994_AIF2DACL_TO_DAC1L_MASK 0x0004
|
||||
/* AIF1DAC1L_TO_DAC1L */
|
||||
#define WM8994_AIF1DAC1L_TO_DAC1L 0x0001
|
||||
|
||||
/*
|
||||
* R1538 (0x602) - DAC1 Right Mixer Routing
|
||||
*/
|
||||
/* AIF2DACR_TO_DAC1R */
|
||||
#define WM8994_AIF2DACR_TO_DAC1R 0x0004
|
||||
#define WM8994_AIF2DACR_TO_DAC1R_MASK 0x0004
|
||||
/* AIF1DAC1R_TO_DAC1R */
|
||||
#define WM8994_AIF1DAC1R_TO_DAC1R 0x0001
|
||||
|
||||
/*
|
||||
* R1552 (0x610) - DAC1 Left Volume
|
||||
*/
|
||||
/* DAC1L_MUTE */
|
||||
#define WM8994_DAC1L_MUTE_MASK 0x0200
|
||||
/* DAC1_VU */
|
||||
#define WM8994_DAC1_VU 0x0100
|
||||
#define WM8994_DAC1_VU_MASK 0x0100
|
||||
/* DAC1L_VOL - [7:0] */
|
||||
#define WM8994_DAC1L_VOL_MASK 0x00FF
|
||||
|
||||
/*
|
||||
* R1553 (0x611) - DAC1 Right Volume
|
||||
*/
|
||||
/* DAC1R_MUTE */
|
||||
#define WM8994_DAC1R_MUTE_MASK 0x0200
|
||||
/* DAC1R_VOL - [7:0] */
|
||||
#define WM8994_DAC1R_VOL_MASK 0x00FF
|
||||
|
||||
/*
|
||||
* GPIO
|
||||
*/
|
||||
/* OUTPUT PIN */
|
||||
#define WM8994_GPIO_DIR_OUTPUT 0x8000
|
||||
/* GPIO PIN MASK */
|
||||
#define WM8994_GPIO_DIR_MASK 0xFFE0
|
||||
/* I2S CLK */
|
||||
#define WM8994_GPIO_FUNCTION_I2S_CLK 0x0001
|
||||
#define WM8994_GPIO_INPUT_DEBOUNCE 0x0100
|
||||
/* GPn FN */
|
||||
#define WM8994_GPIO_FUNCTION_MASK 0x001F
|
||||
#endif
|
||||
Reference in New Issue
Block a user