I2S is used to send digital audio data to an audio codec. Add support for
this on broadwell.
Signed-off-by: Simon Glass
---
drivers/sound/Makefile| 1 +
drivers/sound/broadwell_i2s.c | 306 ++
drivers/sound/broadwell_i2s.h | 301 +
3 files changed, 608 insertions(+)
create mode 100644 drivers/sound/broadwell_i2s.c
create mode 100644 drivers/sound/broadwell_i2s.h
diff --git a/drivers/sound/Makefile b/drivers/sound/Makefile
index f01fb2f739..102b3bca1a 100644
--- a/drivers/sound/Makefile
+++ b/drivers/sound/Makefile
@@ -16,4 +16,5 @@ 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_INTEL_BROADWELL) += broadwell_i2s.o
obj-$(CONFIG_SOUND_IVYBRIDGE) += ivybridge_sound.o
diff --git a/drivers/sound/broadwell_i2s.c b/drivers/sound/broadwell_i2s.c
new file mode 100644
index 00..a03bb5e4dc
--- /dev/null
+++ b/drivers/sound/broadwell_i2s.c
@@ -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
+#include
+#include
+#include
+#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)
+{
+ uint32_t 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)
+{
+ uint32_t 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)
+{
+ uint32_t 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,