summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHarsha Priya <harshapriya.n@intel.com>2016-11-08 04:44:18 (GMT)
committerchrome-bot <chrome-bot@chromium.org>2016-11-30 19:04:43 (GMT)
commiteef5ce9e780339ec336e834da2590b2910af8549 (patch)
treebc476149d28e160e5c582f1330037bf01f011b4c
parentcdcf79ecf48ac5b62534e5cf3f884934546f5747 (diff)
downloaddepthcharge-firmware-servo-9040.B.tar.gz
depthcharge-firmware-servo-9040.B.tar.xz
drivers/bus/i2s/apollolake: Enable boot beep for Apollolakefirmware-servo-9040.B
This adds the drivers and functions to generate a boot beep when system enters dev mode. BUG=chrome-os-partner:54741 BRANCH=none TEST=Enter Dev mode and wait for a beep Change-Id: I906722addf1804d83990c1b91cb1cb04d1619be8 Signed-off-by: Harsha Priya <HarshaPriya.n@intel.com> Reviewed-on: https://chromium-review.googlesource.com/408654 Commit-Ready: Sathyanarayana Nujella <sathyanarayana.nujella@intel.com> Tested-by: Sathyanarayana Nujella <sathyanarayana.nujella@intel.com> Reviewed-by: Sathyanarayana Nujella <sathyanarayana.nujella@intel.com> Reviewed-by: Aaron Durbin <adurbin@chromium.org>
-rw-r--r--board/reef/defconfig8
-rw-r--r--src/board/reef/board.c25
-rw-r--r--src/drivers/bus/i2s/Kconfig1
-rw-r--r--src/drivers/bus/i2s/Makefile.inc2
-rw-r--r--src/drivers/bus/i2s/apollolake/Kconfig21
-rw-r--r--src/drivers/bus/i2s/apollolake/Makefile.inc16
-rw-r--r--src/drivers/bus/i2s/apollolake/apollolake-max98357a.c39
-rw-r--r--src/drivers/bus/i2s/apollolake/apollolake-max98357a.h26
-rw-r--r--src/drivers/bus/i2s/apollolake/apollolake-regs.h214
-rw-r--r--src/drivers/bus/i2s/apollolake/apollolake.c333
-rw-r--r--src/drivers/bus/i2s/apollolake/apollolake.h129
11 files changed, 812 insertions, 2 deletions
diff --git a/board/reef/defconfig b/board/reef/defconfig
index a904d4c..dcb6e81 100644
--- a/board/reef/defconfig
+++ b/board/reef/defconfig
@@ -32,3 +32,11 @@ CONFIG_DRIVER_STORAGE_MMC=y
CONFIG_DRIVER_STORAGE_SDHCI_PCI=y
CONFIG_DRIVER_SOC_APOLLOLAKE=y
CONFIG_DRIVER_TPM_CR50_I2C=y
+
+#Audio
+
+CONFIG_DRIVER_BUS_I2S_APOLLOLAKE=y
+CONFIG_DRIVER_BUS_I2S_APOLLOLAKE_MAX98357A=y
+CONFIG_DRIVER_SOUND_I2S=y
+CONFIG_DRIVER_SOUND_MAX98357A=y
+CONFIG_DRIVER_SOUND_ROUTE=y
diff --git a/src/board/reef/board.c b/src/board/reef/board.c
index 9e2ffaf..f113dbf 100644
--- a/src/board/reef/board.c
+++ b/src/board/reef/board.c
@@ -34,15 +34,25 @@
#include "drivers/power/pch.h"
#include "drivers/storage/sdhci.h"
+#include "drivers/sound/i2s.h"
+#include "drivers/sound/max98357a.h"
+#include "drivers/gpio/apollolake.h"
+#include "drivers/gpio/gpio.h"
+#include "drivers/bus/i2s/apollolake/apollolake-max98357a.h"
+
#define EMMC_SD_CLOCK_MIN 400000
#define EMMC_CLOCK_MAX 200000000
#define SD_CLOCK_MAX 52000000
-#define SPIBAR_BIOS_BFPREG (0x0)
+#define SPIBAR_BIOS_BFPREG (0x0)
+#define SPIBAR_BIOS_BFPREG (0x0)
#define BFPREG_BASE_MASK (0x7fff)
#define BFPREG_LIMIT_SHIFT (16)
#define BFPREG_LIMIT_MASK (0x7fff << BFPREG_LIMIT_SHIFT)
+#define AUD_VOLUME 4000
+#define SDMODE_PIN GPIO_76
+
static void board_flash_init(void)
{
uintptr_t mmio_base = pci_read_config32(PCI_DEV(0, 0xd, 2),
@@ -116,6 +126,19 @@ static int board_setup(void)
/* PCH Power */
power_set_ops(&apollolake_power_ops);
+ /* Audio Setup (for boot beep) */
+ GpioOps *sdmode = &new_apollolake_gpio_output(SDMODE_PIN, 0)->ops;
+
+ AplI2s *i2s = new_apl_i2s(&apollolake_max98357a_settings, 16, sdmode);
+ I2sSource *i2s_source = new_i2s_source(&i2s->ops, 48000, 2, AUD_VOLUME);
+ /* Connect the Codec to the I2S source */
+ SoundRoute *sound_route = new_sound_route(&i2s_source->ops);
+ max98357aCodec *speaker_amp = new_max98357a_codec(sdmode);
+
+ list_insert_after(&speaker_amp->component.list_node,
+ &sound_route->components);
+ sound_set_ops(&sound_route->ops);
+
return 0;
}
diff --git a/src/drivers/bus/i2s/Kconfig b/src/drivers/bus/i2s/Kconfig
index 2f69bea..4103d09 100644
--- a/src/drivers/bus/i2s/Kconfig
+++ b/src/drivers/bus/i2s/Kconfig
@@ -26,6 +26,7 @@ config DRIVER_BUS_I2S_ROCKCHIP
bool "Rockchip I2S driver"
default n
+source src/drivers/bus/i2s/apollolake/Kconfig
source src/drivers/bus/i2s/baytrail/Kconfig
source src/drivers/bus/i2s/broadwell/Kconfig
source src/drivers/bus/i2s/braswell/Kconfig
diff --git a/src/drivers/bus/i2s/Makefile.inc b/src/drivers/bus/i2s/Makefile.inc
index e820e04..82f0472 100644
--- a/src/drivers/bus/i2s/Makefile.inc
+++ b/src/drivers/bus/i2s/Makefile.inc
@@ -11,7 +11,7 @@
## GNU General Public License for more details.
##
-subdirs-y += baytrail broadwell braswell
+subdirs-y += apollolake baytrail broadwell braswell
depthcharge-$(CONFIG_DRIVER_BUS_I2S_EXYNOS5) += exynos5.c
depthcharge-$(CONFIG_DRIVER_BUS_I2S_MT8173) += mt8173.c
diff --git a/src/drivers/bus/i2s/apollolake/Kconfig b/src/drivers/bus/i2s/apollolake/Kconfig
new file mode 100644
index 0000000..927eb8d
--- /dev/null
+++ b/src/drivers/bus/i2s/apollolake/Kconfig
@@ -0,0 +1,21 @@
+##
+## Copyright (c) 2016, Intel Corporation.
+## Copyright 2016 Google Inc. All rights reserved.
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; version 2 of the License.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+
+config DRIVER_BUS_I2S_APOLLOLAKE
+ bool "Apollolake I2S driver"
+ default n
+
+config DRIVER_BUS_I2S_APOLLOLAKE_MAX98357A
+ bool "Apollolake/Max98357a I2S driver"
+ depends on DRIVER_BUS_I2S_APOLLOLAKE
+ default n
diff --git a/src/drivers/bus/i2s/apollolake/Makefile.inc b/src/drivers/bus/i2s/apollolake/Makefile.inc
new file mode 100644
index 0000000..194cf5c
--- /dev/null
+++ b/src/drivers/bus/i2s/apollolake/Makefile.inc
@@ -0,0 +1,16 @@
+##
+## Copyright (c) 2016, Intel Corporation.
+## Copyright 2016 Google Inc.
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; version 2 of the License.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+
+depthcharge-$(CONFIG_DRIVER_BUS_I2S_APOLLOLAKE) += apollolake.c
+depthcharge-$(CONFIG_DRIVER_BUS_I2S_APOLLOLAKE_MAX98357A) += apollolake-max98357a.c
diff --git a/src/drivers/bus/i2s/apollolake/apollolake-max98357a.c b/src/drivers/bus/i2s/apollolake/apollolake-max98357a.c
new file mode 100644
index 0000000..a4fb43b
--- /dev/null
+++ b/src/drivers/bus/i2s/apollolake/apollolake-max98357a.c
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2016 Google Inc.
+ * Copyright (c) 2016, Intel Corporation.
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but without any warranty; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "drivers/bus/i2s/apollolake/apollolake-max98357a.h"
+
+/* Apollolake I2S controller settings for MAX98357a codec. */
+const AplI2sSettings apollolake_max98357a_settings = {
+ .mode = SSP_IN_NETWORK_MODE,
+ /* To set MOD bit in SSC0 - definig as network/normal mode */
+ .frame_rate_divider_ctrl = FRAME_RATE_CONTROL_STEREO,
+ /* To set FRDC bit in SSC0 - timeslot per frame in network mode */
+ .ssp_psp_T4 = 2,
+ /* To set EDMYSTOP bit in SSPSP - number of SCLK cycles after data */
+ .ssp_psp_T6 = 0x18,
+ /* To set SFRMWDTH bit in SSPSP - frame width */
+ .ssp_active_tx_slots_map = 3,
+ /* To set TTSA bit n SSTSA - data transmit timeslot */
+ .ssp_active_rx_slots_map = 3,
+ /* To set RTSA bit n SSRSA - data receive timeslot */
+ .m_value = 0x64,
+ /* I2S Divider M value */
+ .n_value = 0x18,
+ /* I2S Divider N value */
+};
diff --git a/src/drivers/bus/i2s/apollolake/apollolake-max98357a.h b/src/drivers/bus/i2s/apollolake/apollolake-max98357a.h
new file mode 100644
index 0000000..b642577
--- /dev/null
+++ b/src/drivers/bus/i2s/apollolake/apollolake-max98357a.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2015 Google Inc.
+ * Copyright (c) 2016, Intel Corporation.
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but without any warranty; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __DRIVERS_BUS_I2S_APOLLOLAKE_MAX98357A_H__
+#define __DRIVERS_BUS_I2S_APOLLOLAKE_MAX98357A_H__
+
+#include "drivers/bus/i2s/apollolake/apollolake.h"
+
+extern const AplI2sSettings apollolake_max98357a_settings;
+
+#endif
diff --git a/src/drivers/bus/i2s/apollolake/apollolake-regs.h b/src/drivers/bus/i2s/apollolake/apollolake-regs.h
new file mode 100644
index 0000000..4e5cf95
--- /dev/null
+++ b/src/drivers/bus/i2s/apollolake/apollolake-regs.h
@@ -0,0 +1,214 @@
+/*
+ * Copyright (c) 2016, Intel Corporation.
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without evenp the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+
+#ifndef __DRIVERS_BUS_I2S_APOLLOLAKE_APOLLOLAKE_REGS_H__
+#define __DRIVERS_BUS_I2S_APOLLOLAKE_APOLLOLAKE_REGS_H__
+
+#include <libpayload.h>
+
+/* FIFO over/under-run interrupt config. */
+enum {
+ SSP_FIFO_INT_ENABLE = 0x0,
+ SSP_FIFO_INT_DISABLE,
+};
+
+/* Frame format. */
+enum {
+ MOTOROLA_SPI_FORMAT = 0x0,
+ TI_SSP_FORMAT,
+ MICROWIRE_FORMAT,
+ PSP_FORMAT,
+};
+
+enum {
+ APL_SSP5_START_ADDRESS = 0x3000,
+ APL_SSP5_SHIM_START_ADDRESS = 0x800,
+};
+
+#define DEFINE_REG(reg, offset) \
+ const uint8_t OFFSET_ ## reg = offset; \
+ static inline uint32_t read_ ## reg(void *p) \
+ { return readl(p + (OFFSET_ ## reg)); } \
+ static inline void write_ ## reg(uint32_t v, void *p) \
+ { writel(v, p + (OFFSET_ ## reg)); }
+
+#define DEFINE_FIELD(reg, field, mask, shift) \
+ const uint32_t reg ## _ ## field ## _MASK = (uint32_t)(mask); \
+ const uint8_t reg ## _ ## field ## _SHIFT = (uint8_t)(shift); \
+ \
+ static inline uint32_t extract_ ## reg ## _ ## field \
+ (uint32_t reg_value) { \
+ return ((reg_value) >> reg ## _ ## field ## _SHIFT) \
+ & reg ## _ ## field ## _MASK; \
+ } \
+ \
+ static inline uint32_t replace_ ## reg ## _ ## field \
+ (uint32_t reg_value, uint32_t field_value) { \
+ return (((field_value) & reg ## _ ## field ## _MASK) \
+ << reg ## _ ## field ## _SHIFT) \
+ | ((reg_value) & ~(reg ## _ ## field ## _MASK \
+ << reg ## _ ## field ## _SHIFT)); \
+ } \
+ \
+ static inline uint32_t set_ ## reg ## _ ## field \
+ (uint32_t reg_value) { \
+ return ((reg ## _ ## field ## _MASK) \
+ << reg ## _ ## field ## _SHIFT) \
+ | ((reg_value) & ~(reg ## _ ## field ## _MASK \
+ << reg ## _ ## field ## _SHIFT)); \
+ }
+
+/* SSP registers definitions. */
+DEFINE_REG(SSCR0, 0x00)
+DEFINE_REG(SSCR1, 0x04)
+DEFINE_REG(SSSR, 0x08)
+DEFINE_REG(SSTO, 0x28)
+DEFINE_REG(SSPSP, 0x2C)
+DEFINE_REG(SSTSA, 0x30)
+DEFINE_REG(SSRSA, 0x34)
+DEFINE_REG(SSCR2, 0x40)
+DEFINE_REG(SSPSP2, 0x44)
+DEFINE_REG(SSCR3, 0x48)
+DEFINE_REG(SSIOC, 0x4C)
+
+/* SSP SSCR0 field definitions. */
+DEFINE_FIELD(SSCR0, DSS, 0x0F, 0) /* Data Size Select [4..16] */
+DEFINE_FIELD(SSCR0, FRF, 0x03, 4) /* Frame Format */
+DEFINE_FIELD(SSCR0, ECS, 0x01, 6) /* External clock select */
+DEFINE_FIELD(SSCR0, SSE, 0x01, 7) /* Synchronous Serial Port Enable */
+DEFINE_FIELD(SSCR0, SCR, 0xFFF, 8) /* Not implemented */
+DEFINE_FIELD(SSCR0, EDSS, 0x1, 20) /* Extended data size select */
+DEFINE_FIELD(SSCR0, NCS, 0x1, 21) /* Network clock select */
+DEFINE_FIELD(SSCR0, RIM, 0x1, 22) /* Receive FIFO overrrun int mask */
+DEFINE_FIELD(SSCR0, TIM, 0x1, 23) /* Transmit FIFO underrun int mask */
+DEFINE_FIELD(SSCR0, FRDC, 0x7, 24) /* Frame Rate Divider Control */
+DEFINE_FIELD(SSCR0, MOD, 0x1, 31) /* Mode (normal or network) */
+
+#define SSCR0_DataSize(x) ((x) - 1) /* Data Size Select [4..16] */
+#define SSCR0_SlotsPerFrm(x) ((x) - 1) /* Time slots per frame */
+
+/* SSP SSCR1 fields definitions */
+DEFINE_FIELD(SSCR1, TTELP, 0x1, 31) /* TXD Tristate Enable on Last Phase */
+DEFINE_FIELD(SSCR1, TTE, 0x1, 30) /* TXD Tristate Enable */
+DEFINE_FIELD(SSCR1, TRAIL, 0x1, 22) /* Trailing Byte */
+DEFINE_FIELD(SSCR1, TSRE, 0x1, 21) /* DMA Tx Service Request Enable */
+DEFINE_FIELD(SSCR1, RSRE, 0x1, 20) /* DMA Rx Service Request Enable */
+
+/* SSP SSCR2 fields definitions */
+DEFINE_FIELD(SSCR2, SDFD, 0x1, 14)
+DEFINE_FIELD(SSCR2, TURM1, 0x1, 1)
+
+/* SSP SSIOC fields definitions */
+DEFINE_FIELD(SSIOC, SFCR, 0x1, 4) /* SFCR SSP Force Clock Running */
+DEFINE_FIELD(SSIOC, SCOE, 0x1, 5) /* SCOE SSP Clock Output Enable */
+
+/* SSP SSPSP fields definitions */
+DEFINE_FIELD(SSPSP, FSRT, 0x1, 25)
+DEFINE_FIELD(SSPSP, EDMYSTOP, 0x7, 26)
+DEFINE_FIELD(SSPSP, SFRMWDTH, 0x3F, 16)
+
+/* SSP SSTSA fields definitions */
+DEFINE_FIELD(SSTSA, TXEN, 0x1, 8)
+DEFINE_FIELD(SSTSA, TTSA, 0xFF, 0)
+
+/* SSP SSRSA fields definitions */
+DEFINE_FIELD(SSRSA, RTSA, 0xFF, 0)
+
+#define SSCR0_reg(regbit, value) \
+ (((value) & SSCR0_##regbit##_MASK) << SSCR0_##regbit##_SHIFT)
+
+#define SSCR1_reg(regbit, value) \
+ (((value) & SSCR1_##regbit##_MASK) << SSCR1_##regbit##_SHIFT)
+
+#define SSCR2_reg(regbit, value) \
+ (((value) & SSCR2_##regbit##_MASK) << SSCR2_##regbit##_SHIFT)
+
+#define SSPSP_reg(regbit, value) \
+ (((value) & SSPSP_##regbit##_MASK) << SSPSP_##regbit##_SHIFT)
+
+#define SSRSA_reg(regbit, value) \
+ (((value) & SSRSA_##regbit##_MASK) << SSRSA_##regbit##_SHIFT)
+
+#define SSIOC_reg(regbit, value) \
+ (((value) & SSIOC_##regbit##_MASK) << SSIOC_##regbit##_SHIFT)
+
+#define SSTSA_reg(regbit, value) \
+ (((value) & SSTSA_##regbit##_MASK) << SSTSA_##regbit##_SHIFT)
+
+#define set_SSCR0_reg(reg_pointer, regbit) \
+ write_SSCR0(read_SSCR0(reg_pointer) \
+ | (SSCR0_##regbit##_MASK << SSCR0_##regbit##_SHIFT), \
+ reg_pointer);
+
+#define clear_SSCR0_reg(reg_pointer, regbit) \
+ write_SSCR0((read_SSCR0(reg_pointer) \
+ & (~((SSCR0_##regbit##_MASK << SSCR0_##regbit##_SHIFT)))), \
+ reg_pointer);
+
+#define set_SSTSA_reg(reg_pointer, regbit) \
+ write_SSTSA(read_SSTSA(reg_pointer) \
+ | (SSTSA_##regbit##_MASK << SSTSA_##regbit##_SHIFT), \
+ reg_pointer);
+
+#define clear_SSTSA_reg(reg_pointer, regbit) \
+ write_SSTSA((read_SSTSA(reg_pointer) \
+ & (~((SSTSA_##regbit##_MASK << SSTSA_##regbit##_SHIFT)))), \
+ reg_pointer);
+
+#define set_SSCR3_reg(reg_pointer, regbit) \
+ write_SSCR3(read_SSCR3(reg_pointer) \
+ | (SSCR3_##regbit##_MASK << SSCR3_##regbit##_SHIFT), \
+ reg_pointer);
+
+#define clear_SSCR3_reg(reg_pointer, regbit) \
+ write_SSCR3((read_SSCR3(reg_pointer) \
+ & (~((SSCR3_##regbit##_MASK << SSCR3_##regbit##_SHIFT)))), \
+ reg_pointer);
+
+typedef struct AplI2sRegs {
+ uint32_t sscr0;
+ uint32_t sscr1;
+ uint32_t sssr;
+ uint32_t ssitr;
+ uint32_t ssdr;
+ uint32_t ssto;
+ uint32_t sspsp;
+ uint32_t sstsa;
+ uint32_t ssrsa;
+ uint32_t sstss;
+ uint32_t ssacd;
+ uint32_t sscr2;
+ uint32_t ssfs;
+ uint32_t frame_cnt0;
+ uint32_t frame_cnt1;
+ uint32_t frame_cnt2;
+ uint32_t frame_cnt3;
+ uint32_t frame_cnt4;
+ uint32_t frame_cnt5;
+ uint32_t frame_cnt6;
+ uint32_t frame_cnt7;
+ uint32_t sfifol;
+ uint32_t sfifott;
+ uint32_t sscr3;
+ uint32_t sscr4;
+ uint32_t sscr5;
+ uint32_t asrc_frt;
+ uint32_t asrc_ftc;
+ uint32_t asrc_snpsht;
+ uint32_t asrc_frmcnt;
+} __attribute__ ((packed)) AplI2sRegs;
+
+#endif
diff --git a/src/drivers/bus/i2s/apollolake/apollolake.c b/src/drivers/bus/i2s/apollolake/apollolake.c
new file mode 100644
index 0000000..ebbda92
--- /dev/null
+++ b/src/drivers/bus/i2s/apollolake/apollolake.c
@@ -0,0 +1,333 @@
+/*
+ * Copyright (C) 2016 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <libpayload.h>
+
+#include "base/container_of.h"
+#include "drivers/bus/i2s/i2s.h"
+#include "drivers/bus/i2s/apollolake/apollolake.h"
+#include "drivers/bus/i2s/apollolake/apollolake-regs.h"
+#include <arch/io.h>
+
+#define LPE_SSP_FIFO_SIZE 16
+
+/*
+ * i2s_ensable - enable SSP device
+ * @regs: pointer to registers
+ * Writes SSP register to enable the device
+ */
+static void i2s_enable(AplI2sRegs *regs)
+{
+ set_SSCR0_reg(regs, SSE);
+ set_SSTSA_reg(regs, TXEN);
+}
+
+/**
+ * i2s_disable - disable SSP device
+ * @regs: pointer to registers
+ *
+ * Writes SSP register to disable the device
+ */
+static void i2s_disable(AplI2sRegs *regs)
+{
+ clear_SSCR0_reg(regs, SSE);
+ clear_SSTSA_reg(regs, TXEN);
+}
+
+/*
+ * calculate_sspsp - Calculate sspsp register
+ * @settings: config settings
+ *
+ * Calculate sspsp register.
+ */
+static uint32_t calculate_sspsp(const AplI2sSettings *settings)
+{
+ uint32_t sspsp = 0;
+
+ sspsp = SSPSP_reg(FSRT, NEXT_FRMS_ASS_WITH_LSB_PREVIOUS_FRM) |
+ SSPSP_reg(SFRMWDTH, settings->ssp_psp_T6) |
+ SSPSP_reg(EDMYSTOP, settings->ssp_psp_T4);
+ return sspsp;
+}
+
+
+/*
+ * calculate_sscr0: Calculate sscr0 register
+ * @settings: config settings
+ * @bps: bits per sample
+ *
+ * Calculate sscr0 register
+ */
+static uint32_t calculate_sscr0(const AplI2sSettings *settings, int bps)
+{
+ uint32_t sscr0 = 0;
+
+ if (bps > 16)
+ sscr0 = SSCR0_reg(DSS, SSCR0_DataSize(bps - 16)) |
+ SSCR0_reg(EDSS, EDSS_17_32_BITS);
+ else
+ sscr0 = SSCR0_reg(DSS, SSCR0_DataSize(bps)) |
+ SSCR0_reg(EDSS, EDSS_4_16_BITS);
+
+ sscr0 |= SSCR0_reg(MOD, settings->mode) |
+ SSCR0_reg(FRF, PSP_FORMAT) |
+ SSCR0_reg(SCR, 0x7) |
+ SSCR0_reg(RIM, SSP_FIFO_INT_DISABLE) |
+ SSCR0_reg(TIM, SSP_FIFO_INT_DISABLE) |
+ SSCR0_reg(ECS, DIV_DISABLE) |
+ SSCR0_reg(NCS, NETWORK_CLOCK_DISABLE) |
+ SSCR0_reg(FRDC,
+ SSCR0_SlotsPerFrm(settings->frame_rate_divider_ctrl));
+ return sscr0;
+}
+
+/**
+ * calculate_sscr1 - Calculate sscr1 register.
+ * @settings: config settings
+ *
+ * Calculate sscr1 register.
+ */
+static uint32_t calculate_sscr1(const AplI2sSettings *settings)
+{
+ uint32_t sscr1 = 0;
+
+ sscr1 = SSCR1_reg(TTE, TXD_TRISTATE_ON) |
+ SSCR1_reg(TTELP, TXD_TRISTATE_LAST_PHASE_ON) |
+ SSCR1_reg(RSRE, 1) |
+ SSCR1_reg(TSRE, 1) |
+ SSCR1_reg(TRAIL, 1);
+ return sscr1;
+}
+
+/*
+ * calculate_ssioc- Calculate ssioc register.
+ */
+static uint32_t calculate_ssioc(void)
+{
+ uint32_t ssioc = 0;
+
+ ssioc = SSIOC_reg(SCOE, SSP_ENABLE_CLOCK);
+ return ssioc;
+}
+
+/*
+ * calculate_sscr2 - Calculate sscr2 register.
+ */
+static uint32_t calculate_sscr2(void)
+
+{
+ uint32_t sscr2 = 0;
+
+ sscr2 |= SSCR2_reg(SDFD, SSP_DMA_FINISH_DISABLE) |
+ SSCR2_reg(TURM1, TRANSMIT_UNDERRUN_MODE_1_ENABLE);
+
+ return sscr2;
+}
+
+/*
+* Power on DSP and Enable SSP for data transmission
+*/
+
+static int enable_DSP_SSP(AplI2s *bus)
+{
+ /* Power On Audio Controller and wait till its powered on */
+ writel(0x1, bus->lpe_bar0 + POWER_OFFSET);
+ for (int i = 0; i < RETRY_COUNT; i++) {
+ if (readl(bus->lpe_bar0 + POWER_OFFSET) == 0x1)
+ break;
+ mdelay(1);
+ }
+
+ if (readl(bus->lpe_bar0 + POWER_OFFSET) != 0x1)
+ return -1;
+
+ /* Enable the ADSP bar fuctionality */
+ writel(ENABLE_ADSP_BAR, bus->lpe_bar0 + BAR_OFFSET);
+ for (int i = 0; i < RETRY_COUNT; i++) {
+ if (readl(bus->lpe_bar0 + BAR_OFFSET) == ENABLE_ADSP_BAR)
+ break;
+ mdelay(1);
+ }
+ if (readl(bus->lpe_bar0 + BAR_OFFSET) != ENABLE_ADSP_BAR)
+ return -1;
+
+ /* power on dsp core to access ssp registeres*/
+ writel(DSP_POWER_ON, bus->lpe_bar4 + DSP_POWER_OFFSET);
+ /* wait till the DSP powers on */
+ for (int i = 0; i < RETRY_COUNT; i++) {
+ if (readl(bus->lpe_bar4 + DSP_POWER_OFFSET) == DSP_POWERED_UP)
+ break;
+ mdelay(1);
+ }
+ if (readl(bus->lpe_bar4 + DSP_POWER_OFFSET) != DSP_POWERED_UP)
+ return -1;
+
+ /* setup the clock to disable dynamic clock gating of SSP */
+ writel(DISABLE_CLOCK_GATING, bus->lpe_bar4 + CLOCK_GATING_OFFSET);
+ for (int i = 0; i < RETRY_COUNT; i++) {
+ if (readl(bus->lpe_bar4 + CLOCK_GATING_OFFSET) ==
+ DISABLED_CLOCK_GATING)
+ break;
+ mdelay(1);
+ }
+ if (readl(bus->lpe_bar4 + CLOCK_GATING_OFFSET) != DISABLED_CLOCK_GATING)
+ return -1;
+
+ return 0;
+}
+/*
+ * set_ssp_i2s_hw - Configure SSP driver according to settings.
+ * @regs: pointer to registers
+ * @shim: pointer to shim registers
+ * @settings: config settings
+ * @bps: bits per sample
+ *
+ * Configure SSP driver according to settings.
+ */
+static void set_ssp_i2s_hw(AplI2sRegs *regs, AplI2sRegs *shim,
+ const AplI2sSettings *settings, int bps)
+{
+ uint32_t sscr0;
+ uint32_t sscr1;
+ uint32_t sscr2;
+ uint32_t sscr3;
+ uint32_t sstsa;
+ uint32_t ssrsa;
+ uint32_t sspsp;
+ uint32_t sspsp2 = 0;
+ uint32_t sssr = 0;
+ uint32_t ssioc;
+
+ sscr0 = calculate_sscr0(settings, bps);
+ sscr1 = calculate_sscr1(settings);
+ sscr2 = calculate_sscr2();
+ sscr3 = 0;
+ sspsp = calculate_sspsp(settings);
+ sstsa = SSTSA_reg(TTSA, settings->ssp_active_tx_slots_map);
+ ssrsa = SSRSA_reg(RTSA, settings->ssp_active_rx_slots_map);
+ ssioc = calculate_ssioc();
+
+ write_SSCR0(sscr0, regs);
+ write_SSCR1(sscr1, regs);
+ write_SSCR2(sscr2, regs);
+ write_SSCR3(sscr3, regs);
+ write_SSPSP(sspsp, regs);
+ write_SSPSP2(sspsp2, regs);
+ write_SSTSA(sstsa, regs);
+ write_SSRSA(ssrsa, regs);
+ write_SSIOC(ssioc, regs);
+
+ /* Clear status */
+ write_SSSR(sssr, regs);
+
+ /* set the time out for the reception */
+ write_SSTO(SSP_TIMEOUT, regs);
+}
+
+/*
+ * apl_i2s_init - Initialize I2s.
+ * @bus: i2s config structure
+ *
+ * Initialize I2s.
+ */
+static int apl_i2s_init(AplI2s *bus)
+{
+ if (enable_DSP_SSP(bus))
+ return -1;
+ i2s_disable(bus->regs);
+ set_ssp_i2s_hw(bus->regs,
+ bus->shim,
+ bus->settings,
+ bus->bits_per_sample);
+ return 0;
+}
+
+/*
+ * apl_i2s_send - Send audio samples to I2s controller.
+ * @me: I2sOps structure
+ * @data: Audio samples
+ * @length: Number of samples
+ *
+ * Send audio samples to I2s controller.
+*/
+static int apl_i2s_send(I2sOps *me, unsigned int *data, unsigned int length)
+{
+ int i;
+ uint64_t start;
+ AplI2s *bus = container_of(me, AplI2s, ops);
+ struct AplI2sRegs *i2s_reg = bus->regs;
+
+ if (!bus->initialized) {
+ if (apl_i2s_init(bus))
+ return -1;
+ bus->initialized = 1;
+ }
+
+ if (length < LPE_SSP_FIFO_SIZE) {
+ printf("%s : Invalid data size\n", __func__);
+ return -1;
+ }
+
+ gpio_set(bus->sdmode_gpio, 1);
+
+ for (i = 0; i < LPE_SSP_FIFO_SIZE; i++)
+ writel(*data++, &i2s_reg->ssdr);
+
+ i2s_enable(bus->regs);
+ length -= LPE_SSP_FIFO_SIZE;
+
+ while (length > 0) {
+ start = timer_us(0);
+ if (read_SSSR(bus->regs) & 0x4) {
+ writel(*data++, &i2s_reg->ssdr);
+ length--;
+ } else {
+ if (timer_us(start) > 100000) {
+ i2s_disable(bus->regs);
+ gpio_set(bus->sdmode_gpio, 0);
+ printf("I2S Transfer Timeout\n");
+ return -1;
+ }
+ }
+ }
+
+ mdelay(1);
+ gpio_set(bus->sdmode_gpio, 0);
+ i2s_disable(bus->regs);
+ return 0;
+}
+
+/*
+ * new_apl_i2s - Allocate new I2s data structures.
+ * @settings: Apollolake codec settigns
+ * @bps: Bits per sample
+ *
+ * Allocate new I2s data structures.
+ */
+AplI2s *new_apl_i2s(const AplI2sSettings *settings, int bps, GpioOps *sdmode)
+{
+ AplI2s *bus = xzalloc(sizeof(*bus));
+ pcidev_t lpe_pcidev = PCI_DEV(0, AUDIO_DEV, 0);
+
+ bus->lpe_bar0 = pci_read_config32(lpe_pcidev, REG_BAR0) & (~0xf);
+ bus->lpe_bar4 = pci_read_config32(lpe_pcidev, REG_BAR4) & (~0xf);
+ bus->ops.send = &apl_i2s_send;
+ bus->regs = (AplI2sRegs *)(bus->lpe_bar4 + APL_SSP5_START_ADDRESS);
+ bus->shim = (AplI2sRegs *)(bus->lpe_bar4 + APL_SSP5_SHIM_START_ADDRESS);
+ bus->settings = settings;
+ bus->bits_per_sample = bps;
+ bus->sdmode_gpio = sdmode;
+
+ return bus;
+}
diff --git a/src/drivers/bus/i2s/apollolake/apollolake.h b/src/drivers/bus/i2s/apollolake/apollolake.h
new file mode 100644
index 0000000..e0f0506
--- /dev/null
+++ b/src/drivers/bus/i2s/apollolake/apollolake.h
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 2016, Intel Corporation.
+ * Copyright 2016 Google Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without evenp the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+
+#ifndef __DRIVERS_BUS_I2S_APOLLOLAKE_H__
+#define __DRIVERS_BUS_I2S_APOLLOLAKE_H___
+
+#include <pci.h>
+#include "drivers/gpio/gpio.h"
+#include "drivers/gpio/apollolake.h"
+#include "drivers/bus/i2s/i2s.h"
+
+#define DSP_POWER_OFFSET 0x04
+#define CLOCK_GATING_OFFSET 0x378
+#define DSP_POWER_ON 0x00030303
+#define DSP_POWERED_UP 0x03030303
+#define DISABLE_CLOCK_GATING 0x0FFC0000
+#define DISABLED_CLOCK_GATING 0x0DFC0000
+#define RETRY_COUNT 1000
+#define SSP_TIMEOUT 0
+#define AUDIO_DEV 0x0e
+#define POWER_OFFSET 0x8
+#define BAR_OFFSET 0x804
+#define ENABLE_ADSP_BAR 0x40000000
+
+typedef enum {
+ SSP_IN_NORMAL_MODE = 0x0,
+ SSP_IN_NETWORK_MODE,
+ SSP_INVALID_MODE = 0xF,
+} AplSspMode;
+
+typedef enum {
+ TXD_TRISTATE_LAST_PHASE_OFF = 0x0,
+ TXD_TRISTATE_LAST_PHASE_ON,
+} AplSspTxdTristateLastPhase;
+
+typedef enum {
+ TXD_TRISTATE_OFF = 0x0,
+ TXD_TRISTATE_ON,
+} AplSspTxdTristateEnable;
+
+typedef enum {
+ NEXT_FRMS_ASS_AFTER_END_OF_T4 = 0x0,
+ NEXT_FRMS_ASS_WITH_LSB_PREVIOUS_FRM,
+} AplSspFrameSyncRelativeTimingBit;
+
+typedef enum {
+ DIV_DISABLE = 0x0,
+ DIV_ENABLE,
+} AplSspMnDividerEnable;
+
+
+typedef enum {
+ SSP_TS_1 = 0,
+ SSP_TS_2,
+ SSP_TS_4,
+ SSP_TS_8,
+ SSP_TS_SIZE
+} AplSspTimeslot;
+
+typedef enum {
+ EDSS_4_16_BITS = 0,
+ EDSS_17_32_BITS,
+} AplDataSizeSelect;
+
+typedef enum {
+ NETWORK_CLOCK_DISABLE = 0,
+ NETWORK_CLOCK_ENABLE,
+} AplNetworkClockSelect;
+
+typedef enum {
+ SSP_DISABLE_CLOCK = 0,
+ SSP_ENABLE_CLOCK,
+} AplForceSSPClockRunning;
+
+typedef enum {
+ SSP_DMA_FINISH_ENABLE = 0,
+ SSP_DMA_FINISH_DISABLE,
+} AplSSPDMAFinishDisable;
+
+typedef enum {
+ TRANSMIT_UNDERRUN_MODE_1_DISABLE = 0,
+ TRANSMIT_UNDERRUN_MODE_1_ENABLE,
+} AplTransmitUnderrunMode1;
+
+typedef enum {
+ FRAME_RATE_CONTROL_STEREO = 2,
+ FRAME_RATE_CONTROL_TDM8 = 8,
+} FrameRateDividerControl;
+
+/* Configuration-dependent settings. */
+typedef struct AplI2sSettings {
+ AplSspMode mode;
+ uint8_t frame_rate_divider_ctrl;
+ uint8_t ssp_psp_T4;
+ uint8_t ssp_psp_T6;
+ uint8_t ssp_active_tx_slots_map;
+ uint8_t ssp_active_rx_slots_map;
+ uint32_t m_value;
+ uint32_t n_value;
+} AplI2sSettings;
+
+typedef struct {
+ I2sOps ops;
+ uintptr_t lpe_bar0;
+ uintptr_t lpe_bar4;
+ struct AplI2sRegs *regs;
+ struct AplI2sRegs *shim;
+ const struct AplI2sSettings *settings;
+
+ int initialized;
+ int bits_per_sample;
+ GpioOps *sdmode_gpio;
+} AplI2s;
+
+
+AplI2s *new_apl_i2s(const AplI2sSettings *settings, int bps, GpioOps *sdmode);
+
+#endif