/** * fsl_sai_dir_is_synced - Check if stream is synced by the opposite stream * * SAI supports synchronous mode using bit/frame clocks of either Transmitter's * or Receiver's for both streams. This function is used to check if clocks of * the stream's are synced by the opposite stream. * * @sai: SAI context * @dir: stream direction
*/ staticinlinebool fsl_sai_dir_is_synced(struct fsl_sai *sai, int dir)
{ int adir = (dir == TX) ? RX : TX;
/* current dir in async mode while opposite dir in sync mode */ return !sai->synchronous[dir] && sai->synchronous[adir];
}
if (sai->is_pdm_mode) { /* DSD512@44.1kHz, DSD512@48kHz */ if (bclk >= 22579200)
state = pinctrl_lookup_state(sai->pinctrl, "dsd512");
/* Get default DSD state */ if (IS_ERR_OR_NULL(state))
state = pinctrl_lookup_state(sai->pinctrl, "dsd");
} else { /* 706k32b2c, 768k32b2c, etc */ if (bclk >= 45158400)
state = pinctrl_lookup_state(sai->pinctrl, "pcm_b2m");
}
/* Get default state */ if (IS_ERR_OR_NULL(state))
state = pinctrl_lookup_state(sai->pinctrl, "default");
/* * Both IRQ status bits and IRQ mask bits are in the xCSR but * different shifts. And we here create a mask only for those * IRQs that we activated.
*/
mask = (FSL_SAI_FLAGS >> FSL_SAI_CSR_xIE_SHIFT) << FSL_SAI_CSR_xF_SHIFT;
staticint fsl_sai_xlate_tdm_slot_mask(unsignedint slots, unsignedint *tx_mask, unsignedint *rx_mask)
{ /* Leave it empty, don't change the value of tx_mask and rx_mask */ return 0;
}
if (!sai->is_lsb_first)
val_cr4 |= FSL_SAI_CR4_MF;
sai->is_pdm_mode = false;
sai->is_dsp_mode[tx] = false; /* DAI mode */ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: /* * Frame low, 1clk before data, one word length for frame sync, * frame sync starts one serial clock cycle earlier, * that is, together with the last bit of the previous * data word.
*/
val_cr2 |= FSL_SAI_CR2_BCP;
val_cr4 |= FSL_SAI_CR4_FSE | FSL_SAI_CR4_FSP; break; case SND_SOC_DAIFMT_LEFT_J: /* * Frame high, one word length for frame sync, * frame sync asserts with the first bit of the frame.
*/
val_cr2 |= FSL_SAI_CR2_BCP; break; case SND_SOC_DAIFMT_DSP_A: /* * Frame high, 1clk before data, one bit for frame sync, * frame sync starts one serial clock cycle earlier, * that is, together with the last bit of the previous * data word.
*/
val_cr2 |= FSL_SAI_CR2_BCP;
val_cr4 |= FSL_SAI_CR4_FSE;
sai->is_dsp_mode[tx] = true; break; case SND_SOC_DAIFMT_DSP_B: /* * Frame high, one bit for frame sync, * frame sync asserts with the first bit of the frame.
*/
val_cr2 |= FSL_SAI_CR2_BCP;
sai->is_dsp_mode[tx] = true; break; case SND_SOC_DAIFMT_PDM:
val_cr2 |= FSL_SAI_CR2_BCP;
sai->is_pdm_mode = true; break; case SND_SOC_DAIFMT_RIGHT_J: /* To be done */ default: return -EINVAL;
}
/* DAI clock inversion */ switch (fmt & SND_SOC_DAIFMT_INV_MASK) { case SND_SOC_DAIFMT_IB_IF: /* Invert both clocks */
val_cr2 ^= FSL_SAI_CR2_BCP;
val_cr4 ^= FSL_SAI_CR4_FSP; break; case SND_SOC_DAIFMT_IB_NF: /* Invert bit clock */
val_cr2 ^= FSL_SAI_CR2_BCP; break; case SND_SOC_DAIFMT_NB_IF: /* Invert frame clock */
val_cr4 ^= FSL_SAI_CR4_FSP; break; case SND_SOC_DAIFMT_NB_NF: /* Nothing to do for both normal cases */ break; default: return -EINVAL;
}
/* Don't apply to consumer mode */ if (sai->is_consumer_mode[tx]) return 0;
/* * There is no point in polling MCLK0 if it is identical to MCLK1. * And given that MQS use case has to use MCLK1 though two clocks * are the same, we simply skip MCLK0 and start to find from MCLK1.
*/
id = sai->soc_data->mclk0_is_mclk1 ? 1 : 0;
for (; id < FSL_SAI_MCLK_MAX; id++) { int diff;
clk_rate = clk_get_rate(sai->mclk_clk[id]); if (!clk_rate) continue;
ratio = DIV_ROUND_CLOSEST(clk_rate, freq); if (!ratio || ratio > 512) continue; if (ratio == 1 && !support_1_1_ratio) continue; if ((ratio & 1) && ratio > 1) continue;
diff = abs((long)clk_rate - ratio * freq);
/* * Drop the source that can not be * divided into the required rate.
*/ if (diff != 0 && clk_rate / diff < 1000) continue;
dev_dbg(dai->dev, "ratio %d for freq %dHz based on clock %ldHz\n",
ratio, freq, clk_rate);
/* * 1) For Asynchronous mode, we must set RCR2 register for capture, and * set TCR2 register for playback. * 2) For Tx sync with Rx clock, we must set RCR2 register for playback * and capture. * 3) For Rx sync with Tx clock, we must set TCR2 register for playback * and capture. * 4) For Tx and Rx are both Synchronous with another SAI, we just * ignore it.
*/ if (fsl_sai_dir_is_synced(sai, adir))
reg = FSL_SAI_xCR2(!tx, ofs); elseif (!sai->synchronous[dir])
reg = FSL_SAI_xCR2(tx, ofs); else return 0;
/* * PDM mode, channels are independent * each channels are on one dataline/FIFO.
*/ if (sai->is_pdm_mode) {
pins = channels;
dl_type = FSL_SAI_DL_PDM;
}
for (i = 0; i < dl_cfg_cnt; i++) { if (dl_cfg[i].type == dl_type && dl_cfg[i].pins[tx] == pins) {
dl_cfg_idx = i; break;
}
}
if (hweight8(dl_cfg[dl_cfg_idx].mask[tx]) < pins) {
dev_err(cpu_dai->dev, "channel not supported\n"); return -EINVAL;
}
if (!IS_ERR_OR_NULL(sai->pinctrl)) {
sai->pins_state = fsl_sai_get_pins_state(sai, bclk); if (!IS_ERR_OR_NULL(sai->pins_state)) {
ret = pinctrl_select_state(sai->pinctrl, sai->pins_state); if (ret) {
dev_err(cpu_dai->dev, "failed to set proper pins state: %d\n", ret); return ret;
}
}
}
if (!sai->is_consumer_mode[tx]) {
ret = fsl_sai_set_bclk(cpu_dai, tx, bclk); if (ret) return ret;
/* Do not enable the clock if it is already enabled */ if (!(sai->mclk_streams & BIT(substream->stream))) {
ret = clk_prepare_enable(sai->mclk_clk[sai->mclk_id[tx]]); if (ret) return ret;
sai->mclk_streams |= BIT(substream->stream);
}
}
if (!sai->is_dsp_mode[tx] && !sai->is_pdm_mode)
val_cr4 |= FSL_SAI_CR4_SYWD(slot_width);
/* Set to avoid channel swap */
val_cr4 |= FSL_SAI_CR4_FCONT;
/* Set to output mode to avoid tri-stated data pins */ if (tx)
val_cr4 |= FSL_SAI_CR4_CHMOD;
/* * When Tx(Rx) sync with Rx(Tx) clock, Rx(Tx) will provide bclk and * frame clock for Tx(Rx). We should set RCR4(TCR4), RCR5(TCR5) * for playback(capture), or there will be sync error.
*/
/* * Combine mode has limation: * - Can't used for singel dataline/FIFO case except the FIFO0 * - Can't used for multi dataline/FIFO case except the enabled FIFOs * are successive and start from FIFO0 * * So for common usage, all multi fifo case disable the combine mode.
*/ if (hweight8(dl_cfg[dl_cfg_idx].mask[tx]) <= 1 || sai->is_multi_fifo_dma)
regmap_update_bits(sai->regmap, FSL_SAI_xCR4(tx, ofs),
FSL_SAI_CR4_FCOMB_MASK, 0); else
regmap_update_bits(sai->regmap, FSL_SAI_xCR4(tx, ofs),
FSL_SAI_CR4_FCOMB_MASK, FSL_SAI_CR4_FCOMB_SOFT);
/* * When the TERE and FSD_MSTR enabled before configuring the word width * There will be no frame sync clock issue, because word width impact * the generation of frame sync clock. * * TERE enabled earlier only for i.MX8MP case for the hardware limitation, * We need to disable FSD_MSTR before configuring word width, then enable * FSD_MSTR bit for this specific case.
*/ if (sai->soc_data->mclk_with_tere && sai->mclk_direction_output &&
!sai->is_consumer_mode[tx])
regmap_update_bits(sai->regmap, FSL_SAI_xCR4(tx, ofs),
FSL_SAI_CR4_FSD_MSTR, 0);
/* TERE will remain set till the end of current frame */ do {
udelay(10);
regmap_read(sai->regmap, FSL_SAI_xCSR(tx, ofs), &xcsr);
} while (--count && xcsr & FSL_SAI_CSR_TERE);
/* * For sai master mode, after several open/close sai, * there will be no frame clock, and can't recover * anymore. Add software reset to fix this issue. * This is a hardware bug, and will be fix in the * next sai version. * * In consumer mode, this can happen even after a * single open/close, especially if both tx and rx * are running concurrently.
*/ /* Software Reset */
regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx, ofs), FSL_SAI_CSR_SR, FSL_SAI_CSR_SR); /* Clear SR bit to finish the reset */
regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx, ofs), FSL_SAI_CSR_SR, 0);
}
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; int adir = tx ? RX : TX; int dir = tx ? TX : RX;
u32 xcsr;
/* * Asynchronous mode: Clear SYNC for both Tx and Rx. * Rx sync with Tx clocks: Clear SYNC for Tx, set it for Rx. * Tx sync with Rx clocks: Clear SYNC for Rx, set it for Tx.
*/
regmap_update_bits(sai->regmap, FSL_SAI_TCR2(ofs), FSL_SAI_CR2_SYNC,
sai->synchronous[TX] ? FSL_SAI_CR2_SYNC : 0);
regmap_update_bits(sai->regmap, FSL_SAI_RCR2(ofs), FSL_SAI_CR2_SYNC,
sai->synchronous[RX] ? FSL_SAI_CR2_SYNC : 0);
/* * It is recommended that the transmitter is the last enabled * and the first disabled.
*/ switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx, ofs),
FSL_SAI_CSR_FRDE, FSL_SAI_CSR_FRDE);
regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx, ofs),
FSL_SAI_CSR_TERE, FSL_SAI_CSR_TERE); /* * Enable the opposite direction for synchronous mode * 1. Tx sync with Rx: only set RE for Rx; set TE & RE for Tx * 2. Rx sync with Tx: only set TE for Tx; set RE & TE for Rx * * RM recommends to enable RE after TE for case 1 and to enable * TE after RE for case 2, but we here may not always guarantee * that happens: "arecord 1.wav; aplay 2.wav" in case 1 enables * TE after RE, which is against what RM recommends but should * be safe to do, judging by years of testing results.
*/ if (fsl_sai_dir_is_synced(sai, adir))
regmap_update_bits(sai->regmap, FSL_SAI_xCSR((!tx), ofs),
FSL_SAI_CSR_TERE, FSL_SAI_CSR_TERE);
regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx, ofs),
FSL_SAI_CSR_xIE_MASK, FSL_SAI_FLAGS); break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx, ofs),
FSL_SAI_CSR_FRDE, 0);
regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx, ofs),
FSL_SAI_CSR_xIE_MASK, 0);
/* Check if the opposite FRDE is also disabled */
regmap_read(sai->regmap, FSL_SAI_xCSR(!tx, ofs), &xcsr);
/* * If opposite stream provides clocks for synchronous mode and * it is inactive, disable it before disabling the current one
*/ if (fsl_sai_dir_is_synced(sai, adir) && !(xcsr & FSL_SAI_CSR_FRDE))
fsl_sai_config_disable(sai, adir);
/* * Disable current stream if either of: * 1. current stream doesn't provide clocks for synchronous mode * 2. current stream provides clocks for synchronous mode but no * more stream is active.
*/ if (!fsl_sai_dir_is_synced(sai, dir) || !(xcsr & FSL_SAI_CSR_FRDE))
fsl_sai_config_disable(sai, dir);
/* * EDMA controller needs period size to be a multiple of * tx/rx maxburst
*/ if (sai->soc_data->use_edma)
snd_pcm_hw_constraint_step(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
tx ? sai->dma_params_tx.maxburst :
sai->dma_params_rx.maxburst);
ret = snd_pcm_hw_constraint_list(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_RATE, &sai->constraint_rates);
/* Software Reset for both Tx and Rx */
regmap_update_bits(sai->regmap, FSL_SAI_TCSR(ofs), FSL_SAI_CSR_SR, FSL_SAI_CSR_SR);
regmap_update_bits(sai->regmap, FSL_SAI_RCSR(ofs), FSL_SAI_CSR_SR, FSL_SAI_CSR_SR); /* Clear SR bit to finish the reset */
regmap_update_bits(sai->regmap, FSL_SAI_TCSR(ofs), FSL_SAI_CSR_SR, 0);
regmap_update_bits(sai->regmap, FSL_SAI_RCSR(ofs), FSL_SAI_CSR_SR, 0);
if (!IS_ERR_OR_NULL(sai->pinctrl) && !IS_ERR_OR_NULL(sai->pins_state)) {
ret = pinctrl_select_state(sai->pinctrl, sai->pins_state); if (ret) {
dev_err(dev, "failed to set proper pins state: %d\n", ret); return ret;
}
}
if (reg >= FSL_SAI_TCSR(ofs) && reg <= FSL_SAI_TCR5(ofs)) returntrue;
if (reg >= FSL_SAI_RCSR(ofs) && reg <= FSL_SAI_RCR5(ofs)) returntrue;
switch (reg) { case FSL_SAI_TFR0: case FSL_SAI_TFR1: case FSL_SAI_TFR2: case FSL_SAI_TFR3: case FSL_SAI_TFR4: case FSL_SAI_TFR5: case FSL_SAI_TFR6: case FSL_SAI_TFR7: case FSL_SAI_TMR: case FSL_SAI_RDR0: case FSL_SAI_RDR1: case FSL_SAI_RDR2: case FSL_SAI_RDR3: case FSL_SAI_RDR4: case FSL_SAI_RDR5: case FSL_SAI_RDR6: case FSL_SAI_RDR7: case FSL_SAI_RFR0: case FSL_SAI_RFR1: case FSL_SAI_RFR2: case FSL_SAI_RFR3: case FSL_SAI_RFR4: case FSL_SAI_RFR5: case FSL_SAI_RFR6: case FSL_SAI_RFR7: case FSL_SAI_RMR: case FSL_SAI_MCTL: case FSL_SAI_MDIV: case FSL_SAI_VERID: case FSL_SAI_PARAM: case FSL_SAI_TTCTN: case FSL_SAI_RTCTN: case FSL_SAI_TTCTL: case FSL_SAI_TBCTN: case FSL_SAI_TTCAP: case FSL_SAI_RTCTL: case FSL_SAI_RBCTN: case FSL_SAI_RTCAP: returntrue; default: returnfalse;
}
}
if (reg == FSL_SAI_TCSR(ofs) || reg == FSL_SAI_RCSR(ofs)) returntrue;
/* Set VERID and PARAM be volatile for reading value in probe */ if (ofs == 8 && (reg == FSL_SAI_VERID || reg == FSL_SAI_PARAM)) returntrue;
switch (reg) { case FSL_SAI_TFR0: case FSL_SAI_TFR1: case FSL_SAI_TFR2: case FSL_SAI_TFR3: case FSL_SAI_TFR4: case FSL_SAI_TFR5: case FSL_SAI_TFR6: case FSL_SAI_TFR7: case FSL_SAI_RFR0: case FSL_SAI_RFR1: case FSL_SAI_RFR2: case FSL_SAI_RFR3: case FSL_SAI_RFR4: case FSL_SAI_RFR5: case FSL_SAI_RFR6: case FSL_SAI_RFR7: case FSL_SAI_RDR0: case FSL_SAI_RDR1: case FSL_SAI_RDR2: case FSL_SAI_RDR3: case FSL_SAI_RDR4: case FSL_SAI_RDR5: case FSL_SAI_RDR6: case FSL_SAI_RDR7: returntrue; default: returnfalse;
}
}
if (reg >= FSL_SAI_TCSR(ofs) && reg <= FSL_SAI_TCR5(ofs)) returntrue;
if (reg >= FSL_SAI_RCSR(ofs) && reg <= FSL_SAI_RCR5(ofs)) returntrue;
switch (reg) { case FSL_SAI_TDR0: case FSL_SAI_TDR1: case FSL_SAI_TDR2: case FSL_SAI_TDR3: case FSL_SAI_TDR4: case FSL_SAI_TDR5: case FSL_SAI_TDR6: case FSL_SAI_TDR7: case FSL_SAI_TMR: case FSL_SAI_RMR: case FSL_SAI_MCTL: case FSL_SAI_MDIV: case FSL_SAI_TTCTL: case FSL_SAI_RTCTL: returntrue; default: returnfalse;
}
}
ret = regmap_read(sai->regmap, FSL_SAI_VERID, &val); if (ret < 0) return ret;
dev_dbg(dev, "VERID: 0x%016X\n", val);
sai->verid.version = val &
(FSL_SAI_VERID_MAJOR_MASK | FSL_SAI_VERID_MINOR_MASK);
sai->verid.version >>= FSL_SAI_VERID_MINOR_SHIFT;
sai->verid.feature = val & FSL_SAI_VERID_FEATURE_MASK;
ret = regmap_read(sai->regmap, FSL_SAI_PARAM, &val); if (ret < 0) return ret;
dev_dbg(dev, "PARAM: 0x%016X\n", val);
/* Max slots per frame, power of 2 */
sai->param.slot_num = 1 <<
((val & FSL_SAI_PARAM_SPF_MASK) >> FSL_SAI_PARAM_SPF_SHIFT);
/* Words per fifo, power of 2 */
sai->param.fifo_depth = 1 <<
((val & FSL_SAI_PARAM_WPF_MASK) >> FSL_SAI_PARAM_WPF_SHIFT);
/* Number of datalines implemented */
sai->param.dataline = val & FSL_SAI_PARAM_DLN_MASK;
return 0;
}
/* * Calculate the offset between first two datalines, don't * different offset in one case.
*/ staticunsignedint fsl_sai_calc_dl_off(unsignedlong dl_mask)
{ int fbidx, nbidx, offset;
/* * read the fsl,dataline property from dts file. * It has 3 value for each configuration, first one means the type: * I2S(1) or PDM(2), second one is dataline mask for 'rx', third one is * dataline mask for 'tx'. for example * * fsl,dataline = <1 0xff 0xff 2 0xff 0x11>, * * It means I2S type rx mask is 0xff, tx mask is 0xff, PDM type * rx mask is 0xff, tx mask is 0x11 (dataline 1 and 4 enabled). *
*/ staticint fsl_sai_read_dlcfg(struct fsl_sai *sai)
{ struct platform_device *pdev = sai->pdev; struct device_node *np = pdev->dev.of_node; struct device *dev = &pdev->dev; int ret, elems, i, index, num_cfg; char *propname = "fsl,dataline"; struct fsl_sai_dl_cfg *cfg; unsignedlong dl_mask; unsignedint soc_dl;
u32 rx, tx, type;
sai->bus_clk = devm_clk_get(dev, "bus"); /* Compatible with old DTB cases */ if (IS_ERR(sai->bus_clk) && PTR_ERR(sai->bus_clk) != -EPROBE_DEFER)
sai->bus_clk = devm_clk_get(dev, "sai"); if (IS_ERR(sai->bus_clk)) {
dev_err(dev, "failed to get bus clock: %ld\n",
PTR_ERR(sai->bus_clk)); /* -EPROBE_DEFER */ return PTR_ERR(sai->bus_clk);
}
for (i = 1; i < FSL_SAI_MCLK_MAX; i++) {
sprintf(tmp, "mclk%d", i);
sai->mclk_clk[i] = devm_clk_get(dev, tmp); if (IS_ERR(sai->mclk_clk[i])) {
dev_err(dev, "failed to get mclk%d clock: %ld\n",
i, PTR_ERR(sai->mclk_clk[i]));
sai->mclk_clk[i] = NULL;
}
}
if (sai->soc_data->mclk0_is_mclk1)
sai->mclk_clk[0] = sai->mclk_clk[1]; else
sai->mclk_clk[0] = sai->bus_clk;
/* Use Multi FIFO mode depending on the support from SDMA script */
ret = of_property_read_u32_array(np, "dmas", dmas, 4); if (!sai->soc_data->use_edma && !ret && dmas[2] == IMX_DMATYPE_MULTI_SAI)
sai->is_multi_fifo_dma = true;
/* read dataline mask for rx and tx*/
ret = fsl_sai_read_dlcfg(sai); if (ret < 0) {
dev_err(dev, "failed to read dlcfg %d\n", ret); return ret;
}
irq = platform_get_irq(pdev, 0); if (irq < 0) return irq;
ret = devm_request_irq(dev, irq, fsl_sai_isr, IRQF_SHARED,
np->name, sai); if (ret) {
dev_err(dev, "failed to claim irq %u\n", irq); return ret;
}
/* Sync Tx with Rx as default by following old DT binding */
sai->synchronous[RX] = true;
sai->synchronous[TX] = false;
sai->cpu_dai_drv[0].symmetric_rate = 1;
sai->cpu_dai_drv[0].symmetric_channels = 1;
sai->cpu_dai_drv[0].symmetric_sample_bits = 1;
if (of_property_read_bool(np, "fsl,sai-synchronous-rx") &&
of_property_read_bool(np, "fsl,sai-asynchronous")) { /* error out if both synchronous and asynchronous are present */
dev_err(dev, "invalid binding for synchronous mode\n"); return -EINVAL;
}
platform_set_drvdata(pdev, sai);
pm_runtime_enable(dev); if (!pm_runtime_enabled(dev)) {
ret = fsl_sai_runtime_resume(dev); if (ret) goto err_pm_disable;
}
ret = pm_runtime_resume_and_get(dev); if (ret < 0) goto err_pm_get_sync;
/* Get sai version */
ret = fsl_sai_check_version(dev); if (ret < 0)
dev_warn(dev, "Error reading SAI version: %d\n", ret);
/* Select MCLK direction */ if (sai->mclk_direction_output &&
sai->soc_data->max_register >= FSL_SAI_MCTL) {
regmap_update_bits(sai->regmap, FSL_SAI_MCTL,
FSL_SAI_MCTL_MCLK_EN, FSL_SAI_MCTL_MCLK_EN);
}
ret = pm_runtime_put_sync(dev); if (ret < 0 && ret != -ENOSYS) goto err_pm_get_sync;
/* * Register platform component before registering cpu dai for there * is not defer probe for platform component in snd_soc_add_pcm_runtime().
*/ if (sai->soc_data->use_imx_pcm) {
ret = imx_pcm_dma_init(pdev); if (ret) {
dev_err_probe(dev, ret, "PCM DMA init failed\n"); if (!IS_ENABLED(CONFIG_SND_SOC_IMX_PCM_DMA))
dev_err(dev, "Error: You must enable the imx-pcm-dma support!\n"); goto err_pm_get_sync;
}
} else {
ret = devm_snd_dmaengine_pcm_register(dev, NULL, 0); if (ret) {
dev_err_probe(dev, ret, "Registering PCM dmaengine failed\n"); goto err_pm_get_sync;
}
}
ret = devm_snd_soc_register_component(dev, &fsl_component,
sai->cpu_dai_drv, ARRAY_SIZE(fsl_sai_dai_template)); if (ret) goto err_pm_get_sync;
return ret;
err_pm_get_sync: if (!pm_runtime_status_suspended(dev))
fsl_sai_runtime_suspend(dev);
err_pm_disable:
pm_runtime_disable(dev);
return ret;
}
staticvoid fsl_sai_remove(struct platform_device *pdev)
{
pm_runtime_disable(&pdev->dev); if (!pm_runtime_status_suspended(&pdev->dev))
fsl_sai_runtime_suspend(&pdev->dev);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.