if (sai->is_master_mode || sai->version < SAI_VER_2311) return 0;
regmap_read(sai->regmap, SAI_FSCR, &fw);
cnt = SAI_FSCR_FW_V(fw) << 1; /* two fsync lost */
regmap_update_bits(sai->regmap, SAI_INTCR,
SAI_INTCR_FSLOSTC, SAI_INTCR_FSLOSTC);
regmap_update_bits(sai->regmap, SAI_INTCR,
SAI_INTCR_FSLOST_MASK,
SAI_INTCR_FSLOST(en)); /* * The `cnt` is the number of SCLK cycles of the CRU's SCLK signal that * should be used as timeout. Consequently, in slave mode, this value * is only correct if the CRU SCLK is equal to the external SCLK.
*/
regmap_update_bits(sai->regmap, SAI_FS_TIMEOUT,
SAI_FS_TIMEOUT_VAL_MASK | SAI_FS_TIMEOUT_EN_MASK,
SAI_FS_TIMEOUT_VAL(cnt) | SAI_FS_TIMEOUT_EN(en));
if (sai->version >= SAI_VER_2307) {
reg = SAI_STATUS; if (playback)
idle |= SAI_STATUS_TX_IDLE; if (capture)
idle |= SAI_STATUS_RX_IDLE;
idle = sai->version >= SAI_VER_2311 ? idle >> 1 : idle;
} else {
reg = SAI_XFER; if (playback)
idle |= SAI_XFER_TX_IDLE; if (capture)
idle |= SAI_XFER_RX_IDLE;
}
ret = regmap_read_poll_timeout_atomic(sai->regmap, reg, val,
(val & idle), 10, TIMEOUT_US); if (ret < 0)
dev_warn(sai->dev, "Failed to idle stream\n");
return ret;
}
/** * rockchip_sai_xfer_clk_stop_and_wait() - stop the xfer clock and wait for it to be idle * @sai: pointer to the driver instance's rk_sai_dev struct * @to_restore: pointer to store the CLK/FSS register values in as they were * found before they were cleared, or NULL. * * Clear the XFER_CLK and XFER_FSS registers if needed, then busy-waits for the * XFER clocks to be idle. Before clearing the bits, it stores the state of the * registers as it encountered them in to_restore if it isn't NULL. * * Context: Any context. Expects sai->xfer_lock to be held by caller.
*/ staticvoid rockchip_sai_xfer_clk_stop_and_wait(struct rk_sai_dev *sai, unsignedint *to_restore)
{ unsignedint mask = SAI_XFER_CLK_MASK | SAI_XFER_FSS_MASK; unsignedint disable = SAI_XFER_CLK_DIS | SAI_XFER_FSS_DIS; unsignedint val;
regcache_cache_only(sai->regmap, true); /* * After FS is idle, we should wait at least 2 BCLK cycles to make sure * the CLK gate operation has completed, and only then disable mclk. * * Otherwise, the BCLK is still ungated, and once the mclk is enabled, * there is a risk that a few BCLK cycles leak. This is true especially * at low speeds, such as with a samplerate of 8k. * * Ideally we'd adjust the delay based on the samplerate, but it's such * a tiny value that we can just delay for the maximum clock period * for the sake of simplicity. * * The maximum BCLK period is 31us @ 8K-8Bit (64kHz BCLK). We wait for * 40us to give ourselves a safety margin in case udelay falls short.
*/
udelay(40);
clk_disable_unprepare(sai->mclk);
clk_disable_unprepare(sai->hclk);
if (en)
rockchip_sai_fifo_xrun_detect(sai, stream, 1);
}
staticvoid rockchip_sai_reset(struct rk_sai_dev *sai)
{ /* * It is advised to reset the hclk domain before resetting the mclk * domain, especially in slave mode without a clock input. * * To deal with the aforementioned case of slave mode without a clock * input, we work around a potential issue by resetting the whole * controller, bringing it back into master mode, and then recovering * the controller configuration in the regmap.
*/
reset_control_assert(sai->rst_h);
udelay(10);
reset_control_deassert(sai->rst_h);
udelay(10);
reset_control_assert(sai->rst_m);
udelay(10);
reset_control_deassert(sai->rst_m);
udelay(10);
switch (params_format(params)) { case SNDRV_PCM_FORMAT_S8: case SNDRV_PCM_FORMAT_U8:
val = SAI_XCR_VDW(8); break; case SNDRV_PCM_FORMAT_S16_LE:
val = SAI_XCR_VDW(16); break; case SNDRV_PCM_FORMAT_S24_LE:
val = SAI_XCR_VDW(24); break; case SNDRV_PCM_FORMAT_S32_LE: case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE:
val = SAI_XCR_VDW(32); break; default:
ret = -EINVAL; goto err_pm_put;
}
if (!rockchip_sai_stream_valid(substream, dai)) return 0;
if (sai->is_master_mode) { /* * We should wait for the first BCLK pulse to have definitely * occurred after any DIV settings have potentially been * changed in order to guarantee a clean clock signal once we * ungate the clock. * * Ideally, this would be done depending on the samplerate, but * for the sake of simplicity, we'll just delay for the maximum * possible clock offset time, which is quite a small value. * * The maximum BCLK offset is 15.6us @ 8K-8Bit (64kHz BCLK). We * wait for 20us in order to give us a safety margin in case * udelay falls short.
*/
udelay(20);
spin_lock_irqsave(&sai->xfer_lock, flags);
regmap_update_bits(sai->regmap, SAI_XFER,
SAI_XFER_CLK_MASK |
SAI_XFER_FSS_MASK,
SAI_XFER_CLK_EN |
SAI_XFER_FSS_EN);
spin_unlock_irqrestore(&sai->xfer_lock, flags);
}
staticvoid rockchip_sai_path_config(struct rk_sai_dev *sai, int num, bool is_rx)
{ int i;
if (is_rx) for (i = 0; i < num; i++)
regmap_update_bits(sai->regmap, SAI_PATH_SEL,
SAI_RX_PATH_MASK(i),
SAI_RX_PATH(i, sai->sdi[i])); else for (i = 0; i < num; i++)
regmap_update_bits(sai->regmap, SAI_PATH_SEL,
SAI_TX_PATH_MASK(i),
SAI_TX_PATH(i, sai->sdo[i]));
}
staticint rockchip_sai_path_prepare(struct rk_sai_dev *sai, struct device_node *np, bool is_rx)
{ constchar *path_prop; unsignedint *data; unsignedint *lanes; int i, num, ret;
if (is_rx) {
path_prop = "rockchip,sai-rx-route";
data = sai->sdi;
lanes = &sai->rx_lanes;
} else {
path_prop = "rockchip,sai-tx-route";
data = sai->sdo;
lanes = &sai->tx_lanes;
}
num = of_count_phandle_with_args(np, path_prop, NULL); if (num == -ENOENT) { return 0;
} elseif (num > MAX_LANES || num == 0) {
dev_err(sai->dev, "found %d entries in %s, outside of range 1 to %d\n",
num, path_prop, MAX_LANES); return -EINVAL;
} elseif (num < 0) {
dev_err(sai->dev, "error in %s property: %pe\n", path_prop,
ERR_PTR(num)); return num;
}
*lanes = num;
ret = device_property_read_u32_array(sai->dev, path_prop, data, num); if (ret < 0) {
dev_err(sai->dev, "failed to read property '%s': %pe\n",
path_prop, ERR_PTR(ret)); return ret;
}
for (i = 0; i < num; i++) { if (data[i] >= MAX_LANES) {
dev_err(sai->dev, "%s[%d] is %d, should be less than %d\n",
path_prop, i, data[i], MAX_LANES); return -EINVAL;
}
}
rockchip_sai_path_config(sai, num, is_rx);
return 0;
}
staticint rockchip_sai_parse_paths(struct rk_sai_dev *sai, struct device_node *np)
{ int ret;
if (sai->has_playback) {
sai->tx_lanes = 1;
ret = rockchip_sai_path_prepare(sai, np, false); if (ret < 0) {
dev_err(sai->dev, "Failed to prepare TX path: %pe\n",
ERR_PTR(ret)); return ret;
}
}
if (sai->has_capture) {
sai->rx_lanes = 1;
ret = rockchip_sai_path_prepare(sai, np, true); if (ret < 0) {
dev_err(sai->dev, "Failed to prepare RX path: %pe\n",
ERR_PTR(ret)); return ret;
}
}
return 0;
}
staticint rockchip_sai_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai)
{ struct rk_sai_dev *sai = snd_soc_dai_get_drvdata(dai); int ret = 0;
if (!rockchip_sai_stream_valid(substream, dai)) return 0;
switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
rockchip_sai_start(sai, substream->stream); break; case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
rockchip_sai_stop(sai, substream->stream); break; default:
ret = -EINVAL; break;
}
staticbool rockchip_sai_wr_reg(struct device *dev, unsignedint reg)
{ switch (reg) { case SAI_TXCR: case SAI_FSCR: case SAI_RXCR: case SAI_MONO_CR: case SAI_XFER: case SAI_CLR: case SAI_CKR: case SAI_DMACR: case SAI_INTCR: case SAI_TXDR: case SAI_PATH_SEL: case SAI_TX_SLOT_MASK0: case SAI_TX_SLOT_MASK1: case SAI_TX_SLOT_MASK2: case SAI_TX_SLOT_MASK3: case SAI_RX_SLOT_MASK0: case SAI_RX_SLOT_MASK1: case SAI_RX_SLOT_MASK2: case SAI_RX_SLOT_MASK3: case SAI_TX_SHIFT: case SAI_RX_SHIFT: case SAI_FSXN: case SAI_FS_TIMEOUT: case SAI_LOOPBACK_LR: returntrue; default: returnfalse;
}
}
staticbool rockchip_sai_rd_reg(struct device *dev, unsignedint reg)
{ switch (reg) { case SAI_TXCR: case SAI_FSCR: case SAI_RXCR: case SAI_MONO_CR: case SAI_XFER: case SAI_CLR: case SAI_CKR: case SAI_TXFIFOLR: case SAI_RXFIFOLR: case SAI_DMACR: case SAI_INTCR: case SAI_INTSR: case SAI_TXDR: case SAI_RXDR: case SAI_PATH_SEL: case SAI_TX_SLOT_MASK0: case SAI_TX_SLOT_MASK1: case SAI_TX_SLOT_MASK2: case SAI_TX_SLOT_MASK3: case SAI_RX_SLOT_MASK0: case SAI_RX_SLOT_MASK1: case SAI_RX_SLOT_MASK2: case SAI_RX_SLOT_MASK3: case SAI_TX_DATA_CNT: case SAI_RX_DATA_CNT: case SAI_TX_SHIFT: case SAI_RX_SHIFT: case SAI_STATUS: case SAI_VERSION: case SAI_FSXN: case SAI_FS_TIMEOUT: case SAI_LOOPBACK_LR: returntrue; default: returnfalse;
}
}
staticbool rockchip_sai_volatile_reg(struct device *dev, unsignedint reg)
{ switch (reg) { case SAI_XFER: case SAI_INTCR: case SAI_INTSR: case SAI_CLR: case SAI_TXFIFOLR: case SAI_RXFIFOLR: case SAI_TXDR: case SAI_RXDR: case SAI_TX_DATA_CNT: case SAI_RX_DATA_CNT: case SAI_STATUS: case SAI_VERSION: returntrue; default: returnfalse;
}
}
sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL); if (!sai) return -ENOMEM;
sai->dev = &pdev->dev;
sai->fw_ratio = 1; /* match to register default */
sai->is_master_mode = true;
dev_set_drvdata(&pdev->dev, sai);
spin_lock_init(&sai->xfer_lock);
sai->rst_h = devm_reset_control_get_optional_exclusive(&pdev->dev, "h"); if (IS_ERR(sai->rst_h)) return dev_err_probe(&pdev->dev, PTR_ERR(sai->rst_h), "Error in 'h' reset control\n");
sai->rst_m = devm_reset_control_get_optional_exclusive(&pdev->dev, "m"); if (IS_ERR(sai->rst_m)) return dev_err_probe(&pdev->dev, PTR_ERR(sai->rst_m), "Error in 'm' reset control\n");
regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(regs)) return dev_err_probe(&pdev->dev, PTR_ERR(regs), "Failed to get and ioremap resource\n");
sai->regmap = devm_regmap_init_mmio(&pdev->dev, regs,
&rockchip_sai_regmap_config); if (IS_ERR(sai->regmap)) return dev_err_probe(&pdev->dev, PTR_ERR(sai->regmap), "Failed to initialize regmap\n");
irq = platform_get_irq_optional(pdev, 0); if (irq > 0) {
ret = devm_request_irq(&pdev->dev, irq, rockchip_sai_isr,
IRQF_SHARED, node->name, sai); if (ret) return dev_err_probe(&pdev->dev, ret, "Failed to request irq %d\n", irq);
} else {
dev_dbg(&pdev->dev, "Asked for an IRQ but got %d\n", irq);
}
sai->mclk = devm_clk_get(&pdev->dev, "mclk"); if (IS_ERR(sai->mclk)) return dev_err_probe(&pdev->dev, PTR_ERR(sai->mclk), "Failed to get mclk\n");
sai->hclk = devm_clk_get_enabled(&pdev->dev, "hclk"); if (IS_ERR(sai->hclk)) return dev_err_probe(&pdev->dev, PTR_ERR(sai->hclk), "Failed to get hclk\n");
ret = rockchip_sai_init_dai(sai, res, &dai); if (ret) return dev_err_probe(&pdev->dev, ret, "Failed to initialize DAI\n");
ret = rockchip_sai_parse_paths(sai, node); if (ret) return dev_err_probe(&pdev->dev, ret, "Failed to parse paths\n");
/* * From here on, all register accesses need to be wrapped in * pm_runtime_get_sync/pm_runtime_put calls * * NB: we don't rely on _resume_and_get in case of !CONFIG_PM
*/
devm_pm_runtime_enable(&pdev->dev);
pm_runtime_get_noresume(&pdev->dev);
ret = rockchip_sai_runtime_resume(&pdev->dev); if (ret) return dev_err_probe(&pdev->dev, ret, "Failed to resume device\n");
ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); if (ret) {
dev_err(&pdev->dev, "Failed to register PCM: %d\n", ret); goto err_runtime_suspend;
}
ret = devm_snd_soc_register_component(&pdev->dev,
&rockchip_sai_component,
dai, 1); if (ret) {
dev_err(&pdev->dev, "Failed to register component: %d\n", ret); goto err_runtime_suspend;
}
err_runtime_suspend: /* If we're !CONFIG_PM, we get -ENOSYS and disable manually */ if (pm_runtime_put(&pdev->dev))
rockchip_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.0.8Bemerkung:
(vorverarbeitet)
¤
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.