Merge git://git.denx.de/u-boot-x86

- Add support for sound.

Albeit the big changeset, changes are pretty limited to x86 only and a
few new sound drivers used by x86 so I think it would be good to have
this in the next release.
This commit is contained in:
Tom Rini
2019-02-20 12:28:40 -05:00
60 changed files with 4623 additions and 101 deletions

View File

@@ -64,6 +64,8 @@ source "drivers/nvme/Kconfig"
source "drivers/pci/Kconfig"
source "drivers/pch/Kconfig"
source "drivers/pcmcia/Kconfig"
source "drivers/phy/Kconfig"

View File

@@ -86,7 +86,8 @@ obj-y += misc/
obj-$(CONFIG_MMC) += mmc/
obj-$(CONFIG_NVME) += nvme/
obj-y += pcmcia/
obj-$(CONFIG_X86) += pch/
obj-y += dfu/
obj-$(CONFIG_PCH) += pch/
obj-y += phy/allwinner/
obj-y += phy/marvell/
obj-y += rtc/

View File

@@ -37,6 +37,10 @@ static int syscon_pre_probe(struct udevice *dev)
{
struct syscon_uc_info *priv = dev_get_uclass_priv(dev);
/* Special case for PCI devices, which don't have a regmap */
if (device_get_uclass_id(dev->parent) == UCLASS_PCI)
return 0;
/*
* With OF_PLATDATA we really have no way of knowing the format of
* the device-specific platform data. So we assume that it starts with

View File

@@ -38,14 +38,16 @@ struct dw_i2c {
};
#ifdef CONFIG_SYS_I2C_DW_ENABLE_STATUS_UNSUPPORTED
static void dw_i2c_enable(struct i2c_regs *i2c_base, bool enable)
static int dw_i2c_enable(struct i2c_regs *i2c_base, bool enable)
{
u32 ena = enable ? IC_ENABLE_0B : 0;
writel(ena, &i2c_base->ic_enable);
return 0;
}
#else
static void dw_i2c_enable(struct i2c_regs *i2c_base, bool enable)
static int dw_i2c_enable(struct i2c_regs *i2c_base, bool enable)
{
u32 ena = enable ? IC_ENABLE_0B : 0;
int timeout = 100;
@@ -53,7 +55,7 @@ static void dw_i2c_enable(struct i2c_regs *i2c_base, bool enable)
do {
writel(ena, &i2c_base->ic_enable);
if ((readl(&i2c_base->ic_enable_status) & IC_ENABLE_0B) == ena)
return;
return 0;
/*
* Wait 10 times the signaling period of the highest I2C
@@ -62,8 +64,9 @@ static void dw_i2c_enable(struct i2c_regs *i2c_base, bool enable)
*/
udelay(25);
} while (timeout--);
printf("timeout in %sabling I2C adapter\n", enable ? "en" : "dis");
return -ETIMEDOUT;
}
#endif
@@ -370,10 +373,14 @@ static int __dw_i2c_write(struct i2c_regs *i2c_base, u8 dev, uint addr,
*
* Initialization function.
*/
static void __dw_i2c_init(struct i2c_regs *i2c_base, int speed, int slaveaddr)
static int __dw_i2c_init(struct i2c_regs *i2c_base, int speed, int slaveaddr)
{
int ret;
/* Disable i2c */
dw_i2c_enable(i2c_base, false);
ret = dw_i2c_enable(i2c_base, false);
if (ret)
return ret;
writel(IC_CON_SD | IC_CON_RE | IC_CON_SPD_FS | IC_CON_MM,
&i2c_base->ic_con);
@@ -386,7 +393,11 @@ static void __dw_i2c_init(struct i2c_regs *i2c_base, int speed, int slaveaddr)
#endif
/* Enable i2c */
dw_i2c_enable(i2c_base, true);
ret = dw_i2c_enable(i2c_base, true);
if (ret)
return ret;
return 0;
}
#ifndef CONFIG_DM_I2C
@@ -558,9 +569,7 @@ static int designware_i2c_probe(struct udevice *bus)
if (&priv->reset_ctl)
reset_deassert(&priv->reset_ctl);
__dw_i2c_init(priv->regs, 0, 0);
return 0;
return __dw_i2c_init(priv->regs, 0, 0);
}
static int designware_i2c_bind(struct udevice *dev)

9
drivers/pch/Kconfig Normal file
View File

@@ -0,0 +1,9 @@
config PCH
bool "Enable Platform-controller Hub (PCH) support"
depends on X86 || SANDBOX
help
Most x86 chips include a PCH which is responsible for handling
parts of the system not handled by that CPU. It supersedes the
northbridge / southbridge architecture that was previously used. The
PCH allows for higher performance since the memory functions are
handled in the CPU.

View File

@@ -3,3 +3,4 @@
obj-y += pch-uclass.o
obj-y += pch7.o
obj-y += pch9.o
obj-$(CONFIG_SANDBOX) += sandbox_pch.o

View File

@@ -51,6 +51,16 @@ int pch_get_io_base(struct udevice *dev, u32 *iobasep)
return ops->get_io_base(dev, iobasep);
}
int pch_ioctl(struct udevice *dev, ulong req, void *data, int size)
{
struct pch_ops *ops = pch_get_ops(dev);
if (!ops->ioctl)
return -ENOSYS;
return ops->ioctl(dev, req, data, size);
}
UCLASS_DRIVER(pch) = {
.id = UCLASS_PCH,
.name = "pch",

86
drivers/pch/sandbox_pch.c Normal file
View File

@@ -0,0 +1,86 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2018 Google LLC
*/
#include <common.h>
#include <dm.h>
#include <pch.h>
struct sandbox_pch_priv {
bool protect;
};
int sandbox_get_pch_spi_protect(struct udevice *dev)
{
struct sandbox_pch_priv *priv = dev_get_priv(dev);
return priv->protect;
}
static int sandbox_pch_get_spi_base(struct udevice *dev, ulong *sbasep)
{
*sbasep = 0x10;
return 0;
}
static int sandbox_pch_set_spi_protect(struct udevice *dev, bool protect)
{
struct sandbox_pch_priv *priv = dev_get_priv(dev);
priv->protect = protect;
return 0;
}
static int sandbox_pch_get_gpio_base(struct udevice *dev, u32 *gbasep)
{
*gbasep = 0x20;
return 0;
}
static int sandbox_pch_get_io_base(struct udevice *dev, u32 *iobasep)
{
*iobasep = 0x30;
return 0;
}
int sandbox_pch_ioctl(struct udevice *dev, enum pch_req_t req, void *data,
int size)
{
switch (req) {
case PCH_REQ_TEST1:
return -ENOSYS;
case PCH_REQ_TEST2:
return *(char *)data;
case PCH_REQ_TEST3:
*(char *)data = 'x';
return 1;
default:
return -ENOSYS;
}
}
static const struct pch_ops sandbox_pch_ops = {
.get_spi_base = sandbox_pch_get_spi_base,
.set_spi_protect = sandbox_pch_set_spi_protect,
.get_gpio_base = sandbox_pch_get_gpio_base,
.get_io_base = sandbox_pch_get_io_base,
.ioctl = sandbox_pch_ioctl,
};
static const struct udevice_id sandbox_pch_ids[] = {
{ .compatible = "sandbox,pch" },
{ }
};
U_BOOT_DRIVER(sandbox_pch_drv) = {
.name = "sandbox-pch",
.id = UCLASS_PCH,
.of_match = sandbox_pch_ids,
.ops = &sandbox_pch_ops,
.priv_auto_alloc_size = sizeof(struct sandbox_pch_priv),
};

View File

@@ -184,11 +184,8 @@ pci_dev_t pci_find_devices(struct pci_device_id *ids, int index)
return -1;
}
int pci_hose_config_device(struct pci_controller *hose,
pci_dev_t dev,
unsigned long io,
pci_addr_t mem,
unsigned long command)
static int pci_hose_config_device(struct pci_controller *hose, pci_dev_t dev,
ulong io, pci_addr_t mem, ulong command)
{
u32 bar_response;
unsigned int old_command;

View File

@@ -40,6 +40,37 @@ config I2S_SAMSUNG
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 SOUND_MAX98088
bool "Support Maxim max98088 audio codec"
depends on I2S
@@ -64,6 +95,15 @@ config SOUND_MAX98095
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

View File

@@ -15,3 +15,8 @@ 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

View File

@@ -0,0 +1,306 @@
// 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 <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),
};

View File

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

View File

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

556
drivers/sound/hda_codec.c Normal file
View File

@@ -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(&regs->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(&regs->icii, HDA_ICII_BUSY | HDA_ICII_VALID);
/* Use a 1msec timeout */
while (timeout--) {
reg32 = readl(&regs->icii);
if ((reg32 & (HDA_ICII_VALID | HDA_ICII_BUSY)) ==
HDA_ICII_VALID) {
if (response)
*response = readl(&regs->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(&regs->gctl, 1, 1))
goto no_codec;
/* Write back the value once reset bit is set */
writew(readw(&regs->gcap), &regs->gcap);
/* Read in Codec location */
reg8 = readb(&regs->statests) & 0xf;
if (!reg8)
goto no_codec;
return reg8;
no_codec:
/* Codec Not found - put HDA back in reset */
set_bits(&regs->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]), &regs->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, &regs->cmd);
ret = hda_wait_for_valid(regs);
if (ret) {
log_debug(" codec not valid\n");
return ret;
}
id = readl(&regs->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, &regs->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, &current_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", &current_nid);
if (!ret)
return current_nid;
ret = get_subnode_info(priv->regs, HDA_ROOT_NODE, &node_count,
&current_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);

View File

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

View File

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

334
drivers/sound/rt5677.c Normal file
View File

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

1428
drivers/sound/rt5677.h Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -3,6 +3,8 @@
* Copyright (c) 2013 Google, Inc
*/
#define LOG_CATEGORY UCLASS_SOUND
#include <common.h>
#include <audio_codec.h>
#include <dm.h>
@@ -20,11 +22,14 @@ struct sandbox_codec_priv {
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 */
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,
@@ -61,6 +66,20 @@ int sandbox_get_sound_sum(struct udevice *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)
@@ -85,12 +104,21 @@ static int sandbox_i2s_tx_data(struct udevice *dev, void *data,
for (i = 0; i < data_size; i++)
priv->sum += ((uint8_t *)data)[i];
return sandbox_sdl_sound_play(data, data_size);
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;
@@ -101,8 +129,15 @@ static int sandbox_i2s_probe(struct udevice *dev)
uc_priv->channels = 2;
uc_priv->id = 1;
/* Ignore any error here - we'll just have no sound */
sandbox_sdl_sound_init(uc_priv->samplingrate, uc_priv->channels);
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;
}
@@ -128,6 +163,28 @@ static int sandbox_sound_play(struct udevice *dev, void *data, uint data_size)
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);
@@ -169,8 +226,10 @@ U_BOOT_DRIVER(sandbox_i2s) = {
};
static const struct sound_ops sandbox_sound_ops = {
.setup = sandbox_sound_setup,
.play = sandbox_sound_play,
.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[] = {

View File

@@ -16,7 +16,7 @@ int sound_setup(struct udevice *dev)
struct sound_ops *ops = sound_get_ops(dev);
if (!ops->setup)
return -ENOSYS;
return 0;
return ops->setup(dev);
}
@@ -31,10 +31,30 @@ int sound_play(struct udevice *dev, void *data, uint data_size)
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 = dev_get_uclass_priv(uc_priv->i2s);
struct i2s_uc_priv *i2s_uc_priv;
unsigned short *data;
uint data_size;
int ret;
@@ -43,7 +63,19 @@ int sound_beep(struct udevice *dev, int msecs, int frequency_hz)
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);