summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sound/soc/atmel/mchp-spdifrx.c143
1 files changed, 77 insertions, 66 deletions
diff --git a/sound/soc/atmel/mchp-spdifrx.c b/sound/soc/atmel/mchp-spdifrx.c
index 076a78fd0b12..eb1b8724e11f 100644
--- a/sound/soc/atmel/mchp-spdifrx.c
+++ b/sound/soc/atmel/mchp-spdifrx.c
@@ -217,7 +217,6 @@ struct mchp_spdifrx_ch_stat {
struct mchp_spdifrx_user_data {
unsigned char data[SPDIFRX_UD_BITS / 8];
struct completion done;
- spinlock_t lock; /* protect access to user data */
};
struct mchp_spdifrx_mixer_control {
@@ -231,8 +230,6 @@ struct mchp_spdifrx_mixer_control {
struct mchp_spdifrx_dev {
struct snd_dmaengine_dai_dma_data capture;
struct mchp_spdifrx_mixer_control control;
- spinlock_t blockend_lock; /* protect access to blockend_refcount */
- int blockend_refcount;
struct mutex mlock;
struct device *dev;
struct regmap *regmap;
@@ -277,37 +274,11 @@ static void mchp_spdifrx_channel_user_data_read(struct mchp_spdifrx_dev *dev,
}
}
-/* called from non-atomic context only */
-static void mchp_spdifrx_isr_blockend_en(struct mchp_spdifrx_dev *dev)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&dev->blockend_lock, flags);
- dev->blockend_refcount++;
- /* don't enable BLOCKEND interrupt if it's already enabled */
- if (dev->blockend_refcount == 1)
- regmap_write(dev->regmap, SPDIFRX_IER, SPDIFRX_IR_BLOCKEND);
- spin_unlock_irqrestore(&dev->blockend_lock, flags);
-}
-
-/* called from atomic/non-atomic context */
-static void mchp_spdifrx_isr_blockend_dis(struct mchp_spdifrx_dev *dev)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&dev->blockend_lock, flags);
- dev->blockend_refcount--;
- /* don't enable BLOCKEND interrupt if it's already enabled */
- if (dev->blockend_refcount == 0)
- regmap_write(dev->regmap, SPDIFRX_IDR, SPDIFRX_IR_BLOCKEND);
- spin_unlock_irqrestore(&dev->blockend_lock, flags);
-}
-
static irqreturn_t mchp_spdif_interrupt(int irq, void *dev_id)
{
struct mchp_spdifrx_dev *dev = dev_id;
struct mchp_spdifrx_mixer_control *ctrl = &dev->control;
- u32 sr, imr, pending, idr = 0;
+ u32 sr, imr, pending;
irqreturn_t ret = IRQ_NONE;
int ch;
@@ -322,13 +293,10 @@ static irqreturn_t mchp_spdif_interrupt(int irq, void *dev_id)
if (pending & SPDIFRX_IR_BLOCKEND) {
for (ch = 0; ch < SPDIFRX_CHANNELS; ch++) {
- spin_lock(&ctrl->user_data[ch].lock);
mchp_spdifrx_channel_user_data_read(dev, ch);
- spin_unlock(&ctrl->user_data[ch].lock);
-
complete(&ctrl->user_data[ch].done);
}
- mchp_spdifrx_isr_blockend_dis(dev);
+ regmap_write(dev->regmap, SPDIFRX_IDR, SPDIFRX_IR_BLOCKEND);
ret = IRQ_HANDLED;
}
@@ -336,7 +304,7 @@ static irqreturn_t mchp_spdif_interrupt(int irq, void *dev_id)
if (pending & SPDIFRX_IR_CSC(ch)) {
mchp_spdifrx_channel_status_read(dev, ch);
complete(&ctrl->ch_stat[ch].done);
- idr |= SPDIFRX_IR_CSC(ch);
+ regmap_write(dev->regmap, SPDIFRX_IDR, SPDIFRX_IR_CSC(ch));
ret = IRQ_HANDLED;
}
}
@@ -346,8 +314,6 @@ static irqreturn_t mchp_spdif_interrupt(int irq, void *dev_id)
ret = IRQ_HANDLED;
}
- regmap_write(dev->regmap, SPDIFRX_IDR, idr);
-
return ret;
}
@@ -517,23 +483,51 @@ static int mchp_spdifrx_cs_get(struct mchp_spdifrx_dev *dev,
{
struct mchp_spdifrx_mixer_control *ctrl = &dev->control;
struct mchp_spdifrx_ch_stat *ch_stat = &ctrl->ch_stat[channel];
- int ret;
+ int ret = 0;
+
+ mutex_lock(&dev->mlock);
- regmap_write(dev->regmap, SPDIFRX_IER, SPDIFRX_IR_CSC(channel));
- /* check for new data available */
- ret = wait_for_completion_interruptible_timeout(&ch_stat->done,
- msecs_to_jiffies(100));
- /* IP might not be started or valid stream might not be present */
- if (ret <= 0) {
- dev_dbg(dev->dev, "channel status for channel %d timeout\n",
- channel);
- return ret ? : -ETIMEDOUT;
+ /*
+ * We may reach this point with both clocks enabled but the receiver
+ * still disabled. To void waiting for completion and return with
+ * timeout check the dev->trigger_enabled.
+ *
+ * To retrieve data:
+ * - if the receiver is enabled CSC IRQ will update the data in software
+ * caches (ch_stat->data)
+ * - otherwise we just update it here the software caches with latest
+ * available information and return it; in this case we don't need
+ * spin locking as the IRQ is disabled and will not be raised from
+ * anywhere else.
+ */
+
+ if (dev->trigger_enabled) {
+ reinit_completion(&ch_stat->done);
+ regmap_write(dev->regmap, SPDIFRX_IER, SPDIFRX_IR_CSC(channel));
+ /* Check for new data available */
+ ret = wait_for_completion_interruptible_timeout(&ch_stat->done,
+ msecs_to_jiffies(100));
+ /* Valid stream might not be present */
+ if (ret <= 0) {
+ dev_dbg(dev->dev, "channel status for channel %d timeout\n",
+ channel);
+ regmap_write(dev->regmap, SPDIFRX_IDR, SPDIFRX_IR_CSC(channel));
+ ret = ret ? : -ETIMEDOUT;
+ goto unlock;
+ } else {
+ ret = 0;
+ }
+ } else {
+ /* Update software cache with latest channel status. */
+ mchp_spdifrx_channel_status_read(dev, channel);
}
memcpy(uvalue->value.iec958.status, ch_stat->data,
sizeof(ch_stat->data));
- return 0;
+unlock:
+ mutex_unlock(&dev->mlock);
+ return ret;
}
static int mchp_spdifrx_cs1_get(struct snd_kcontrol *kcontrol,
@@ -567,29 +561,49 @@ static int mchp_spdifrx_subcode_ch_get(struct mchp_spdifrx_dev *dev,
int channel,
struct snd_ctl_elem_value *uvalue)
{
- unsigned long flags;
struct mchp_spdifrx_mixer_control *ctrl = &dev->control;
struct mchp_spdifrx_user_data *user_data = &ctrl->user_data[channel];
- int ret;
+ int ret = 0;
+
+ mutex_lock(&dev->mlock);
+
+ /*
+ * We may reach this point with both clocks enabled but the receiver
+ * still disabled. To void waiting for completion to just timeout we
+ * check here the dev->trigger_enabled flag.
+ *
+ * To retrieve data:
+ * - if the receiver is enabled we need to wait for blockend IRQ to read
+ * data to and update it for us in software caches
+ * - otherwise reading the SPDIFRX_CHUD() registers is enough.
+ */
- reinit_completion(&user_data->done);
- mchp_spdifrx_isr_blockend_en(dev);
- ret = wait_for_completion_interruptible_timeout(&user_data->done,
- msecs_to_jiffies(100));
- /* IP might not be started or valid stream might not be present */
- if (ret <= 0) {
- dev_dbg(dev->dev, "user data for channel %d timeout\n",
- channel);
- mchp_spdifrx_isr_blockend_dis(dev);
- return ret ? : -ETIMEDOUT;
+ if (dev->trigger_enabled) {
+ reinit_completion(&user_data->done);
+ regmap_write(dev->regmap, SPDIFRX_IER, SPDIFRX_IR_BLOCKEND);
+ ret = wait_for_completion_interruptible_timeout(&user_data->done,
+ msecs_to_jiffies(100));
+ /* Valid stream might not be present. */
+ if (ret <= 0) {
+ dev_dbg(dev->dev, "user data for channel %d timeout\n",
+ channel);
+ regmap_write(dev->regmap, SPDIFRX_IDR, SPDIFRX_IR_BLOCKEND);
+ ret = ret ? : -ETIMEDOUT;
+ goto unlock;
+ } else {
+ ret = 0;
+ }
+ } else {
+ /* Update software cache with last available data. */
+ mchp_spdifrx_channel_user_data_read(dev, channel);
}
- spin_lock_irqsave(&user_data->lock, flags);
memcpy(uvalue->value.iec958.subcode, user_data->data,
sizeof(user_data->data));
- spin_unlock_irqrestore(&user_data->lock, flags);
- return 0;
+unlock:
+ mutex_unlock(&dev->mlock);
+ return ret;
}
static int mchp_spdifrx_subcode_ch1_get(struct snd_kcontrol *kcontrol,
@@ -890,11 +904,9 @@ static int mchp_spdifrx_dai_probe(struct snd_soc_dai *dai)
SPDIFRX_MR_AUTORST_NOACTION |
SPDIFRX_MR_PACK_DISABLED);
- dev->blockend_refcount = 0;
for (ch = 0; ch < SPDIFRX_CHANNELS; ch++) {
init_completion(&ctrl->ch_stat[ch].done);
init_completion(&ctrl->user_data[ch].done);
- spin_lock_init(&ctrl->user_data[ch].lock);
}
/* Add controls */
@@ -1004,7 +1016,6 @@ static int mchp_spdifrx_probe(struct platform_device *pdev)
*/
clk_set_min_rate(dev->gclk, 48000 * SPDIFRX_GCLK_RATIO_MIN + 1);
- spin_lock_init(&dev->blockend_lock);
mutex_init(&dev->mlock);
dev->dev = &pdev->dev;