1*88eb404cSKevin Li // SPDX-License-Identifier: GPL-2.0-or-later 2*88eb404cSKevin Li // linux/sound/bcm/bcm63xx-pcm-whistler.c 3*88eb404cSKevin Li // BCM63xx whistler pcm interface 4*88eb404cSKevin Li // Copyright (c) 2020 Broadcom Corporation 5*88eb404cSKevin Li // Author: Kevin-Ke Li <kevin-ke.li@broadcom.com> 6*88eb404cSKevin Li 7*88eb404cSKevin Li #include <linux/dma-mapping.h> 8*88eb404cSKevin Li #include <linux/io.h> 9*88eb404cSKevin Li #include <linux/module.h> 10*88eb404cSKevin Li #include <sound/pcm_params.h> 11*88eb404cSKevin Li #include <linux/regmap.h> 12*88eb404cSKevin Li #include <linux/of_device.h> 13*88eb404cSKevin Li #include <sound/soc.h> 14*88eb404cSKevin Li #include "bcm63xx-i2s.h" 15*88eb404cSKevin Li 16*88eb404cSKevin Li 17*88eb404cSKevin Li struct i2s_dma_desc { 18*88eb404cSKevin Li unsigned char *dma_area; 19*88eb404cSKevin Li dma_addr_t dma_addr; 20*88eb404cSKevin Li unsigned int dma_len; 21*88eb404cSKevin Li }; 22*88eb404cSKevin Li 23*88eb404cSKevin Li struct bcm63xx_runtime_data { 24*88eb404cSKevin Li int dma_len; 25*88eb404cSKevin Li dma_addr_t dma_addr; 26*88eb404cSKevin Li dma_addr_t dma_addr_next; 27*88eb404cSKevin Li }; 28*88eb404cSKevin Li 29*88eb404cSKevin Li static const struct snd_pcm_hardware bcm63xx_pcm_hardware = { 30*88eb404cSKevin Li .info = SNDRV_PCM_INFO_MMAP | 31*88eb404cSKevin Li SNDRV_PCM_INFO_MMAP_VALID | 32*88eb404cSKevin Li SNDRV_PCM_INFO_INTERLEAVED | 33*88eb404cSKevin Li SNDRV_PCM_INFO_PAUSE | 34*88eb404cSKevin Li SNDRV_PCM_INFO_RESUME, 35*88eb404cSKevin Li .formats = SNDRV_PCM_FMTBIT_S32_LE, /* support S32 only */ 36*88eb404cSKevin Li .period_bytes_max = 8192 - 32, 37*88eb404cSKevin Li .periods_min = 1, 38*88eb404cSKevin Li .periods_max = PAGE_SIZE/sizeof(struct i2s_dma_desc), 39*88eb404cSKevin Li .buffer_bytes_max = 128 * 1024, 40*88eb404cSKevin Li .fifo_size = 32, 41*88eb404cSKevin Li }; 42*88eb404cSKevin Li 43*88eb404cSKevin Li static int bcm63xx_pcm_hw_params(struct snd_soc_component *component, 44*88eb404cSKevin Li struct snd_pcm_substream *substream, 45*88eb404cSKevin Li struct snd_pcm_hw_params *params) 46*88eb404cSKevin Li { 47*88eb404cSKevin Li struct i2s_dma_desc *dma_desc; 48*88eb404cSKevin Li struct snd_soc_pcm_runtime *rtd = substream->private_data; 49*88eb404cSKevin Li struct snd_pcm_runtime *runtime = substream->runtime; 50*88eb404cSKevin Li 51*88eb404cSKevin Li snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); 52*88eb404cSKevin Li runtime->dma_bytes = params_buffer_bytes(params); 53*88eb404cSKevin Li 54*88eb404cSKevin Li dma_desc = kzalloc(sizeof(*dma_desc), GFP_NOWAIT); 55*88eb404cSKevin Li if (!dma_desc) 56*88eb404cSKevin Li return -ENOMEM; 57*88eb404cSKevin Li 58*88eb404cSKevin Li snd_soc_dai_set_dma_data(rtd->cpu_dai, substream, dma_desc); 59*88eb404cSKevin Li 60*88eb404cSKevin Li return 0; 61*88eb404cSKevin Li } 62*88eb404cSKevin Li 63*88eb404cSKevin Li static int bcm63xx_pcm_hw_free(struct snd_soc_component *component, 64*88eb404cSKevin Li struct snd_pcm_substream *substream) 65*88eb404cSKevin Li { 66*88eb404cSKevin Li struct i2s_dma_desc *dma_desc; 67*88eb404cSKevin Li struct snd_soc_pcm_runtime *rtd = substream->private_data; 68*88eb404cSKevin Li 69*88eb404cSKevin Li dma_desc = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); 70*88eb404cSKevin Li kfree(dma_desc); 71*88eb404cSKevin Li snd_pcm_set_runtime_buffer(substream, NULL); 72*88eb404cSKevin Li 73*88eb404cSKevin Li return 0; 74*88eb404cSKevin Li } 75*88eb404cSKevin Li 76*88eb404cSKevin Li static int bcm63xx_pcm_trigger(struct snd_soc_component *component, 77*88eb404cSKevin Li struct snd_pcm_substream *substream, int cmd) 78*88eb404cSKevin Li { 79*88eb404cSKevin Li int ret = 0; 80*88eb404cSKevin Li struct snd_soc_pcm_runtime *rtd; 81*88eb404cSKevin Li struct bcm_i2s_priv *i2s_priv; 82*88eb404cSKevin Li struct regmap *regmap_i2s; 83*88eb404cSKevin Li 84*88eb404cSKevin Li rtd = substream->private_data; 85*88eb404cSKevin Li i2s_priv = dev_get_drvdata(rtd->cpu_dai->dev); 86*88eb404cSKevin Li regmap_i2s = i2s_priv->regmap_i2s; 87*88eb404cSKevin Li 88*88eb404cSKevin Li if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 89*88eb404cSKevin Li switch (cmd) { 90*88eb404cSKevin Li case SNDRV_PCM_TRIGGER_START: 91*88eb404cSKevin Li regmap_update_bits(regmap_i2s, 92*88eb404cSKevin Li I2S_TX_IRQ_EN, 93*88eb404cSKevin Li I2S_TX_DESC_OFF_INTR_EN, 94*88eb404cSKevin Li I2S_TX_DESC_OFF_INTR_EN); 95*88eb404cSKevin Li regmap_update_bits(regmap_i2s, 96*88eb404cSKevin Li I2S_TX_CFG, 97*88eb404cSKevin Li I2S_TX_ENABLE_MASK, 98*88eb404cSKevin Li I2S_TX_ENABLE); 99*88eb404cSKevin Li break; 100*88eb404cSKevin Li case SNDRV_PCM_TRIGGER_STOP: 101*88eb404cSKevin Li case SNDRV_PCM_TRIGGER_SUSPEND: 102*88eb404cSKevin Li case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 103*88eb404cSKevin Li regmap_write(regmap_i2s, 104*88eb404cSKevin Li I2S_TX_IRQ_EN, 105*88eb404cSKevin Li 0); 106*88eb404cSKevin Li regmap_update_bits(regmap_i2s, 107*88eb404cSKevin Li I2S_TX_CFG, 108*88eb404cSKevin Li I2S_TX_ENABLE_MASK, 109*88eb404cSKevin Li 0); 110*88eb404cSKevin Li break; 111*88eb404cSKevin Li default: 112*88eb404cSKevin Li ret = -EINVAL; 113*88eb404cSKevin Li } 114*88eb404cSKevin Li } else { 115*88eb404cSKevin Li switch (cmd) { 116*88eb404cSKevin Li case SNDRV_PCM_TRIGGER_START: 117*88eb404cSKevin Li regmap_update_bits(regmap_i2s, 118*88eb404cSKevin Li I2S_RX_IRQ_EN, 119*88eb404cSKevin Li I2S_RX_DESC_OFF_INTR_EN_MSK, 120*88eb404cSKevin Li I2S_RX_DESC_OFF_INTR_EN); 121*88eb404cSKevin Li regmap_update_bits(regmap_i2s, 122*88eb404cSKevin Li I2S_RX_CFG, 123*88eb404cSKevin Li I2S_RX_ENABLE_MASK, 124*88eb404cSKevin Li I2S_RX_ENABLE); 125*88eb404cSKevin Li break; 126*88eb404cSKevin Li case SNDRV_PCM_TRIGGER_STOP: 127*88eb404cSKevin Li case SNDRV_PCM_TRIGGER_SUSPEND: 128*88eb404cSKevin Li case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 129*88eb404cSKevin Li regmap_update_bits(regmap_i2s, 130*88eb404cSKevin Li I2S_RX_IRQ_EN, 131*88eb404cSKevin Li I2S_RX_DESC_OFF_INTR_EN_MSK, 132*88eb404cSKevin Li 0); 133*88eb404cSKevin Li regmap_update_bits(regmap_i2s, 134*88eb404cSKevin Li I2S_RX_CFG, 135*88eb404cSKevin Li I2S_RX_ENABLE_MASK, 136*88eb404cSKevin Li 0); 137*88eb404cSKevin Li break; 138*88eb404cSKevin Li default: 139*88eb404cSKevin Li ret = -EINVAL; 140*88eb404cSKevin Li } 141*88eb404cSKevin Li } 142*88eb404cSKevin Li return ret; 143*88eb404cSKevin Li } 144*88eb404cSKevin Li 145*88eb404cSKevin Li static int bcm63xx_pcm_prepare(struct snd_soc_component *component, 146*88eb404cSKevin Li struct snd_pcm_substream *substream) 147*88eb404cSKevin Li { 148*88eb404cSKevin Li struct i2s_dma_desc *dma_desc; 149*88eb404cSKevin Li struct regmap *regmap_i2s; 150*88eb404cSKevin Li struct bcm_i2s_priv *i2s_priv; 151*88eb404cSKevin Li struct snd_soc_pcm_runtime *rtd = substream->private_data; 152*88eb404cSKevin Li struct snd_pcm_runtime *runtime = substream->runtime; 153*88eb404cSKevin Li uint32_t regaddr_desclen, regaddr_descaddr; 154*88eb404cSKevin Li 155*88eb404cSKevin Li dma_desc = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); 156*88eb404cSKevin Li dma_desc->dma_len = snd_pcm_lib_period_bytes(substream); 157*88eb404cSKevin Li dma_desc->dma_addr = runtime->dma_addr; 158*88eb404cSKevin Li dma_desc->dma_area = runtime->dma_area; 159*88eb404cSKevin Li 160*88eb404cSKevin Li if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 161*88eb404cSKevin Li regaddr_desclen = I2S_TX_DESC_IFF_LEN; 162*88eb404cSKevin Li regaddr_descaddr = I2S_TX_DESC_IFF_ADDR; 163*88eb404cSKevin Li } else { 164*88eb404cSKevin Li regaddr_desclen = I2S_RX_DESC_IFF_LEN; 165*88eb404cSKevin Li regaddr_descaddr = I2S_RX_DESC_IFF_ADDR; 166*88eb404cSKevin Li } 167*88eb404cSKevin Li 168*88eb404cSKevin Li i2s_priv = dev_get_drvdata(rtd->cpu_dai->dev); 169*88eb404cSKevin Li regmap_i2s = i2s_priv->regmap_i2s; 170*88eb404cSKevin Li 171*88eb404cSKevin Li regmap_write(regmap_i2s, regaddr_desclen, dma_desc->dma_len); 172*88eb404cSKevin Li regmap_write(regmap_i2s, regaddr_descaddr, dma_desc->dma_addr); 173*88eb404cSKevin Li 174*88eb404cSKevin Li return 0; 175*88eb404cSKevin Li } 176*88eb404cSKevin Li 177*88eb404cSKevin Li static snd_pcm_uframes_t 178*88eb404cSKevin Li bcm63xx_pcm_pointer(struct snd_soc_component *component, 179*88eb404cSKevin Li struct snd_pcm_substream *substream) 180*88eb404cSKevin Li { 181*88eb404cSKevin Li snd_pcm_uframes_t x; 182*88eb404cSKevin Li struct bcm63xx_runtime_data *prtd = substream->runtime->private_data; 183*88eb404cSKevin Li 184*88eb404cSKevin Li if ((void *)prtd->dma_addr_next == NULL) 185*88eb404cSKevin Li prtd->dma_addr_next = substream->runtime->dma_addr; 186*88eb404cSKevin Li 187*88eb404cSKevin Li x = bytes_to_frames(substream->runtime, 188*88eb404cSKevin Li prtd->dma_addr_next - substream->runtime->dma_addr); 189*88eb404cSKevin Li 190*88eb404cSKevin Li return x == substream->runtime->buffer_size ? 0 : x; 191*88eb404cSKevin Li } 192*88eb404cSKevin Li 193*88eb404cSKevin Li static int bcm63xx_pcm_mmap(struct snd_soc_component *component, 194*88eb404cSKevin Li struct snd_pcm_substream *substream, 195*88eb404cSKevin Li struct vm_area_struct *vma) 196*88eb404cSKevin Li { 197*88eb404cSKevin Li struct snd_pcm_runtime *runtime = substream->runtime; 198*88eb404cSKevin Li 199*88eb404cSKevin Li return dma_mmap_wc(substream->pcm->card->dev, vma, 200*88eb404cSKevin Li runtime->dma_area, 201*88eb404cSKevin Li runtime->dma_addr, 202*88eb404cSKevin Li runtime->dma_bytes); 203*88eb404cSKevin Li 204*88eb404cSKevin Li } 205*88eb404cSKevin Li 206*88eb404cSKevin Li static int bcm63xx_pcm_open(struct snd_soc_component *component, 207*88eb404cSKevin Li struct snd_pcm_substream *substream) 208*88eb404cSKevin Li { 209*88eb404cSKevin Li int ret = 0; 210*88eb404cSKevin Li struct snd_pcm_runtime *runtime = substream->runtime; 211*88eb404cSKevin Li struct bcm63xx_runtime_data *prtd; 212*88eb404cSKevin Li 213*88eb404cSKevin Li runtime->hw = bcm63xx_pcm_hardware; 214*88eb404cSKevin Li ret = snd_pcm_hw_constraint_step(runtime, 0, 215*88eb404cSKevin Li SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32); 216*88eb404cSKevin Li if (ret) 217*88eb404cSKevin Li goto out; 218*88eb404cSKevin Li 219*88eb404cSKevin Li ret = snd_pcm_hw_constraint_step(runtime, 0, 220*88eb404cSKevin Li SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32); 221*88eb404cSKevin Li if (ret) 222*88eb404cSKevin Li goto out; 223*88eb404cSKevin Li 224*88eb404cSKevin Li ret = snd_pcm_hw_constraint_integer(runtime, 225*88eb404cSKevin Li SNDRV_PCM_HW_PARAM_PERIODS); 226*88eb404cSKevin Li if (ret < 0) 227*88eb404cSKevin Li goto out; 228*88eb404cSKevin Li 229*88eb404cSKevin Li ret = -ENOMEM; 230*88eb404cSKevin Li prtd = kzalloc(sizeof(*prtd), GFP_KERNEL); 231*88eb404cSKevin Li if (!prtd) 232*88eb404cSKevin Li goto out; 233*88eb404cSKevin Li 234*88eb404cSKevin Li runtime->private_data = prtd; 235*88eb404cSKevin Li return 0; 236*88eb404cSKevin Li out: 237*88eb404cSKevin Li return ret; 238*88eb404cSKevin Li } 239*88eb404cSKevin Li 240*88eb404cSKevin Li static int bcm63xx_pcm_close(struct snd_soc_component *component, 241*88eb404cSKevin Li struct snd_pcm_substream *substream) 242*88eb404cSKevin Li { 243*88eb404cSKevin Li struct snd_pcm_runtime *runtime = substream->runtime; 244*88eb404cSKevin Li struct bcm63xx_runtime_data *prtd = runtime->private_data; 245*88eb404cSKevin Li 246*88eb404cSKevin Li kfree(prtd); 247*88eb404cSKevin Li return 0; 248*88eb404cSKevin Li } 249*88eb404cSKevin Li 250*88eb404cSKevin Li static irqreturn_t i2s_dma_isr(int irq, void *bcm_i2s_priv) 251*88eb404cSKevin Li { 252*88eb404cSKevin Li unsigned int availdepth, ifflevel, offlevel, int_status, val_1, val_2; 253*88eb404cSKevin Li struct bcm63xx_runtime_data *prtd; 254*88eb404cSKevin Li struct snd_pcm_substream *substream; 255*88eb404cSKevin Li struct snd_pcm_runtime *runtime; 256*88eb404cSKevin Li struct regmap *regmap_i2s; 257*88eb404cSKevin Li struct i2s_dma_desc *dma_desc; 258*88eb404cSKevin Li struct snd_soc_pcm_runtime *rtd; 259*88eb404cSKevin Li struct bcm_i2s_priv *i2s_priv; 260*88eb404cSKevin Li 261*88eb404cSKevin Li i2s_priv = (struct bcm_i2s_priv *)bcm_i2s_priv; 262*88eb404cSKevin Li regmap_i2s = i2s_priv->regmap_i2s; 263*88eb404cSKevin Li 264*88eb404cSKevin Li /* rx */ 265*88eb404cSKevin Li regmap_read(regmap_i2s, I2S_RX_IRQ_CTL, &int_status); 266*88eb404cSKevin Li 267*88eb404cSKevin Li if (int_status & I2S_RX_DESC_OFF_INTR_EN_MSK) { 268*88eb404cSKevin Li substream = i2s_priv->capture_substream; 269*88eb404cSKevin Li runtime = substream->runtime; 270*88eb404cSKevin Li rtd = substream->private_data; 271*88eb404cSKevin Li prtd = runtime->private_data; 272*88eb404cSKevin Li dma_desc = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); 273*88eb404cSKevin Li 274*88eb404cSKevin Li offlevel = (int_status & I2S_RX_DESC_OFF_LEVEL_MASK) >> 275*88eb404cSKevin Li I2S_RX_DESC_OFF_LEVEL_SHIFT; 276*88eb404cSKevin Li while (offlevel) { 277*88eb404cSKevin Li regmap_read(regmap_i2s, I2S_RX_DESC_OFF_ADDR, &val_1); 278*88eb404cSKevin Li regmap_read(regmap_i2s, I2S_RX_DESC_OFF_LEN, &val_2); 279*88eb404cSKevin Li offlevel--; 280*88eb404cSKevin Li } 281*88eb404cSKevin Li prtd->dma_addr_next = val_1 + val_2; 282*88eb404cSKevin Li ifflevel = (int_status & I2S_RX_DESC_IFF_LEVEL_MASK) >> 283*88eb404cSKevin Li I2S_RX_DESC_IFF_LEVEL_SHIFT; 284*88eb404cSKevin Li 285*88eb404cSKevin Li availdepth = I2S_DESC_FIFO_DEPTH - ifflevel; 286*88eb404cSKevin Li while (availdepth) { 287*88eb404cSKevin Li dma_desc->dma_addr += 288*88eb404cSKevin Li snd_pcm_lib_period_bytes(substream); 289*88eb404cSKevin Li dma_desc->dma_area += 290*88eb404cSKevin Li snd_pcm_lib_period_bytes(substream); 291*88eb404cSKevin Li if (dma_desc->dma_addr - runtime->dma_addr >= 292*88eb404cSKevin Li runtime->dma_bytes) { 293*88eb404cSKevin Li dma_desc->dma_addr = runtime->dma_addr; 294*88eb404cSKevin Li dma_desc->dma_area = runtime->dma_area; 295*88eb404cSKevin Li } 296*88eb404cSKevin Li 297*88eb404cSKevin Li prtd->dma_addr = dma_desc->dma_addr; 298*88eb404cSKevin Li regmap_write(regmap_i2s, I2S_RX_DESC_IFF_LEN, 299*88eb404cSKevin Li snd_pcm_lib_period_bytes(substream)); 300*88eb404cSKevin Li regmap_write(regmap_i2s, I2S_RX_DESC_IFF_ADDR, 301*88eb404cSKevin Li dma_desc->dma_addr); 302*88eb404cSKevin Li availdepth--; 303*88eb404cSKevin Li } 304*88eb404cSKevin Li 305*88eb404cSKevin Li snd_pcm_period_elapsed(substream); 306*88eb404cSKevin Li 307*88eb404cSKevin Li /* Clear interrupt by writing 0 */ 308*88eb404cSKevin Li regmap_update_bits(regmap_i2s, I2S_RX_IRQ_CTL, 309*88eb404cSKevin Li I2S_RX_INTR_MASK, 0); 310*88eb404cSKevin Li } 311*88eb404cSKevin Li 312*88eb404cSKevin Li /* tx */ 313*88eb404cSKevin Li regmap_read(regmap_i2s, I2S_TX_IRQ_CTL, &int_status); 314*88eb404cSKevin Li 315*88eb404cSKevin Li if (int_status & I2S_TX_DESC_OFF_INTR_EN_MSK) { 316*88eb404cSKevin Li substream = i2s_priv->play_substream; 317*88eb404cSKevin Li runtime = substream->runtime; 318*88eb404cSKevin Li rtd = substream->private_data; 319*88eb404cSKevin Li prtd = runtime->private_data; 320*88eb404cSKevin Li dma_desc = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); 321*88eb404cSKevin Li 322*88eb404cSKevin Li offlevel = (int_status & I2S_TX_DESC_OFF_LEVEL_MASK) >> 323*88eb404cSKevin Li I2S_TX_DESC_OFF_LEVEL_SHIFT; 324*88eb404cSKevin Li while (offlevel) { 325*88eb404cSKevin Li regmap_read(regmap_i2s, I2S_TX_DESC_OFF_ADDR, &val_1); 326*88eb404cSKevin Li regmap_read(regmap_i2s, I2S_TX_DESC_OFF_LEN, &val_2); 327*88eb404cSKevin Li prtd->dma_addr_next = val_1 + val_2; 328*88eb404cSKevin Li offlevel--; 329*88eb404cSKevin Li } 330*88eb404cSKevin Li 331*88eb404cSKevin Li ifflevel = (int_status & I2S_TX_DESC_IFF_LEVEL_MASK) >> 332*88eb404cSKevin Li I2S_TX_DESC_IFF_LEVEL_SHIFT; 333*88eb404cSKevin Li availdepth = I2S_DESC_FIFO_DEPTH - ifflevel; 334*88eb404cSKevin Li 335*88eb404cSKevin Li while (availdepth) { 336*88eb404cSKevin Li dma_desc->dma_addr += 337*88eb404cSKevin Li snd_pcm_lib_period_bytes(substream); 338*88eb404cSKevin Li dma_desc->dma_area += 339*88eb404cSKevin Li snd_pcm_lib_period_bytes(substream); 340*88eb404cSKevin Li 341*88eb404cSKevin Li if (dma_desc->dma_addr - runtime->dma_addr >= 342*88eb404cSKevin Li runtime->dma_bytes) { 343*88eb404cSKevin Li dma_desc->dma_addr = runtime->dma_addr; 344*88eb404cSKevin Li dma_desc->dma_area = runtime->dma_area; 345*88eb404cSKevin Li } 346*88eb404cSKevin Li 347*88eb404cSKevin Li prtd->dma_addr = dma_desc->dma_addr; 348*88eb404cSKevin Li regmap_write(regmap_i2s, I2S_TX_DESC_IFF_LEN, 349*88eb404cSKevin Li snd_pcm_lib_period_bytes(substream)); 350*88eb404cSKevin Li regmap_write(regmap_i2s, I2S_TX_DESC_IFF_ADDR, 351*88eb404cSKevin Li dma_desc->dma_addr); 352*88eb404cSKevin Li availdepth--; 353*88eb404cSKevin Li } 354*88eb404cSKevin Li 355*88eb404cSKevin Li snd_pcm_period_elapsed(substream); 356*88eb404cSKevin Li 357*88eb404cSKevin Li /* Clear interrupt by writing 0 */ 358*88eb404cSKevin Li regmap_update_bits(regmap_i2s, I2S_TX_IRQ_CTL, 359*88eb404cSKevin Li I2S_TX_INTR_MASK, 0); 360*88eb404cSKevin Li } 361*88eb404cSKevin Li 362*88eb404cSKevin Li return IRQ_HANDLED; 363*88eb404cSKevin Li } 364*88eb404cSKevin Li 365*88eb404cSKevin Li static int bcm63xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) 366*88eb404cSKevin Li { 367*88eb404cSKevin Li struct snd_pcm_substream *substream = pcm->streams[stream].substream; 368*88eb404cSKevin Li struct snd_dma_buffer *buf = &substream->dma_buffer; 369*88eb404cSKevin Li size_t size = bcm63xx_pcm_hardware.buffer_bytes_max; 370*88eb404cSKevin Li 371*88eb404cSKevin Li buf->dev.type = SNDRV_DMA_TYPE_DEV; 372*88eb404cSKevin Li buf->dev.dev = pcm->card->dev; 373*88eb404cSKevin Li buf->private_data = NULL; 374*88eb404cSKevin Li 375*88eb404cSKevin Li buf->area = dma_alloc_wc(pcm->card->dev, 376*88eb404cSKevin Li size, &buf->addr, 377*88eb404cSKevin Li GFP_KERNEL); 378*88eb404cSKevin Li if (!buf->area) 379*88eb404cSKevin Li return -ENOMEM; 380*88eb404cSKevin Li buf->bytes = size; 381*88eb404cSKevin Li return 0; 382*88eb404cSKevin Li } 383*88eb404cSKevin Li 384*88eb404cSKevin Li static int bcm63xx_soc_pcm_new(struct snd_soc_component *component, 385*88eb404cSKevin Li struct snd_soc_pcm_runtime *rtd) 386*88eb404cSKevin Li { 387*88eb404cSKevin Li struct snd_pcm *pcm = rtd->pcm; 388*88eb404cSKevin Li struct bcm_i2s_priv *i2s_priv; 389*88eb404cSKevin Li int ret; 390*88eb404cSKevin Li 391*88eb404cSKevin Li i2s_priv = dev_get_drvdata(rtd->cpu_dai->dev); 392*88eb404cSKevin Li 393*88eb404cSKevin Li of_dma_configure(pcm->card->dev, pcm->card->dev->of_node, 1); 394*88eb404cSKevin Li 395*88eb404cSKevin Li ret = dma_coerce_mask_and_coherent(pcm->card->dev, DMA_BIT_MASK(32)); 396*88eb404cSKevin Li if (ret) 397*88eb404cSKevin Li goto out; 398*88eb404cSKevin Li 399*88eb404cSKevin Li if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) { 400*88eb404cSKevin Li ret = bcm63xx_pcm_preallocate_dma_buffer(pcm, 401*88eb404cSKevin Li SNDRV_PCM_STREAM_PLAYBACK); 402*88eb404cSKevin Li if (ret) 403*88eb404cSKevin Li goto out; 404*88eb404cSKevin Li 405*88eb404cSKevin Li i2s_priv->play_substream = 406*88eb404cSKevin Li pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; 407*88eb404cSKevin Li } 408*88eb404cSKevin Li 409*88eb404cSKevin Li if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { 410*88eb404cSKevin Li ret = bcm63xx_pcm_preallocate_dma_buffer(pcm, 411*88eb404cSKevin Li SNDRV_PCM_STREAM_CAPTURE); 412*88eb404cSKevin Li if (ret) 413*88eb404cSKevin Li goto out; 414*88eb404cSKevin Li i2s_priv->capture_substream = 415*88eb404cSKevin Li pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; 416*88eb404cSKevin Li } 417*88eb404cSKevin Li 418*88eb404cSKevin Li out: 419*88eb404cSKevin Li return ret; 420*88eb404cSKevin Li } 421*88eb404cSKevin Li 422*88eb404cSKevin Li static void bcm63xx_pcm_free_dma_buffers(struct snd_soc_component *component, 423*88eb404cSKevin Li struct snd_pcm *pcm) 424*88eb404cSKevin Li { 425*88eb404cSKevin Li int stream; 426*88eb404cSKevin Li struct snd_dma_buffer *buf; 427*88eb404cSKevin Li struct snd_pcm_substream *substream; 428*88eb404cSKevin Li 429*88eb404cSKevin Li for (stream = 0; stream < 2; stream++) { 430*88eb404cSKevin Li substream = pcm->streams[stream].substream; 431*88eb404cSKevin Li if (!substream) 432*88eb404cSKevin Li continue; 433*88eb404cSKevin Li buf = &substream->dma_buffer; 434*88eb404cSKevin Li if (!buf->area) 435*88eb404cSKevin Li continue; 436*88eb404cSKevin Li dma_free_wc(pcm->card->dev, buf->bytes, 437*88eb404cSKevin Li buf->area, buf->addr); 438*88eb404cSKevin Li buf->area = NULL; 439*88eb404cSKevin Li } 440*88eb404cSKevin Li } 441*88eb404cSKevin Li 442*88eb404cSKevin Li static const struct snd_soc_component_driver bcm63xx_soc_platform = { 443*88eb404cSKevin Li .open = bcm63xx_pcm_open, 444*88eb404cSKevin Li .close = bcm63xx_pcm_close, 445*88eb404cSKevin Li .hw_params = bcm63xx_pcm_hw_params, 446*88eb404cSKevin Li .hw_free = bcm63xx_pcm_hw_free, 447*88eb404cSKevin Li .prepare = bcm63xx_pcm_prepare, 448*88eb404cSKevin Li .trigger = bcm63xx_pcm_trigger, 449*88eb404cSKevin Li .pointer = bcm63xx_pcm_pointer, 450*88eb404cSKevin Li .mmap = bcm63xx_pcm_mmap, 451*88eb404cSKevin Li .pcm_construct = bcm63xx_soc_pcm_new, 452*88eb404cSKevin Li .pcm_destruct = bcm63xx_pcm_free_dma_buffers, 453*88eb404cSKevin Li }; 454*88eb404cSKevin Li 455*88eb404cSKevin Li int bcm63xx_soc_platform_probe(struct platform_device *pdev, 456*88eb404cSKevin Li struct bcm_i2s_priv *i2s_priv) 457*88eb404cSKevin Li { 458*88eb404cSKevin Li int ret; 459*88eb404cSKevin Li 460*88eb404cSKevin Li i2s_priv->r_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); 461*88eb404cSKevin Li if (!i2s_priv->r_irq) { 462*88eb404cSKevin Li dev_err(&pdev->dev, "Unable to get register irq resource.\n"); 463*88eb404cSKevin Li return -ENODEV; 464*88eb404cSKevin Li } 465*88eb404cSKevin Li 466*88eb404cSKevin Li ret = devm_request_irq(&pdev->dev, i2s_priv->r_irq->start, i2s_dma_isr, 467*88eb404cSKevin Li i2s_priv->r_irq->flags, "i2s_dma", (void *)i2s_priv); 468*88eb404cSKevin Li if (ret) { 469*88eb404cSKevin Li dev_err(&pdev->dev, 470*88eb404cSKevin Li "i2s_init: failed to request interrupt.ret=%d\n", ret); 471*88eb404cSKevin Li return ret; 472*88eb404cSKevin Li } 473*88eb404cSKevin Li 474*88eb404cSKevin Li return devm_snd_soc_register_component(&pdev->dev, 475*88eb404cSKevin Li &bcm63xx_soc_platform, NULL, 0); 476*88eb404cSKevin Li } 477*88eb404cSKevin Li 478*88eb404cSKevin Li int bcm63xx_soc_platform_remove(struct platform_device *pdev) 479*88eb404cSKevin Li { 480*88eb404cSKevin Li return 0; 481*88eb404cSKevin Li } 482*88eb404cSKevin Li 483*88eb404cSKevin Li MODULE_AUTHOR("Kevin,Li <kevin-ke.li@broadcom.com>"); 484*88eb404cSKevin Li MODULE_DESCRIPTION("Broadcom DSL XPON ASOC PCM Interface"); 485*88eb404cSKevin Li MODULE_LICENSE("GPL v2"); 486