struct i2s_dai { /* Platform device for this DAI */ struct platform_device *pdev;
/* Frame clock */ unsigned frmclk; /* * Specifically requested RCLK, BCLK by machine driver. * 0 indicates CPU driver is free to choose any value.
*/ unsigned rfs, bfs; /* Pointer to the Primary_Fifo if this is Sec_Fifo, NULL otherwise */ struct i2s_dai *pri_dai; /* Pointer to the Secondary_Fifo if it has one, NULL otherwise */ struct i2s_dai *sec_dai;
#define DAI_OPENED (1 << 0) /* DAI is opened */ #define DAI_MANAGER (1 << 1) /* DAI is the manager */ unsigned mode;
/* Driver for this DAI */ struct snd_soc_dai_driver *drv;
/* The clock provider's data */ struct clk *clk_table[3]; struct clk_onecell_data clk_data;
/* Spinlock protecting member fields below */
spinlock_t lock;
/* Memory mapped SFR region */ void __iomem *addr;
/* A flag indicating the I2S slave mode operation */ bool slave_mode;
};
/* Returns true if this is the 'overlay' stereo DAI */ staticinlinebool is_secondary(struct i2s_dai *i2s)
{ return i2s->drv->id == SAMSUNG_I2S_ID_SECONDARY;
}
/* If this interface of the controller is transmitting data */ staticinlinebool tx_active(struct i2s_dai *i2s)
{
u32 active;
if (!i2s) returnfalse;
active = readl(i2s->priv->addr + I2SCON);
if (is_secondary(i2s))
active &= CON_TXSDMA_ACTIVE; else
active &= CON_TXDMA_ACTIVE;
return active ? true : false;
}
/* Return pointer to the other DAI */ staticinlinestruct i2s_dai *get_other_dai(struct i2s_dai *i2s)
{ return i2s->pri_dai ? : i2s->sec_dai;
}
/* If the other interface of the controller is transmitting data */ staticinlinebool other_tx_active(struct i2s_dai *i2s)
{ struct i2s_dai *other = get_other_dai(i2s);
return tx_active(other);
}
/* If any interface of the controller is transmitting data */ staticinlinebool any_tx_active(struct i2s_dai *i2s)
{ return tx_active(i2s) || other_tx_active(i2s);
}
/* If this interface of the controller is receiving data */ staticinlinebool rx_active(struct i2s_dai *i2s)
{
u32 active;
if (!i2s) returnfalse;
active = readl(i2s->priv->addr + I2SCON) & CON_RXDMA_ACTIVE;
return active ? true : false;
}
/* If the other interface of the controller is receiving data */ staticinlinebool other_rx_active(struct i2s_dai *i2s)
{ struct i2s_dai *other = get_other_dai(i2s);
return rx_active(other);
}
/* If any interface of the controller is receiving data */ staticinlinebool any_rx_active(struct i2s_dai *i2s)
{ return rx_active(i2s) || other_rx_active(i2s);
}
/* If the other DAI is transmitting or receiving data */ staticinlinebool other_active(struct i2s_dai *i2s)
{ return other_rx_active(i2s) || other_tx_active(i2s);
}
/* If this DAI is transmitting or receiving data */ staticinlinebool this_active(struct i2s_dai *i2s)
{ return tx_active(i2s) || rx_active(i2s);
}
/* If the controller is active anyway */ staticinlinebool any_active(struct i2s_dai *i2s)
{ return this_active(i2s) || other_active(i2s);
}
switch (rfs) { case 7: return 192; case 6: return 96; case 5: return 128; case 4: return 64; case 3: return 768; case 2: return 384; case 1: return 512; default: return 256;
}
}
/* Write RCLK of I2S (in multiples of LRCLK) */ staticinlinevoid set_rfs(struct i2s_dai *i2s, unsigned rfs)
{ struct samsung_i2s_priv *priv = i2s->priv;
u32 mod = readl(priv->addr + I2SMOD); int rfs_shift = priv->variant_regs->rfs_off;
mod &= ~(priv->variant_regs->rfs_mask << rfs_shift);
switch (rfs) { case 192:
mod |= (EXYNOS7_MOD_RCLK_192FS << rfs_shift); break; case 96:
mod |= (EXYNOS7_MOD_RCLK_96FS << rfs_shift); break; case 128:
mod |= (EXYNOS7_MOD_RCLK_128FS << rfs_shift); break; case 64:
mod |= (EXYNOS7_MOD_RCLK_64FS << rfs_shift); break; case 768:
mod |= (MOD_RCLK_768FS << rfs_shift); break; case 512:
mod |= (MOD_RCLK_512FS << rfs_shift); break; case 384:
mod |= (MOD_RCLK_384FS << rfs_shift); break; default:
mod |= (MOD_RCLK_256FS << rfs_shift); break;
}
writel(mod, priv->addr + I2SMOD);
}
/* Read bit-clock of I2S (in multiples of LRCLK) */ staticinlineunsigned get_bfs(struct i2s_dai *i2s)
{ struct samsung_i2s_priv *priv = i2s->priv;
u32 bfs;
switch (bfs) { case 8: return 256; case 7: return 192; case 6: return 128; case 5: return 96; case 4: return 64; case 3: return 24; case 2: return 16; case 1: return 48; default: return 32;
}
}
/* Write bit-clock of I2S (in multiples of LRCLK) */ staticinlinevoid set_bfs(struct i2s_dai *i2s, unsigned bfs)
{ struct samsung_i2s_priv *priv = i2s->priv;
u32 mod = readl(priv->addr + I2SMOD); int tdm = priv->quirks & QUIRK_SUPPORTS_TDM; int bfs_shift = priv->variant_regs->bfs_off;
/* Non-TDM I2S controllers do not support BCLK > 48 * FS */ if (!tdm && bfs > 48) {
dev_err(&i2s->pdev->dev, "Unsupported BCLK divider\n"); return;
}
mod &= ~(priv->variant_regs->bfs_mask << bfs_shift);
switch (bfs) { case 48:
mod |= (MOD_BCLK_48FS << bfs_shift); break; case 32:
mod |= (MOD_BCLK_32FS << bfs_shift); break; case 24:
mod |= (MOD_BCLK_24FS << bfs_shift); break; case 16:
mod |= (MOD_BCLK_16FS << bfs_shift); break; case 64:
mod |= (EXYNOS5420_MOD_BCLK_64FS << bfs_shift); break; case 96:
mod |= (EXYNOS5420_MOD_BCLK_96FS << bfs_shift); break; case 128:
mod |= (EXYNOS5420_MOD_BCLK_128FS << bfs_shift); break; case 192:
mod |= (EXYNOS5420_MOD_BCLK_192FS << bfs_shift); break; case 256:
mod |= (EXYNOS5420_MOD_BCLK_256FS << bfs_shift); break; default:
dev_err(&i2s->pdev->dev, "Wrong BCLK Divider!\n"); return;
}
switch (blc) { case 2: return 24; case 1: return 8; default: return 16;
}
}
/* TX channel control */ staticvoid i2s_txctrl(struct i2s_dai *i2s, int on)
{ struct samsung_i2s_priv *priv = i2s->priv; void __iomem *addr = priv->addr; int txr_off = priv->variant_regs->txr_off;
u32 con = readl(addr + I2SCON);
u32 mod = readl(addr + I2SMOD) & ~(3 << txr_off);
if (on) {
con |= CON_ACTIVE;
con &= ~CON_TXCH_PAUSE;
if (is_secondary(i2s)) {
con |= CON_TXSDMA_ACTIVE;
con &= ~CON_TXSDMA_PAUSE;
} else {
con |= CON_TXDMA_ACTIVE;
con &= ~CON_TXDMA_PAUSE;
}
if (any_rx_active(i2s))
mod |= 2 << txr_off; else
mod |= 0 << txr_off;
} else { if (is_secondary(i2s)) {
con |= CON_TXSDMA_PAUSE;
con &= ~CON_TXSDMA_ACTIVE;
} else {
con |= CON_TXDMA_PAUSE;
con &= ~CON_TXDMA_ACTIVE;
}
if (other_tx_active(i2s)) {
writel(con, addr + I2SCON); return;
}
con |= CON_TXCH_PAUSE;
if (any_rx_active(i2s))
mod |= 1 << txr_off; else
con &= ~CON_ACTIVE;
}
/* Format is priority */ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_RIGHT_J:
tmp |= lrp_rlow;
tmp |= (MOD_SDF_MSB << sdf_shift); break; case SND_SOC_DAIFMT_LEFT_J:
tmp |= lrp_rlow;
tmp |= (MOD_SDF_LSB << sdf_shift); break; case SND_SOC_DAIFMT_I2S:
tmp |= (MOD_SDF_IIS << sdf_shift); break; default:
dev_err(&i2s->pdev->dev, "Format not supported\n"); return -EINVAL;
}
/* * INV flag is relative to the FORMAT flag - if set it simply * flips the polarity specified by the Standard
*/ switch (fmt & SND_SOC_DAIFMT_INV_MASK) { case SND_SOC_DAIFMT_NB_NF: break; case SND_SOC_DAIFMT_NB_IF: if (tmp & lrp_rlow)
tmp &= ~lrp_rlow; else
tmp |= lrp_rlow; break; default:
dev_err(&i2s->pdev->dev, "Polarity not supported\n"); return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { case SND_SOC_DAIFMT_BC_FC:
tmp |= mod_slave; break; case SND_SOC_DAIFMT_BP_FP: /* * Set default source clock in Master mode, only when the * CLK_I2S_RCLK_SRC clock is not exposed so we ensure any * clock configuration assigned in DT is not overwritten.
*/ if (priv->rclk_srcrate == 0 && priv->clk_data.clks == NULL)
i2s_set_sysclk(dai, SAMSUNG_I2S_RCLKSRC_0,
0, SND_SOC_CLOCK_IN); break; default:
dev_err(&i2s->pdev->dev, "master/slave format not supported\n"); return -EINVAL;
}
pm_runtime_get_sync(dai->dev);
spin_lock_irqsave(&priv->lock, flags);
mod = readl(priv->addr + I2SMOD); /* * Don't change the I2S mode if any controller is active on this * channel.
*/ if (any_active(i2s) &&
((mod & (sdf_mask | lrp_rlow | mod_slave)) != tmp)) {
spin_unlock_irqrestore(&priv->lock, flags);
pm_runtime_put(dai->dev);
dev_err(&i2s->pdev->dev, "%s:%d Other DAI busy\n", __func__, __LINE__); return -EAGAIN;
}
if (is_secondary(i2s))
mask |= MOD_BLCS_MASK; else
mask |= MOD_BLCP_MASK;
if (is_manager(i2s))
mask |= MOD_BLC_MASK;
switch (params_width(params)) { case 8: if (is_secondary(i2s))
val |= MOD_BLCS_8BIT; else
val |= MOD_BLCP_8BIT; if (is_manager(i2s))
val |= MOD_BLC_8BIT; break; case 16: if (is_secondary(i2s))
val |= MOD_BLCS_16BIT; else
val |= MOD_BLCP_16BIT; if (is_manager(i2s))
val |= MOD_BLC_16BIT; break; case 24: if (is_secondary(i2s))
val |= MOD_BLCS_24BIT; else
val |= MOD_BLCP_24BIT; if (is_manager(i2s))
val |= MOD_BLC_24BIT; break; default:
dev_err(&i2s->pdev->dev, "Format(%d) not supported\n",
params_format(params)); return -EINVAL;
}
spin_lock_irqsave(&priv->lock, flags);
mod = readl(priv->addr + I2SMOD);
mod = (mod & ~mask) | val;
writel(mod, priv->addr + I2SMOD);
spin_unlock_irqrestore(&priv->lock, flags);
rclksrc = priv->clk_table[CLK_I2S_RCLK_SRC]; if (rclksrc && !IS_ERR(rclksrc))
priv->rclk_srcrate = clk_get_rate(rclksrc);
return 0;
}
/* We set constraints on the substream according to the version of I2S */ staticint i2s_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
{ struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai); struct i2s_dai *i2s = to_info(dai); struct i2s_dai *other = get_other_dai(i2s); unsignedlong flags;
pm_runtime_get_sync(dai->dev);
spin_lock_irqsave(&priv->pcm_lock, flags);
i2s->mode |= DAI_OPENED;
if (is_manager(other))
i2s->mode &= ~DAI_MANAGER; else
i2s->mode |= DAI_MANAGER;
if (!any_active(i2s) && (priv->quirks & QUIRK_NEED_RSTCLR))
writel(CON_RSTCLR, i2s->priv->addr + I2SCON);
switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
pm_runtime_get_sync(dai->dev);
if (priv->fixup_early)
priv->fixup_early(substream, dai);
spin_lock_irqsave(&priv->lock, flags);
if (config_setup(i2s)) {
spin_unlock_irqrestore(&priv->lock, flags); return -EINVAL;
}
if (priv->fixup_late)
priv->fixup_late(substream, dai);
if (capture)
i2s_rxctrl(i2s, 1); else
i2s_txctrl(i2s, 1);
spin_unlock_irqrestore(&priv->lock, flags); break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
spin_lock_irqsave(&priv->lock, flags);
if (is_secondary(i2s)) { /* If this is probe on the secondary DAI */
snd_soc_dai_init_dma_data(dai, &i2s->dma_playback, NULL);
} else {
snd_soc_dai_init_dma_data(dai, &i2s->dma_playback,
&i2s->dma_capture);
if (priv->quirks & QUIRK_NEED_RSTCLR)
writel(CON_RSTCLR, priv->addr + I2SCON);
if (priv->quirks & QUIRK_SUPPORTS_IDMA)
idma_reg_addr_init(priv->addr,
other->idma_playback.addr);
}
/* Reset any constraint on RFS and BFS */
i2s->rfs = 0;
i2s->bfs = 0;
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.