/* Validity Bit Mode */ #define SPDIFRX_MR_VBMODE_MASK GENAMSK(1, 1) #define SPDIFRX_MR_VBMODE_ALWAYS_LOAD \
(0 << 1) /* Load sample regardless of validity bit value */ #define SPDIFRX_MR_VBMODE_DISCARD_IF_VB1 \
(1 << 1) /* Load sample only if validity bit is 0 */
/* Data Word Endian Mode */ #define SPDIFRX_MR_ENDIAN_MASK GENMASK(2, 2) #define SPDIFRX_MR_ENDIAN_LITTLE (0 << 2) /* Little Endian Mode */ #define SPDIFRX_MR_ENDIAN_BIG (1 << 2) /* Big Endian Mode */
staticbool mchp_spdifrx_readable_reg(struct device *dev, unsignedint reg)
{ switch (reg) { case SPDIFRX_MR: case SPDIFRX_IMR: case SPDIFRX_ISR: case SPDIFRX_RSR: case SPDIFRX_CHSR(0, 0): case SPDIFRX_CHSR(0, 1): case SPDIFRX_CHSR(0, 2): case SPDIFRX_CHSR(0, 3): case SPDIFRX_CHSR(0, 4): case SPDIFRX_CHSR(0, 5): case SPDIFRX_CHUD(0, 0): case SPDIFRX_CHUD(0, 1): case SPDIFRX_CHUD(0, 2): case SPDIFRX_CHUD(0, 3): case SPDIFRX_CHUD(0, 4): case SPDIFRX_CHUD(0, 5): case SPDIFRX_CHSR(1, 0): case SPDIFRX_CHSR(1, 1): case SPDIFRX_CHSR(1, 2): case SPDIFRX_CHSR(1, 3): case SPDIFRX_CHSR(1, 4): case SPDIFRX_CHSR(1, 5): case SPDIFRX_CHUD(1, 0): case SPDIFRX_CHUD(1, 1): case SPDIFRX_CHUD(1, 2): case SPDIFRX_CHUD(1, 3): case SPDIFRX_CHUD(1, 4): case SPDIFRX_CHUD(1, 5): case SPDIFRX_WPMR: case SPDIFRX_WPSR: case SPDIFRX_VERSION: returntrue; default: returnfalse;
}
}
staticbool mchp_spdifrx_writeable_reg(struct device *dev, unsignedint reg)
{ switch (reg) { case SPDIFRX_CR: case SPDIFRX_MR: case SPDIFRX_IER: case SPDIFRX_IDR: case SPDIFRX_WPMR: returntrue; default: returnfalse;
}
}
staticbool mchp_spdifrx_precious_reg(struct device *dev, unsignedint reg)
{ switch (reg) { case SPDIFRX_ISR: case SPDIFRX_RHR: returntrue; default: returnfalse;
}
}
staticbool mchp_spdifrx_volatile_reg(struct device *dev, unsignedint reg)
{ switch (reg) { case SPDIFRX_IMR: case SPDIFRX_ISR: case SPDIFRX_RSR: case SPDIFRX_CHSR(0, 0): case SPDIFRX_CHSR(0, 1): case SPDIFRX_CHSR(0, 2): case SPDIFRX_CHSR(0, 3): case SPDIFRX_CHSR(0, 4): case SPDIFRX_CHSR(0, 5): case SPDIFRX_CHUD(0, 0): case SPDIFRX_CHUD(0, 1): case SPDIFRX_CHUD(0, 2): case SPDIFRX_CHUD(0, 3): case SPDIFRX_CHUD(0, 4): case SPDIFRX_CHUD(0, 5): case SPDIFRX_CHSR(1, 0): case SPDIFRX_CHSR(1, 1): case SPDIFRX_CHSR(1, 2): case SPDIFRX_CHSR(1, 3): case SPDIFRX_CHSR(1, 4): case SPDIFRX_CHSR(1, 5): case SPDIFRX_CHUD(1, 0): case SPDIFRX_CHUD(1, 1): case SPDIFRX_CHUD(1, 2): case SPDIFRX_CHUD(1, 3): case SPDIFRX_CHUD(1, 4): case SPDIFRX_CHUD(1, 5): case SPDIFRX_VERSION: returntrue; default: returnfalse;
}
}
/** * struct mchp_spdifrx_ch_stat: MCHP SPDIFRX channel status * @data: channel status bits * @done: completion to signal channel status bits acquisition done
*/ struct mchp_spdifrx_ch_stat { unsignedchar data[SPDIFRX_CS_BITS / 8]; struct completion done;
};
/** * struct mchp_spdifrx_user_data: MCHP SPDIFRX user data * @data: user data bits * @done: completion to signal user data bits acquisition done
*/ struct mchp_spdifrx_user_data { unsignedchar data[SPDIFRX_UD_BITS / 8]; struct completion done;
};
/** * struct mchp_spdifrx_mixer_control: MCHP SPDIFRX mixer control data structure * @ch_stat: array of channel statuses * @user_data: array of user data * @ulock: ulock bit status * @badf: badf bit status * @signal: signal bit status
*/ struct mchp_spdifrx_mixer_control { struct mchp_spdifrx_ch_stat ch_stat[SPDIFRX_CHANNELS]; struct mchp_spdifrx_user_data user_data[SPDIFRX_CHANNELS]; bool ulock; bool badf; bool signal;
};
/** * struct mchp_spdifrx_dev: MCHP SPDIFRX device data structure * @capture: DAI DMA configuration data * @control: mixer controls * @mlock: mutex to protect concurency b/w configuration and control APIs * @dev: struct device * @regmap: regmap for this device * @pclk: peripheral clock * @gclk: generic clock * @trigger_enabled: true if enabled though trigger() ops
*/ struct mchp_spdifrx_dev { struct snd_dmaengine_dai_dma_data capture; struct mchp_spdifrx_mixer_control control; struct mutex mlock; struct device *dev; struct regmap *regmap; struct clk *pclk; struct clk *gclk; unsignedint trigger_enabled;
};
staticvoid mchp_spdifrx_channel_status_read(struct mchp_spdifrx_dev *dev, int channel)
{ struct mchp_spdifrx_mixer_control *ctrl = &dev->control;
u8 *ch_stat = &ctrl->ch_stat[channel].data[0];
u32 val; int i;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
dev_err(dev->dev, "Playback is not supported\n"); return -EINVAL;
}
if (params_channels(params) != SPDIFRX_CHANNELS) {
dev_err(dev->dev, "unsupported number of channels: %d\n",
params_channels(params)); return -EINVAL;
}
switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_BE: case SNDRV_PCM_FORMAT_S20_3BE: case SNDRV_PCM_FORMAT_S24_3BE: case SNDRV_PCM_FORMAT_S24_BE:
mr |= SPDIFRX_MR_ENDIAN_BIG;
fallthrough; case SNDRV_PCM_FORMAT_S16_LE: case SNDRV_PCM_FORMAT_S20_3LE: case SNDRV_PCM_FORMAT_S24_3LE: case SNDRV_PCM_FORMAT_S24_LE:
mr |= SPDIFRX_MR_DATAWIDTH(params_width(params)); break; default:
dev_err(dev->dev, "unsupported PCM format: %d\n",
params_format(params)); return -EINVAL;
}
mutex_lock(&dev->mlock); if (dev->trigger_enabled) {
dev_err(dev->dev, "PCM already running\n");
ret = -EBUSY; goto unlock;
}
/* GCLK is enabled by runtime PM. */
clk_disable_unprepare(dev->gclk);
ret = clk_set_min_rate(dev->gclk, params_rate(params) *
SPDIFRX_GCLK_RATIO_MIN + 1); if (ret) {
dev_err(dev->dev, "unable to set gclk min rate: rate %u * ratio %u + 1\n",
params_rate(params), SPDIFRX_GCLK_RATIO_MIN); /* Restore runtime PM state. */
clk_prepare_enable(dev->gclk); goto unlock;
}
ret = clk_prepare_enable(dev->gclk); if (ret) {
dev_err(dev->dev, "unable to enable gclk: %d\n", ret); goto unlock;
}
dev_dbg(dev->dev, "GCLK range min set to %d\n",
params_rate(params) * SPDIFRX_GCLK_RATIO_MIN + 1);
staticint mchp_spdifrx_cs_get(struct mchp_spdifrx_dev *dev, int channel, struct snd_ctl_elem_value *uvalue)
{ struct mchp_spdifrx_mixer_control *ctrl = &dev->control; struct mchp_spdifrx_ch_stat *ch_stat = &ctrl->ch_stat[channel]; int ret = 0;
mutex_lock(&dev->mlock);
ret = pm_runtime_resume_and_get(dev->dev); if (ret < 0) goto unlock;
/* * 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 pm_runtime_put;
} else {
ret = 0;
}
} else { /* Update software cache with latest channel status. */
mchp_spdifrx_channel_status_read(dev, channel);
}
staticint mchp_spdifrx_subcode_ch_get(struct mchp_spdifrx_dev *dev, int channel, struct snd_ctl_elem_value *uvalue)
{ struct mchp_spdifrx_mixer_control *ctrl = &dev->control; struct mchp_spdifrx_user_data *user_data = &ctrl->user_data[channel]; int ret = 0;
mutex_lock(&dev->mlock);
ret = pm_runtime_resume_and_get(dev->dev); if (ret < 0) goto unlock;
/* * 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.
*/
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 pm_runtime_put;
} else {
ret = 0;
}
} else { /* Update software cache with last available data. */
mchp_spdifrx_channel_user_data_read(dev, channel);
}
ret = pm_runtime_resume_and_get(dev->dev); if (ret < 0) goto unlock;
/* * The RSR.ULOCK has wrong value if both pclk and gclk are enabled * and the receiver is disabled. Thus we take into account the * dev->trigger_enabled here to return a real status.
*/ if (dev->trigger_enabled) {
regmap_read(dev->regmap, SPDIFRX_RSR, &val);
ctrl->ulock = !(val & SPDIFRX_RSR_ULOCK);
} else {
ctrl->ulock = 0;
}
ret = pm_runtime_resume_and_get(dev->dev); if (ret < 0) goto unlock;
/* * The RSR.ULOCK has wrong value if both pclk and gclk are enabled * and the receiver is disabled. Thus we take into account the * dev->trigger_enabled here to return a real status.
*/ if (dev->trigger_enabled) {
regmap_read(dev->regmap, SPDIFRX_RSR, &val);
ctrl->badf = !!(val & SPDIFRX_RSR_BADF);
} else {
ctrl->badf = 0;
}
ret = pm_runtime_resume_and_get(dev->dev); if (ret < 0) goto unlock;
/* * To get the signal we need to have receiver enabled. This * could be enabled also from trigger() function thus we need to * take care of not disabling the receiver when it runs.
*/ if (!dev->trigger_enabled) {
regmap_update_bits(dev->regmap, SPDIFRX_MR, SPDIFRX_MR_RXEN_MASK,
SPDIFRX_MR_RXEN_ENABLE);
/* Wait for RSR.ULOCK bit. */ while (--loops) {
regmap_read(dev->regmap, SPDIFRX_RSR, &val); if (!(val & SPDIFRX_RSR_ULOCK)) break;
usleep_range(100, 150);
}
ret = pm_runtime_resume_and_get(dev->dev); if (ret < 0) goto unlock;
/* * The RSR.ULOCK has wrong value if both pclk and gclk are enabled * and the receiver is disabled. Thus we take into account the * dev->trigger_enabled here to return a real status.
*/ if (dev->trigger_enabled) {
regmap_read(dev->regmap, SPDIFRX_RSR, &val); /* If the receiver is not locked, ISF data is invalid. */ if (val & SPDIFRX_RSR_ULOCK || !(val & SPDIFRX_RSR_IFS_MASK)) {
ucontrol->value.integer.value[0] = 0; goto pm_runtime_put;
}
} else { /* Reveicer is not locked, IFS data is invalid. */
ucontrol->value.integer.value[0] = 0; goto pm_runtime_put;
}
/* Get the peripheral clock */
dev->pclk = devm_clk_get(&pdev->dev, "pclk"); if (IS_ERR(dev->pclk)) {
err = PTR_ERR(dev->pclk);
dev_err(&pdev->dev, "failed to get the peripheral clock: %d\n",
err); return err;
}
/* Get the generated clock */
dev->gclk = devm_clk_get(&pdev->dev, "gclk"); if (IS_ERR(dev->gclk)) {
err = PTR_ERR(dev->gclk);
dev_err(&pdev->dev, "failed to get the PMC generated clock: %d\n", err); return err;
}
/* * Signal control need a valid rate on gclk. hw_params() configures * it propertly but requesting signal before any hw_params() has been * called lead to invalid value returned for signal. Thus, configure * gclk at a valid rate, here, in initialization, to simplify the * control path.
*/
clk_set_min_rate(dev->gclk, 48000 * SPDIFRX_GCLK_RATIO_MIN + 1);
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.