static int bcm947xx_pcm_open(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; bcm947xx_i2s_info_t *snd_bcm = rtd->dai->cpu_dai->private_data; struct snd_pcm_runtime *runtime = substream->runtime; struct bcm947xx_runtime_data *brtd; int ret; DBG("%s %s\n", __FUNCTION__, bcm947xx_direction_str(substream)); if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { snd_soc_set_runtime_hwparams(substream, &bcm947xx_pcm_hardware_capture); } else { snd_soc_set_runtime_hwparams(substream, &bcm947xx_pcm_hardware_playback); } if (snd_bcm->in_use & ~(1 << substream->stream)) { /* Another stream is in-use; set our constraints to match. */ if ((ret = bcm947xx_pcm_set_constraints(runtime, snd_bcm)) < 0) return ret; } brtd = kzalloc(sizeof(struct bcm947xx_runtime_data), GFP_KERNEL); if (brtd == NULL) { return -ENOMEM; } spin_lock_init(&brtd->lock); runtime->private_data = brtd; /* probably should put this somewhere else, after setting up isr ??? */ /* Enable appropriate channel. */ /* Channel dma_XXXinit needs to be called before descriptors can be * posted to the DMA. Posting currently occurs in the copy operator, * which is called after prepare but before trigger(start). */ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { dma_rxreset(snd_bcm->di[0]); dma_rxinit(snd_bcm->di[0]); } else { dma_txreset(snd_bcm->di[0]); dma_txinit(snd_bcm->di[0]); if (BCM947XX_DMA_LOOPBACK_ENABLED) dma_fifoloopbackenable(snd_bcm->di[0]); // dma_txsuspend(snd_bcm->di[0]); } if (BCM947XX_PCM_DEBUG_ON) { DBG("%s: i2s devcontrol 0x%x devstatus 0x%x\n", __FUNCTION__, R_REG(snd_bcm->osh, &snd_bcm->regs->devcontrol), R_REG(snd_bcm->osh, &snd_bcm->regs->devstatus)); DBG("%s: i2s intstatus 0x%x intmask 0x%x\n", __FUNCTION__, R_REG(snd_bcm->osh, &snd_bcm->regs->intstatus), R_REG(snd_bcm->osh, &snd_bcm->regs->intmask)); DBG("%s: i2s control 0x%x\n", __FUNCTION__, R_REG(snd_bcm->osh, &snd_bcm->regs->i2scontrol)); DBG("%s: i2s clkdivider 0x%x txplayth 0x%x\n", __FUNCTION__, R_REG(snd_bcm->osh, &snd_bcm->regs->clkdivider), R_REG(snd_bcm->osh, &snd_bcm->regs->txplayth)); DBG("%s: i2s stxctrl 0x%x\n", __FUNCTION__, R_REG(snd_bcm->osh, &snd_bcm->regs->stxctrl)); { uint32 temp; temp = R_REG(snd_bcm->osh, &snd_bcm->regs->fifocounter); DBG("%s: i2s txcnt 0x%x rxcnt 0x%x\n", __FUNCTION__, (temp & I2S_FC_TX_CNT_MASK)>> I2S_FC_TX_CNT_SHIFT, (temp & I2S_FC_RX_CNT_MASK)>> I2S_FC_RX_CNT_SHIFT); } } return 0; }
/* Maybe called from snd_period_elapsed. */ static int bcm947xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_soc_pcm_runtime *rtd = substream->private_data; bcm947xx_i2s_info_t *snd_bcm = rtd->dai->cpu_dai->private_data; struct snd_pcm_runtime *runtime = substream->runtime; struct bcm947xx_runtime_data *brtd = runtime->private_data; uint32 intmask = R_REG(snd_bcm->osh, &snd_bcm->regs->intmask); int ret = 0; DBG("%s %s w/cmd=%d\n", __FUNCTION__, bcm947xx_direction_str(substream), cmd); spin_lock(&brtd->lock); switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: case SNDRV_PCM_TRIGGER_RESUME: if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { brtd->bytes_pending = snd_pcm_lib_buffer_bytes(substream); bcm947xx_pcm_enqueue(substream); } else { // dma_txresume(snd_bcm->di[0]); } break; case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { // dma_txsuspend(snd_bcm->di[0]); break; } /* fall-thru */ case SNDRV_PCM_TRIGGER_STOP: /* Reset the disable interrupts, DMA RX/TX channel. Might get here as a result of calling snd_pcm_period_elapsed. */ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { intmask &= ~intmask_capture; W_REG(snd_bcm->osh, &snd_bcm->regs->intmask, intmask); dma_rxreset(snd_bcm->di[0]); dma_rxreclaim(snd_bcm->di[0]); dma_rxinit(snd_bcm->di[0]); } else { /* Disable transmit interrupts. */ intmask &= ~intmask_playback; W_REG(snd_bcm->osh, &snd_bcm->regs->intmask, intmask); dma_txreset(snd_bcm->di[0]); dma_txreclaim(snd_bcm->di[0], HNDDMA_RANGE_ALL); dma_txinit(snd_bcm->di[0]); if (BCM947XX_DMA_LOOPBACK_ENABLED) dma_fifoloopbackenable(snd_bcm->di[0]); // dma_txsuspend(snd_bcm->di[0]); } break; default: ret = -EINVAL; } spin_unlock(&brtd->lock); DBG("%s: i2s intstatus 0x%x intmask 0x%x\n", __FUNCTION__, R_REG(snd_bcm->osh, &snd_bcm->regs->intstatus), R_REG(snd_bcm->osh, &snd_bcm->regs->intmask)); return ret; }
/* * Initialize all the chip registers. If dma mode, init tx and rx dma engines * but leave the devcontrol tx and rx (fifos) disabled. */ static void chipinit(struct bcm4xxx *ch, uint options) { etc_info_t *etc; bcmenetregs_t *regs; uint idx; uint i; regs = ch->regs; etc = ch->etc; idx = 0; ET_TRACE(("et%d: chipinit\n", etc->unit)); /* enable crc32 generation */ OR_REG(ch->osh, ®s->emaccontrol, EMC_CG); /* enable one rx interrupt per received frame */ W_REG(ch->osh, ®s->intrecvlazy, (1 << IRL_FC_SHIFT)); /* enable 802.3x tx flow control (honor received PAUSE frames) */ W_REG(ch->osh, ®s->rxconfig, ERC_FE | ERC_UF); /* initialize CAM */ if (etc->promisc || (R_REG(ch->osh, ®s->rxconfig) & ERC_CA)) OR_REG(ch->osh, ®s->rxconfig, ERC_PE); else { /* our local address */ chipwrcam(ch, &etc->cur_etheraddr, idx++); /* allmulti or a list of discrete multicast addresses */ if (etc->allmulti) OR_REG(ch->osh, ®s->rxconfig, ERC_AM); else if (etc->nmulticast) { for (i = 0; i < etc->nmulticast; i++) chipwrcam(ch, &etc->multicast[i], idx++); } /* enable cam */ OR_REG(ch->osh, ®s->camcontrol, CC_CE); } /* optionally enable mac-level loopback */ if (etc->loopbk) OR_REG(ch->osh, ®s->rxconfig, ERC_LE); /* set max frame lengths - account for possible vlan tag */ W_REG(ch->osh, ®s->rxmaxlength, ETHER_MAX_LEN + 32); W_REG(ch->osh, ®s->txmaxlength, ETHER_MAX_LEN + 32); /* set tx watermark */ W_REG(ch->osh, ®s->txwatermark, 56); /* * Optionally, disable phy autonegotiation and force our speed/duplex * or constrain our advertised capabilities. */ if (etc->forcespeed != ET_AUTO) chipphyforce(ch, etc->phyaddr); else if (etc->advertise && etc->needautoneg) chipphyadvertise(ch, etc->phyaddr); if (options & ET_INIT_FULL) { /* initialize the tx and rx dma channels */ dma_txinit(ch->di); dma_rxinit(ch->di); /* post dma receive buffers */ dma_rxfill(ch->di); /* lastly, enable interrupts */ if (options & ET_INIT_INTRON) et_intrson(etc->et); } else dma_rxenable(ch->di); /* turn on the emac */ OR_REG(ch->osh, ®s->enetcontrol, EC_EE); }