Loading sound/soc/renesas/rcar/msiof.c +131 −30 Original line number Diff line number Diff line Loading @@ -7,7 +7,7 @@ // /* * [NOTE] * [NOTE-CLOCK-MODE] * * This driver doesn't support Clock/Frame Provider Mode * Loading @@ -24,12 +24,64 @@ * Clock/Frame Consumer Mode. */ /* * [NOTE-RESET] * * MSIOF has TXRST/RXRST to reset FIFO, but it shouldn't be used during SYNC signal was asserted, * because it will be cause of HW issue. * * When MSIOF is used as Sound driver, this driver is assuming it is used as clock consumer mode * (= Codec is clock provider). This means, it can't control SYNC signal by itself. * * We need to use SW reset (= reset_control_xxx()) instead of TXRST/RXRST. */ /* * [NOTE-BOTH-SETTING] * * SITMDRn / SIRMDRn and some other registers should not be updated during working even though it * was not related the target direction (for example, do TX settings during RX is working), * otherwise it cause a FSERR. * * Setup both direction (Playback/Capture) in the same time. */ /* * [NOTE-R/L] * * The data of Captured might be R/L opposite. * * This driver is assuming MSIOF is used as Clock/Frame Consumer Mode, and there is a case that some * Codec (= Clock/Frame Provider) might output Clock/Frame before setup MSIOF. It depends on Codec * driver implementation. * * MSIOF will capture data without checking SYNC signal Hi/Low (= R/L). * * This means, if MSIOF RXE bit was set as 1 in case of SYNC signal was Hi (= R) timing, it will * start capture data since next SYNC low singla (= L). Because Linux assumes sound data is lined * up as R->L->R->L->..., the data R/L will be opposite. * * The only solution in this case is start CLK/SYNC *after* MSIOF settings, but it depends when and * how Codec driver start it. */ /* * [NOTE-FSERR] * * We can't remove all FSERR. * * Renesas have tried to minimize the occurrence of FSERR errors as much as possible, but * unfortunately we cannot remove them completely, because MSIOF might setup its register during * CLK/SYNC are inputed. It can be happen because MSIOF is working as Clock/Frame Consumer. */ #include <linux/module.h> #include <linux/of.h> #include <linux/of_dma.h> #include <linux/of_graph.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/reset.h> #include <linux/spi/sh_msiof.h> #include <sound/dmaengine_pcm.h> #include <sound/soc.h> Loading Loading @@ -60,10 +112,13 @@ struct msiof_priv { struct device *dev; struct snd_pcm_substream *substream[SNDRV_PCM_STREAM_LAST + 1]; struct reset_control *reset; spinlock_t lock; void __iomem *base; resource_size_t phy_addr; int count; /* for error */ int err_syc[SNDRV_PCM_STREAM_LAST + 1]; int err_ovf[SNDRV_PCM_STREAM_LAST + 1]; Loading Loading @@ -121,7 +176,7 @@ static int msiof_hw_start(struct snd_soc_component *component, /* * see * [NOTE] on top of this driver * [NOTE-CLOCK-MODE] on top of this driver */ /* * see Loading @@ -131,19 +186,38 @@ static int msiof_hw_start(struct snd_soc_component *component, * RX: Fig 109.15 */ /* reset errors */ priv->err_syc[substream->stream] = /* * Use reset_control_xx() instead of TXRST/RXRST. * see * [NOTE-RESET] */ if (!priv->count) reset_control_deassert(priv->reset); priv->count++; /* * Reset errors. ignore 1st FSERR * * see * [NOTE-FSERR] */ priv->err_syc[substream->stream] = -1; priv->err_ovf[substream->stream] = priv->err_udf[substream->stream] = 0; /* Start DMAC */ snd_dmaengine_pcm_trigger(substream, cmd); /* * setup both direction (Playback/Capture) in the same time. * see * above [NOTE-BOTH-SETTING] */ /* SITMDRx */ if (is_play) { val = SITMDR1_PCON | FIELD_PREP(SIMDR1_SYNCMD, SIMDR1_SYNCMD_LR) | SIMDR1_SYNCAC | SIMDR1_XXSTP; val = SITMDR1_PCON | SIMDR1_SYNCAC | SIMDR1_XXSTP | FIELD_PREP(SIMDR1_SYNCMD, SIMDR1_SYNCMD_LR); if (msiof_flag_has(priv, MSIOF_FLAGS_NEED_DELAY)) val |= FIELD_PREP(SIMDR1_DTDL, 1); Loading @@ -153,11 +227,9 @@ static int msiof_hw_start(struct snd_soc_component *component, msiof_write(priv, SITMDR2, val | FIELD_PREP(SIMDR2_GRP, 1)); msiof_write(priv, SITMDR3, val); } /* SIRMDRx */ else { val = FIELD_PREP(SIMDR1_SYNCMD, SIMDR1_SYNCMD_LR) | SIMDR1_SYNCAC; val = SIMDR1_SYNCAC | FIELD_PREP(SIMDR1_SYNCMD, SIMDR1_SYNCMD_LR); if (msiof_flag_has(priv, MSIOF_FLAGS_NEED_DELAY)) val |= FIELD_PREP(SIMDR1_DTDL, 1); Loading @@ -166,7 +238,11 @@ static int msiof_hw_start(struct snd_soc_component *component, val = FIELD_PREP(SIMDR2_BITLEN1, width - 1); msiof_write(priv, SIRMDR2, val | FIELD_PREP(SIMDR2_GRP, 1)); msiof_write(priv, SIRMDR3, val); } /* SIFCTR */ msiof_write(priv, SIFCTR, FIELD_PREP(SIFCTR_TFWM, SIFCTR_TFWM_1) | FIELD_PREP(SIFCTR_RFWM, SIFCTR_RFWM_1)); /* SIIER */ if (is_play) Loading @@ -183,10 +259,11 @@ static int msiof_hw_start(struct snd_soc_component *component, msiof_update(priv, SISTR, val, val); /* SICTR */ val = SICTR_TEDG | SICTR_REDG; if (is_play) val = SICTR_TXE | SICTR_TEDG; val |= SICTR_TXE; else val = SICTR_RXE | SICTR_REDG; val |= SICTR_RXE; msiof_update_and_wait(priv, SICTR, val, val, val); return 0; Loading @@ -207,9 +284,6 @@ static int msiof_hw_stop(struct snd_soc_component *component, val = SIIER_RDREQE | SIIER_RDMAE | SISTR_ERR_RX; msiof_update(priv, SIIER, val, 0); /* Stop DMAC */ snd_dmaengine_pcm_trigger(substream, cmd); /* SICTR */ if (is_play) val = SICTR_TXE; Loading @@ -217,6 +291,18 @@ static int msiof_hw_stop(struct snd_soc_component *component, val = SICTR_RXE; msiof_update_and_wait(priv, SICTR, val, 0, 0); /* Stop DMAC */ snd_dmaengine_pcm_trigger(substream, cmd); /* * Ignore 1st FSERR * * see * [NOTE-FSERR] */ if (priv->err_syc[substream->stream] < 0) priv->err_syc[substream->stream] = 0; /* indicate error status if exist */ if (priv->err_syc[substream->stream] || priv->err_ovf[substream->stream] || Loading @@ -227,6 +313,11 @@ static int msiof_hw_stop(struct snd_soc_component *component, priv->err_ovf[substream->stream], priv->err_udf[substream->stream]); priv->count--; if (!priv->count) reset_control_assert(priv->reset); return 0; } Loading Loading @@ -302,6 +393,9 @@ static struct snd_soc_dai_driver msiof_dai_driver = { .channels_max = 2, }, .ops = &msiof_dai_ops, .symmetric_rate = 1, .symmetric_channels = 1, .symmetric_sample_bits = 1, }; static struct snd_pcm_hardware msiof_pcm_hardware = { Loading Loading @@ -490,12 +584,19 @@ static int msiof_probe(struct platform_device *pdev) if (IS_ERR(priv->base)) return PTR_ERR(priv->base); priv->reset = devm_reset_control_get_exclusive(dev, NULL); if (IS_ERR(priv->reset)) return PTR_ERR(priv->reset); reset_control_assert(priv->reset); ret = devm_request_irq(dev, irq, msiof_interrupt, 0, dev_name(dev), priv); if (ret) return ret; priv->dev = dev; priv->phy_addr = res->start; priv->count = 0; spin_lock_init(&priv->lock); platform_set_drvdata(pdev, priv); Loading Loading
sound/soc/renesas/rcar/msiof.c +131 −30 Original line number Diff line number Diff line Loading @@ -7,7 +7,7 @@ // /* * [NOTE] * [NOTE-CLOCK-MODE] * * This driver doesn't support Clock/Frame Provider Mode * Loading @@ -24,12 +24,64 @@ * Clock/Frame Consumer Mode. */ /* * [NOTE-RESET] * * MSIOF has TXRST/RXRST to reset FIFO, but it shouldn't be used during SYNC signal was asserted, * because it will be cause of HW issue. * * When MSIOF is used as Sound driver, this driver is assuming it is used as clock consumer mode * (= Codec is clock provider). This means, it can't control SYNC signal by itself. * * We need to use SW reset (= reset_control_xxx()) instead of TXRST/RXRST. */ /* * [NOTE-BOTH-SETTING] * * SITMDRn / SIRMDRn and some other registers should not be updated during working even though it * was not related the target direction (for example, do TX settings during RX is working), * otherwise it cause a FSERR. * * Setup both direction (Playback/Capture) in the same time. */ /* * [NOTE-R/L] * * The data of Captured might be R/L opposite. * * This driver is assuming MSIOF is used as Clock/Frame Consumer Mode, and there is a case that some * Codec (= Clock/Frame Provider) might output Clock/Frame before setup MSIOF. It depends on Codec * driver implementation. * * MSIOF will capture data without checking SYNC signal Hi/Low (= R/L). * * This means, if MSIOF RXE bit was set as 1 in case of SYNC signal was Hi (= R) timing, it will * start capture data since next SYNC low singla (= L). Because Linux assumes sound data is lined * up as R->L->R->L->..., the data R/L will be opposite. * * The only solution in this case is start CLK/SYNC *after* MSIOF settings, but it depends when and * how Codec driver start it. */ /* * [NOTE-FSERR] * * We can't remove all FSERR. * * Renesas have tried to minimize the occurrence of FSERR errors as much as possible, but * unfortunately we cannot remove them completely, because MSIOF might setup its register during * CLK/SYNC are inputed. It can be happen because MSIOF is working as Clock/Frame Consumer. */ #include <linux/module.h> #include <linux/of.h> #include <linux/of_dma.h> #include <linux/of_graph.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/reset.h> #include <linux/spi/sh_msiof.h> #include <sound/dmaengine_pcm.h> #include <sound/soc.h> Loading Loading @@ -60,10 +112,13 @@ struct msiof_priv { struct device *dev; struct snd_pcm_substream *substream[SNDRV_PCM_STREAM_LAST + 1]; struct reset_control *reset; spinlock_t lock; void __iomem *base; resource_size_t phy_addr; int count; /* for error */ int err_syc[SNDRV_PCM_STREAM_LAST + 1]; int err_ovf[SNDRV_PCM_STREAM_LAST + 1]; Loading Loading @@ -121,7 +176,7 @@ static int msiof_hw_start(struct snd_soc_component *component, /* * see * [NOTE] on top of this driver * [NOTE-CLOCK-MODE] on top of this driver */ /* * see Loading @@ -131,19 +186,38 @@ static int msiof_hw_start(struct snd_soc_component *component, * RX: Fig 109.15 */ /* reset errors */ priv->err_syc[substream->stream] = /* * Use reset_control_xx() instead of TXRST/RXRST. * see * [NOTE-RESET] */ if (!priv->count) reset_control_deassert(priv->reset); priv->count++; /* * Reset errors. ignore 1st FSERR * * see * [NOTE-FSERR] */ priv->err_syc[substream->stream] = -1; priv->err_ovf[substream->stream] = priv->err_udf[substream->stream] = 0; /* Start DMAC */ snd_dmaengine_pcm_trigger(substream, cmd); /* * setup both direction (Playback/Capture) in the same time. * see * above [NOTE-BOTH-SETTING] */ /* SITMDRx */ if (is_play) { val = SITMDR1_PCON | FIELD_PREP(SIMDR1_SYNCMD, SIMDR1_SYNCMD_LR) | SIMDR1_SYNCAC | SIMDR1_XXSTP; val = SITMDR1_PCON | SIMDR1_SYNCAC | SIMDR1_XXSTP | FIELD_PREP(SIMDR1_SYNCMD, SIMDR1_SYNCMD_LR); if (msiof_flag_has(priv, MSIOF_FLAGS_NEED_DELAY)) val |= FIELD_PREP(SIMDR1_DTDL, 1); Loading @@ -153,11 +227,9 @@ static int msiof_hw_start(struct snd_soc_component *component, msiof_write(priv, SITMDR2, val | FIELD_PREP(SIMDR2_GRP, 1)); msiof_write(priv, SITMDR3, val); } /* SIRMDRx */ else { val = FIELD_PREP(SIMDR1_SYNCMD, SIMDR1_SYNCMD_LR) | SIMDR1_SYNCAC; val = SIMDR1_SYNCAC | FIELD_PREP(SIMDR1_SYNCMD, SIMDR1_SYNCMD_LR); if (msiof_flag_has(priv, MSIOF_FLAGS_NEED_DELAY)) val |= FIELD_PREP(SIMDR1_DTDL, 1); Loading @@ -166,7 +238,11 @@ static int msiof_hw_start(struct snd_soc_component *component, val = FIELD_PREP(SIMDR2_BITLEN1, width - 1); msiof_write(priv, SIRMDR2, val | FIELD_PREP(SIMDR2_GRP, 1)); msiof_write(priv, SIRMDR3, val); } /* SIFCTR */ msiof_write(priv, SIFCTR, FIELD_PREP(SIFCTR_TFWM, SIFCTR_TFWM_1) | FIELD_PREP(SIFCTR_RFWM, SIFCTR_RFWM_1)); /* SIIER */ if (is_play) Loading @@ -183,10 +259,11 @@ static int msiof_hw_start(struct snd_soc_component *component, msiof_update(priv, SISTR, val, val); /* SICTR */ val = SICTR_TEDG | SICTR_REDG; if (is_play) val = SICTR_TXE | SICTR_TEDG; val |= SICTR_TXE; else val = SICTR_RXE | SICTR_REDG; val |= SICTR_RXE; msiof_update_and_wait(priv, SICTR, val, val, val); return 0; Loading @@ -207,9 +284,6 @@ static int msiof_hw_stop(struct snd_soc_component *component, val = SIIER_RDREQE | SIIER_RDMAE | SISTR_ERR_RX; msiof_update(priv, SIIER, val, 0); /* Stop DMAC */ snd_dmaengine_pcm_trigger(substream, cmd); /* SICTR */ if (is_play) val = SICTR_TXE; Loading @@ -217,6 +291,18 @@ static int msiof_hw_stop(struct snd_soc_component *component, val = SICTR_RXE; msiof_update_and_wait(priv, SICTR, val, 0, 0); /* Stop DMAC */ snd_dmaengine_pcm_trigger(substream, cmd); /* * Ignore 1st FSERR * * see * [NOTE-FSERR] */ if (priv->err_syc[substream->stream] < 0) priv->err_syc[substream->stream] = 0; /* indicate error status if exist */ if (priv->err_syc[substream->stream] || priv->err_ovf[substream->stream] || Loading @@ -227,6 +313,11 @@ static int msiof_hw_stop(struct snd_soc_component *component, priv->err_ovf[substream->stream], priv->err_udf[substream->stream]); priv->count--; if (!priv->count) reset_control_assert(priv->reset); return 0; } Loading Loading @@ -302,6 +393,9 @@ static struct snd_soc_dai_driver msiof_dai_driver = { .channels_max = 2, }, .ops = &msiof_dai_ops, .symmetric_rate = 1, .symmetric_channels = 1, .symmetric_sample_bits = 1, }; static struct snd_pcm_hardware msiof_pcm_hardware = { Loading Loading @@ -490,12 +584,19 @@ static int msiof_probe(struct platform_device *pdev) if (IS_ERR(priv->base)) return PTR_ERR(priv->base); priv->reset = devm_reset_control_get_exclusive(dev, NULL); if (IS_ERR(priv->reset)) return PTR_ERR(priv->reset); reset_control_assert(priv->reset); ret = devm_request_irq(dev, irq, msiof_interrupt, 0, dev_name(dev), priv); if (ret) return ret; priv->dev = dev; priv->phy_addr = res->start; priv->count = 0; spin_lock_init(&priv->lock); platform_set_drvdata(pdev, priv); Loading