Загрузка данных
static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai)
{
struct fsl_xcvr *xcvr = snd_soc_dai_get_drvdata(dai);
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
unsigned long lock_flags;
int ret = 0;
spin_lock_irqsave(&xcvr->lock, lock_flags);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
/* set DPATH RESET */
ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_CTRL,
FSL_XCVR_EXT_CTRL_DPTH_RESET(tx),
FSL_XCVR_EXT_CTRL_DPTH_RESET(tx));
if (ret < 0) {
dev_err(dai->dev, "Failed to set DPATH RESET: %d\n", ret);
goto release_lock;
}
if (tx) {
switch (xcvr->mode) {
case FSL_XCVR_MODE_EARC:
/* set isr_cmdc_tx_en, w1c */
ret = regmap_write(xcvr->regmap,
FSL_XCVR_ISR_SET,
FSL_XCVR_ISR_CMDC_TX_EN);
if (ret < 0) {
dev_err(dai->dev, "err updating isr %d\n", ret);
goto release_lock;
}
fallthrough;
case FSL_XCVR_MODE_SPDIF:
ret = regmap_set_bits(xcvr->regmap,
FSL_XCVR_TX_DPTH_CTRL,
FSL_XCVR_TX_DPTH_CTRL_STRT_DATA_TX);
if (ret < 0) {
dev_err(dai->dev, "Failed to start DATA_TX: %d\n", ret);
goto release_lock;
}
break;
}
}
/* enable DMA RD/WR */
ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_CTRL,
FSL_XCVR_EXT_CTRL_DMA_DIS(tx), 0);
if (ret < 0) {
dev_err(dai->dev, "Failed to enable DMA: %d\n", ret);
goto release_lock;
}
ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_IER0,
FSL_XCVR_IRQ_EARC_ALL, FSL_XCVR_IRQ_EARC_ALL);
if (ret < 0) {
dev_err(dai->dev, "Error while setting IER0: %d\n", ret);
goto release_lock;
}
/* clear DPATH RESET */
ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_CTRL,
FSL_XCVR_EXT_CTRL_DPTH_RESET(tx),
0);
if (ret < 0) {
dev_err(dai->dev, "Failed to clear DPATH RESET: %d\n", ret);
goto release_lock;
}
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
/* disable DMA RD/WR */
ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_CTRL,
FSL_XCVR_EXT_CTRL_DMA_DIS(tx),
FSL_XCVR_EXT_CTRL_DMA_DIS(tx));
if (ret < 0) {
dev_err(dai->dev, "Failed to disable DMA: %d\n", ret);
goto release_lock;
}
ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_IER0,
FSL_XCVR_IRQ_EARC_ALL, 0);
if (ret < 0) {
dev_err(dai->dev, "Failed to clear IER0: %d\n", ret);
goto release_lock;
}
if (tx) {
switch (xcvr->mode) {
case FSL_XCVR_MODE_SPDIF:
ret = regmap_clear_bits(xcvr->regmap,
FSL_XCVR_TX_DPTH_CTRL,
FSL_XCVR_TX_DPTH_CTRL_STRT_DATA_TX);
if (ret < 0) {
dev_err(dai->dev, "Failed to stop DATA_TX: %d\n", ret);
goto release_lock;
}
if (xcvr->soc_data->spdif_only)
break;
else
fallthrough;
case FSL_XCVR_MODE_EARC:
/* clear ISR_CMDC_TX_EN, W1C */
ret = regmap_write(xcvr->regmap,
FSL_XCVR_ISR_CLR,
FSL_XCVR_ISR_CMDC_TX_EN);
if (ret < 0) {
dev_err(dai->dev,
"Err updating ISR %d\n", ret);
goto release_lock;
}
break;
}
}
break;
default:
ret = -EINVAL;
break;
}
release_lock:
spin_unlock_irqrestore(&xcvr->lock, lock_flags);
return ret;
}
static int fsl_xcvr_load_firmware(struct fsl_xcvr *xcvr)
{
struct device *dev = &xcvr->pdev->dev;
const struct firmware *fw;
int ret = 0, rem, off, out, page = 0, size = FSL_XCVR_REG_OFFSET;
u32 mask, val;
ret = request_firmware(&fw, xcvr->soc_data->fw_name, dev);
if (ret) {
dev_err(dev, "failed to request firmware.\n");
return ret;
}
rem = fw->size;
/* RAM is 20KiB = 16KiB code + 4KiB data => max 10 pages 2KiB each */
if (rem > 16384) {
dev_err(dev, "FW size %d is bigger than 16KiB.\n", rem);
release_firmware(fw);
return -ENOMEM;
}
for (page = 0; page < 10; page++) {
ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_CTRL,
FSL_XCVR_EXT_CTRL_PAGE_MASK,
FSL_XCVR_EXT_CTRL_PAGE(page));
if (ret < 0) {
dev_err(dev, "FW: failed to set page %d, err=%d\n",
page, ret);
goto err_firmware;
}
off = page * size;
out = min(rem, size);
/* IPG clock is assumed to be running, otherwise it will hang */
if (out > 0) {
/* write firmware into code memory */
memcpy_toio(xcvr->ram_addr, fw->data + off, out);
rem -= out;
if (rem == 0) {
/* last part of firmware written */
/* clean remaining part of code memory page */
memset_io(xcvr->ram_addr + out, 0, size - out);
}
} else {
/* clean current page, including data memory */
memset_io(xcvr->ram_addr, 0, size);
}
}
err_firmware:
release_firmware(fw);
if (ret < 0)
return ret;
/* configure watermarks */
mask = FSL_XCVR_EXT_CTRL_RX_FWM_MASK | FSL_XCVR_EXT_CTRL_TX_FWM_MASK;
val = FSL_XCVR_EXT_CTRL_RX_FWM(FSL_XCVR_FIFO_WMK_RX);
val |= FSL_XCVR_EXT_CTRL_TX_FWM(FSL_XCVR_FIFO_WMK_TX);
/* disable DMA RD/WR */
mask |= FSL_XCVR_EXT_CTRL_DMA_RD_DIS | FSL_XCVR_EXT_CTRL_DMA_WR_DIS;
val |= FSL_XCVR_EXT_CTRL_DMA_RD_DIS | FSL_XCVR_EXT_CTRL_DMA_WR_DIS;
/* Data RAM is 4KiB, last two pages: 8 and 9. Select page 8. */
mask |= FSL_XCVR_EXT_CTRL_PAGE_MASK;
val |= FSL_XCVR_EXT_CTRL_PAGE(8);
ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_CTRL, mask, val);
if (ret < 0) {
dev_err(dev, "Failed to set watermarks: %d\n", ret);
return ret;
}
/* Store Capabilities Data Structure into Data RAM */
memcpy_toio(xcvr->ram_addr + FSL_XCVR_CAP_DATA_STR, xcvr->cap_ds,
FSL_XCVR_CAPDS_SIZE);
return 0;
}
static int fsl_xcvr_type_iec958_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
uinfo->count = 1;
return 0;
}
static int fsl_xcvr_type_iec958_bytes_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
uinfo->count = sizeof_field(struct snd_aes_iec958, status);
return 0;
}
static int fsl_xcvr_rx_cs_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
struct fsl_xcvr *xcvr = snd_soc_dai_get_drvdata(dai);
memcpy(ucontrol->value.iec958.status, xcvr->rx_iec958.status, 24);
return 0;
}
static int fsl_xcvr_tx_cs_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
struct fsl_xcvr *xcvr = snd_soc_dai_get_drvdata(dai);
memcpy(ucontrol->value.iec958.status, xcvr->tx_iec958.status, 24);
return 0;
}
static int fsl_xcvr_tx_cs_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
struct fsl_xcvr *xcvr = snd_soc_dai_get_drvdata(dai);
memcpy(xcvr->tx_iec958.status, ucontrol->value.iec958.status, 24);
return 0;
}
static struct snd_kcontrol_new fsl_xcvr_rx_ctls[] = {
/* Channel status controller */
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("", CAPTURE, DEFAULT),
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.info = fsl_xcvr_type_iec958_info,
.get = fsl_xcvr_rx_cs_get,
},
/* Capture channel status, bytes */
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = "Capture Channel Status",
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.info = fsl_xcvr_type_iec958_bytes_info,
.get = fsl_xcvr_rx_cs_get,
},
};
static struct snd_kcontrol_new fsl_xcvr_tx_ctls[] = {
/* Channel status controller */
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
.info = fsl_xcvr_type_iec958_info,
.get = fsl_xcvr_tx_cs_get,
.put = fsl_xcvr_tx_cs_put,
},
/* Playback channel status, bytes */
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = "Playback Channel Status",
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
.info = fsl_xcvr_type_iec958_bytes_info,
.get = fsl_xcvr_tx_cs_get,
.put = fsl_xcvr_tx_cs_put,
},
};