uboot/drivers/sound/broadwell_i2s.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Intel Broadwell I2S driver
   4 *
   5 * Copyright 2019 Google LLC
   6 *
   7 * Modified from dc i2s/broadwell/broadwell.c
   8 */
   9
  10#define LOG_CATEGORY UCLASS_I2S
  11
  12#include <common.h>
  13#include <dm.h>
  14#include <i2s.h>
  15#include <log.h>
  16#include <time.h>
  17#include <asm/io.h>
  18#include "broadwell_i2s.h"
  19
  20enum {
  21        BDW_SHIM_START_ADDRESS = 0xfb000,
  22        BDW_SSP0_START_ADDRESS = 0xfc000,
  23        BDW_SSP1_START_ADDRESS = 0xfd000,
  24};
  25
  26struct broadwell_i2s_priv {
  27        enum frame_sync_rel_timing_t rel_timing;
  28        enum frame_sync_pol_t sfrm_polarity;
  29        enum end_transfer_state_t end_transfer_state;
  30        enum clock_mode_t sclk_mode;
  31        uint sclk_dummy_stop;   /* 0-31 */
  32        uint sclk_frame_width;  /* 1-38 */
  33        struct i2s_shim_regs *shim;
  34        struct broadwell_i2s_regs *regs;
  35};
  36
  37static void init_shim_csr(struct broadwell_i2s_priv *priv)
  38{
  39        /*
  40         * Select SSP clock
  41         * Turn off low power clock
  42         * Set PIO mode
  43         * Stall DSP core
  44         */
  45        clrsetbits_le32(&priv->shim->csr,
  46                        SHIM_CS_S0IOCS | SHIM_CS_LPCS | SHIM_CS_DCS_MASK,
  47                        SHIM_CS_S1IOCS | SHIM_CS_SBCS_SSP1_24MHZ |
  48                        SHIM_CS_SBCS_SSP0_24MHZ | SHIM_CS_SDPM_PIO_SSP1 |
  49                        SHIM_CS_SDPM_PIO_SSP0 | SHIM_CS_STALL |
  50                        SHIM_CS_DCS_DSP32_AF32);
  51}
  52
  53static void init_shim_clkctl(struct i2s_uc_priv *uc_priv,
  54                             struct broadwell_i2s_priv *priv)
  55{
  56        u32 clkctl = readl(&priv->shim->clkctl);
  57
  58        /* Set 24Mhz mclk, prevent local clock gating, enable SSP0 clock */
  59        clkctl &= SHIM_CLKCTL_RESERVED;
  60        clkctl |= SHIM_CLKCTL_MCLK_24MHZ | SHIM_CLKCTL_DCPLCG;
  61
  62        /* Enable requested SSP interface */
  63        if (uc_priv->id)
  64                clkctl |= SHIM_CLKCTL_SCOE_SSP1 | SHIM_CLKCTL_SFLCGB_SSP1_CGD;
  65        else
  66                clkctl |= SHIM_CLKCTL_SCOE_SSP0 | SHIM_CLKCTL_SFLCGB_SSP0_CGD;
  67
  68        writel(clkctl, &priv->shim->clkctl);
  69}
  70
  71static void init_sscr0(struct i2s_uc_priv *uc_priv,
  72                       struct broadwell_i2s_priv *priv)
  73{
  74        u32 sscr0;
  75        uint scale;
  76
  77        /* Set data size based on BPS */
  78        if (uc_priv->bitspersample > 16)
  79                sscr0 = (uc_priv->bitspersample - 16 - 1) << SSP_SSC0_DSS_SHIFT
  80                         | SSP_SSC0_EDSS;
  81        else
  82                sscr0 = (uc_priv->bitspersample - 1) << SSP_SSC0_DSS_SHIFT;
  83
  84        /* Set network mode, Stereo PSP frame format */
  85        sscr0 |= SSP_SSC0_MODE_NETWORK |
  86                SSP_SSC0_FRDC_STEREO |
  87                SSP_SSC0_FRF_PSP |
  88                SSP_SSC0_TIM |
  89                SSP_SSC0_RIM |
  90                SSP_SSC0_ECS_PCH |
  91                SSP_SSC0_NCS_PCH |
  92                SSP_SSC0_ACS_PCH;
  93
  94        /* Scale 24MHz MCLK */
  95        scale = uc_priv->audio_pll_clk / uc_priv->samplingrate / uc_priv->bfs;
  96        sscr0 |= scale << SSP_SSC0_SCR_SHIFT;
  97
  98        writel(sscr0, &priv->regs->sscr0);
  99}
 100
 101static void init_sscr1(struct broadwell_i2s_priv *priv)
 102{
 103        u32 sscr1 = readl(&priv->regs->sscr1);
 104
 105        sscr1 &= SSP_SSC1_RESERVED;
 106
 107        /* Set as I2S master */
 108        sscr1 |= SSP_SSC1_SCLKDIR_MASTER | SSP_SSC1_SCLKDIR_MASTER;
 109
 110        /* Enable TXD tristate behavior for PCH */
 111        sscr1 |= SSP_SSC1_TTELP | SSP_SSC1_TTE;
 112
 113        /* Disable DMA Tx/Rx service request */
 114        sscr1 |= SSP_SSC1_TSRE | SSP_SSC1_RSRE;
 115
 116        /* Clock on during transfer */
 117        sscr1 |= SSP_SSC1_SCFR;
 118
 119        /* Set FIFO thresholds */
 120        sscr1 |= SSP_FIFO_SIZE << SSP_SSC1_RFT_SHIFT;
 121        sscr1 |= SSP_FIFO_SIZE << SSP_SSC1_TFT_SHIFT;
 122
 123        /* Disable interrupts */
 124        sscr1 &= ~(SSP_SSC1_EBCEI | SSP_SSC1_TINTE | SSP_SSC1_PINTE);
 125        sscr1 &= ~(SSP_SSC1_LBM | SSP_SSC1_RWOT);
 126
 127        writel(sscr1, &priv->regs->sscr1);
 128}
 129
 130static void init_sspsp(struct broadwell_i2s_priv *priv)
 131{
 132        u32 sspsp = readl(&priv->regs->sspsp);
 133
 134        sspsp &= SSP_PSP_RESERVED;
 135        sspsp |= priv->sclk_mode << SSP_PSP_SCMODE_SHIFT;
 136        sspsp |= (priv->sclk_dummy_stop << SSP_PSP_DMYSTOP_SHIFT) &
 137                        SSP_PSP_DMYSTOP_MASK;
 138        sspsp |= (priv->sclk_dummy_stop >> 2 << SSP_PSP_EDYMSTOP_SHIFT) &
 139                        SSP_PSP_EDMYSTOP_MASK;
 140        sspsp |= priv->sclk_frame_width << SSP_PSP_SFRMWDTH_SHIFT;
 141
 142        /* Frame Sync Relative Timing */
 143        if (priv->rel_timing == NEXT_FRMS_AFTER_END_OF_T4)
 144                sspsp |= SSP_PSP_FSRT;
 145        else
 146                sspsp &= ~SSP_PSP_FSRT;
 147
 148        /* Serial Frame Polarity */
 149        if (priv->sfrm_polarity == SSP_FRMS_ACTIVE_HIGH)
 150                sspsp |= SSP_PSP_SFRMP;
 151        else
 152                sspsp &= ~SSP_PSP_SFRMP;
 153
 154        /* End Data Transfer State */
 155        if (priv->end_transfer_state == SSP_END_TRANSFER_STATE_LOW)
 156                sspsp &= ~SSP_PSP_ETDS;
 157        else
 158                sspsp |= SSP_PSP_ETDS;
 159
 160        writel(sspsp, &priv->regs->sspsp);
 161}
 162
 163static void init_ssp_time_slot(struct broadwell_i2s_priv *priv)
 164{
 165        writel(3, &priv->regs->sstsa);
 166        writel(3, &priv->regs->ssrsa);
 167}
 168
 169static int bdw_i2s_init(struct udevice *dev)
 170{
 171        struct i2s_uc_priv *uc_priv = dev_get_uclass_priv(dev);
 172        struct broadwell_i2s_priv *priv = dev_get_priv(dev);
 173
 174        init_shim_csr(priv);
 175        init_shim_clkctl(uc_priv, priv);
 176        init_sscr0(uc_priv, priv);
 177        init_sscr1(priv);
 178        init_sspsp(priv);
 179        init_ssp_time_slot(priv);
 180
 181        return 0;
 182}
 183
 184static void bdw_i2s_enable(struct broadwell_i2s_priv *priv)
 185{
 186        setbits_le32(&priv->regs->sscr0, SSP_SSC0_SSE);
 187        setbits_le32(&priv->regs->sstsa, SSP_SSTSA_EN);
 188}
 189
 190static void bdw_i2s_disable(struct broadwell_i2s_priv *priv)
 191{
 192        clrbits_le32(&priv->regs->sstsa, SSP_SSTSA_EN);
 193        clrbits_le32(&priv->regs->sstsa, SSP_SSTSA_EN);
 194}
 195
 196static int broadwell_i2s_tx_data(struct udevice *dev, void *data,
 197                                 uint data_size)
 198{
 199        struct broadwell_i2s_priv *priv = dev_get_priv(dev);
 200        u32 *ptr = data;
 201
 202        log_debug("data=%p, data_size=%x\n", data, data_size);
 203        if (data_size < SSP_FIFO_SIZE) {
 204                log_err("Invalid I2S data size\n");
 205                return -ENODATA;
 206        }
 207
 208        /* Enable I2S interface */
 209        bdw_i2s_enable(priv);
 210
 211        /* Transfer data */
 212        while (data_size > 0) {
 213                ulong start = timer_get_us() + 100000;
 214
 215                /* Write data if transmit FIFO has room */
 216                if (readl(&priv->regs->sssr) & SSP_SSS_TNF) {
 217                        writel(*ptr++, &priv->regs->ssdr);
 218                        data_size -= sizeof(*ptr);
 219                } else {
 220                        if ((long)(timer_get_us() - start) > 0) {
 221                                /* Disable I2S interface */
 222                                bdw_i2s_disable(priv);
 223                                log_debug("I2S Transfer Timeout\n");
 224                                return -ETIMEDOUT;
 225                        }
 226                }
 227        }
 228
 229        /* Disable I2S interface */
 230        bdw_i2s_disable(priv);
 231        log_debug("done\n");
 232
 233        return 0;
 234}
 235
 236static int broadwell_i2s_probe(struct udevice *dev)
 237{
 238        struct i2s_uc_priv *uc_priv = dev_get_uclass_priv(dev);
 239        struct broadwell_i2s_priv *priv = dev_get_priv(dev);
 240        struct udevice *adsp = dev_get_parent(dev);
 241        u32 bar0, offset;
 242        int ret;
 243
 244        bar0 = dm_pci_read_bar32(adsp, 0);
 245        if (!bar0) {
 246                log_debug("Cannot read adsp bar0\n");
 247                return -EINVAL;
 248        }
 249        offset = dev_read_addr_index(dev, 0);
 250        if (offset == FDT_ADDR_T_NONE) {
 251                log_debug("Cannot read address index 0\n");
 252                return -EINVAL;
 253        }
 254        uc_priv->base_address = bar0 + offset;
 255
 256        /*
 257         * Hard-code these values. If other settings are required we can add
 258         * this to the device tree.
 259         */
 260        uc_priv->rfs = 64;
 261        uc_priv->bfs = 32;
 262        uc_priv->audio_pll_clk = 24 * 1000 * 1000;
 263        uc_priv->samplingrate = 48000;
 264        uc_priv->bitspersample = 16;
 265        uc_priv->channels = 2;
 266        uc_priv->id = 0;
 267
 268        priv->shim = (struct i2s_shim_regs *)uc_priv->base_address;
 269        priv->sfrm_polarity = SSP_FRMS_ACTIVE_LOW;
 270        priv->end_transfer_state = SSP_END_TRANSFER_STATE_LOW;
 271        priv->sclk_mode = SCLK_MODE_DDF_DSR_ISL;
 272        priv->rel_timing = NEXT_FRMS_WITH_LSB_PREVIOUS_FRM;
 273        priv->sclk_dummy_stop = 0;
 274        priv->sclk_frame_width = 31;
 275
 276        offset = dev_read_addr_index(dev, 1 + uc_priv->id);
 277        if (offset == FDT_ADDR_T_NONE) {
 278                log_debug("Cannot read address index %d\n", 1 + uc_priv->id);
 279                return -EINVAL;
 280        }
 281        log_debug("bar0=%x, uc_priv->base_address=%x, offset=%x\n", bar0,
 282                  uc_priv->base_address, offset);
 283        priv->regs = (struct broadwell_i2s_regs *)(bar0 + offset);
 284
 285        ret = bdw_i2s_init(dev);
 286        if (ret)
 287                return ret;
 288
 289        return 0;
 290}
 291
 292static const struct i2s_ops broadwell_i2s_ops = {
 293        .tx_data        = broadwell_i2s_tx_data,
 294};
 295
 296static const struct udevice_id broadwell_i2s_ids[] = {
 297        { .compatible = "intel,broadwell-i2s" },
 298        { }
 299};
 300
 301U_BOOT_DRIVER(broadwell_i2s) = {
 302        .name           = "broadwell_i2s",
 303        .id             = UCLASS_I2S,
 304        .of_match       = broadwell_i2s_ids,
 305        .probe          = broadwell_i2s_probe,
 306        .ops            = &broadwell_i2s_ops,
 307        .priv_auto      = sizeof(struct broadwell_i2s_priv),
 308};
 309