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