staticconststruct reg_default tegra210_i2s_reg_defaults[] = {
{ TEGRA210_I2S_RX_INT_MASK, 0x00000003 },
{ TEGRA210_I2S_RX_CIF_CTRL, 0x00007700 },
{ TEGRA210_I2S_TX_INT_MASK, 0x00000003 },
{ TEGRA210_I2S_TX_CIF_CTRL, 0x00007700 },
{ TEGRA210_I2S_CG, 0x1 },
{ TEGRA210_I2S_TIMING, 0x0000001f },
{ TEGRA210_I2S_ENABLE, 0x1 }, /* * Below update does not have any effect on Tegra186 and Tegra194. * On Tegra210, I2S4 has "i2s4a" and "i2s4b" pins and below update * is required to select i2s4b for it to be functional for I2S * operation.
*/
{ TEGRA210_I2S_CYA, 0x1 },
};
/* No need to set rates if I2S is being operated in slave */ if (!(val & I2S_CTRL_MASTER_EN)) return 0;
err = clk_set_rate(i2s->clk_i2s, clock_rate); if (err) {
dev_err(dev, "can't set I2S bit clock rate %u, err: %d\n",
clock_rate, err); return err;
}
if (!IS_ERR(i2s->clk_sync_input)) { /* * Other I/O modules in AHUB can use i2s bclk as reference * clock. Below sets sync input clock rate as per bclk, * which can be used as input to other I/O modules.
*/
err = clk_set_rate(i2s->clk_sync_input, clock_rate); if (err) {
dev_err(dev, "can't set I2S sync input rate %u, err = %d\n",
clock_rate, err); return err;
}
}
/* Ensure I2S is in disabled state before new session */
err = regmap_read_poll_timeout(i2s->regmap, status_reg, val,
!(val & I2S_EN_MASK & I2S_EN),
10, 10000); if (err) {
dev_err(dev, "timeout: previous I2S %s is still active\n",
snd_pcm_direction_name(stream)); return err;
}
mask = I2S_CTRL_MASTER_EN_MASK; switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { case SND_SOC_DAIFMT_BC_FC:
val = 0; break; case SND_SOC_DAIFMT_BP_FP:
val = I2S_CTRL_MASTER_EN; break; default: return -EINVAL;
}
mask |= I2S_CTRL_FRAME_FMT_MASK | I2S_CTRL_LRCK_POL_MASK; switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_DSP_A:
val |= I2S_CTRL_FRAME_FMT_FSYNC_MODE;
val |= I2S_CTRL_LRCK_POL_HIGH;
tegra210_i2s_set_data_offset(i2s, 1); break; case SND_SOC_DAIFMT_DSP_B:
val |= I2S_CTRL_FRAME_FMT_FSYNC_MODE;
val |= I2S_CTRL_LRCK_POL_HIGH;
tegra210_i2s_set_data_offset(i2s, 0); break; /* I2S mode has data offset of 1 */ case SND_SOC_DAIFMT_I2S:
val |= I2S_CTRL_FRAME_FMT_LRCK_MODE;
val |= I2S_CTRL_LRCK_POL_LOW;
tegra210_i2s_set_data_offset(i2s, 1); break; /* * For RJ mode data offset is dependent on the sample size * and the bclk ratio, and so is set when hw_params is called.
*/ case SND_SOC_DAIFMT_RIGHT_J:
val |= I2S_CTRL_FRAME_FMT_LRCK_MODE;
val |= I2S_CTRL_LRCK_POL_HIGH; break; case SND_SOC_DAIFMT_LEFT_J:
val |= I2S_CTRL_FRAME_FMT_LRCK_MODE;
val |= I2S_CTRL_LRCK_POL_HIGH;
tegra210_i2s_set_data_offset(i2s, 0); break; default: return -EINVAL;
}
mask |= I2S_CTRL_EDGE_CTRL_MASK; switch (fmt & SND_SOC_DAIFMT_INV_MASK) { case SND_SOC_DAIFMT_NB_NF:
val |= I2S_CTRL_EDGE_CTRL_POS_EDGE; break; case SND_SOC_DAIFMT_NB_IF:
val |= I2S_CTRL_EDGE_CTRL_POS_EDGE;
val ^= I2S_CTRL_LRCK_POL_MASK; break; case SND_SOC_DAIFMT_IB_NF:
val |= I2S_CTRL_EDGE_CTRL_NEG_EDGE; break; case SND_SOC_DAIFMT_IB_IF:
val |= I2S_CTRL_EDGE_CTRL_NEG_EDGE;
val ^= I2S_CTRL_LRCK_POL_MASK; break; default: return -EINVAL;
}
/* * Frame sync width is used only for FSYNC modes and not * applicable for LRCK modes. Reset value for this field is "0", * which means the width is one bit clock wide. * The width requirement may depend on the codec and in such * cases mixer control is used to update custom values. A value * of "N" here means, width is "N + 1" bit clock wide.
*/
regmap_update_bits(i2s->regmap, TEGRA210_I2S_CTRL + i2s->soc_data->i2s_ctrl_offset,
i2s->soc_data->fsync_width_mask,
i2s->fsync_width << i2s->soc_data->fsync_width_shift);
/* * For LRCK mode, channel bit count depends on number of bit clocks * on the left channel, where as for FSYNC mode bit count depends on * the number of bit clocks in both left and right channels for DSP * mode or the number of bit clocks in one TDM frame. *
*/ switch (val & I2S_CTRL_FRAME_FMT_MASK) { case I2S_CTRL_FRAME_FMT_LRCK_MODE:
bit_count = (bclk_rate / (srate * 2)) - 1; break; case I2S_CTRL_FRAME_FMT_FSYNC_MODE:
bit_count = (bclk_rate / srate) - 1;
/* For playback I2S RX-CIF and for capture TX-CIF is used */ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
path = I2S_RX_PATH; else
path = I2S_TX_PATH;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { unsignedint max_th;
/* FIFO threshold in terms of frames */
max_th = (I2S_RX_FIFO_DEPTH / cif_conf.audio_ch) - 1;
if (i2s->rx_fifo_th > max_th)
i2s->rx_fifo_th = max_th;
switch (reg) { case TEGRA210_I2S_RX_STATUS: case TEGRA210_I2S_RX_INT_STATUS: case TEGRA210_I2S_RX_CIF_FIFO_STATUS: case TEGRA210_I2S_TX_STATUS: case TEGRA210_I2S_TX_INT_STATUS: case TEGRA210_I2S_TX_CIF_FIFO_STATUS: case TEGRA210_I2S_STATUS: case TEGRA210_I2S_INT_STATUS: returntrue; default: returnfalse;
}
}
staticbool tegra210_i2s_volatile_reg(struct device *dev, unsignedint reg)
{ switch (reg) { case TEGRA210_I2S_RX_STATUS: case TEGRA210_I2S_RX_INT_STATUS: case TEGRA210_I2S_RX_CIF_FIFO_STATUS: case TEGRA210_I2S_TX_STATUS: case TEGRA210_I2S_TX_INT_STATUS: case TEGRA210_I2S_TX_CIF_FIFO_STATUS: case TEGRA210_I2S_STATUS: case TEGRA210_I2S_INT_STATUS: case TEGRA210_I2S_RX_SOFT_RESET: case TEGRA210_I2S_TX_SOFT_RESET: returntrue; default: returnfalse;
}
}
staticbool tegra264_i2s_wr_reg(struct device *dev, unsignedint reg)
{ switch (reg) { case TEGRA210_I2S_RX_ENABLE ... TEGRA210_I2S_RX_SOFT_RESET: case TEGRA210_I2S_RX_INT_MASK ... TEGRA264_I2S_RX_CYA: case TEGRA264_I2S_TX_ENABLE ... TEGRA264_I2S_TX_SOFT_RESET: case TEGRA264_I2S_TX_INT_MASK ... TEGRA264_I2S_TX_FIFO_RD_ACCESS_MODE: case TEGRA264_I2S_TX_FIFO_THRESHOLD ... TEGRA264_I2S_TX_CYA: case TEGRA264_I2S_ENABLE ... TEGRA264_I2S_CG: case TEGRA264_I2S_INT_SET ... TEGRA264_I2S_INT_MASK: case TEGRA264_I2S_CTRL ... TEGRA264_I2S_CYA: returntrue; default: returnfalse;
};
}
switch (reg) { case TEGRA210_I2S_RX_STATUS: case TEGRA210_I2S_RX_INT_STATUS: case TEGRA264_I2S_RX_CIF_FIFO_STATUS: case TEGRA264_I2S_TX_STATUS: case TEGRA264_I2S_TX_INT_STATUS: case TEGRA264_I2S_TX_FIFO_RD_DATA: case TEGRA264_I2S_TX_CIF_FIFO_STATUS: case TEGRA264_I2S_STATUS: case TEGRA264_I2S_INT_STATUS: case TEGRA264_I2S_PIO_MODE_ENABLE: case TEGRA264_I2S_PAD_MACRO_STATUS: returntrue; default: returnfalse;
};
}
staticbool tegra264_i2s_volatile_reg(struct device *dev, unsignedint reg)
{ switch (reg) { case TEGRA210_I2S_RX_SOFT_RESET: case TEGRA210_I2S_RX_STATUS: case TEGRA210_I2S_RX_INT_STATUS: case TEGRA264_I2S_RX_CIF_FIFO_STATUS: case TEGRA264_I2S_TX_STATUS: case TEGRA264_I2S_TX_INT_STATUS: case TEGRA264_I2S_TX_FIFO_RD_DATA: case TEGRA264_I2S_TX_CIF_FIFO_STATUS: case TEGRA264_I2S_STATUS: case TEGRA264_I2S_INT_STATUS: case TEGRA264_I2S_TX_SOFT_RESET: case TEGRA264_I2S_PAD_MACRO_STATUS: returntrue; default: returnfalse;
};
}
/* * The AHUB HW modules are interconnected with CIF which are capable of * supporting Channel and Sample bit format conversion. This needs different * CIF Audio and client configuration. As one of the config comes from * params_channels() or params_format(), the extra configuration is passed from * CIF Port of DT I2S node which can help to perform this conversion. * * 4ch audio = 4ch client = 2ch 2ch * -----> ADMAIF -----------> CIF -------------> I2S ---->
*/ staticvoid tegra210_parse_client_convert(struct device *dev)
{ struct tegra210_i2s *i2s = dev_get_drvdata(dev); struct device_node *ports, *ep; struct simple_util_data data = {}; int cif_port = 0;
ports = of_get_child_by_name(dev->of_node, "ports"); if (ports) {
ep = of_graph_get_endpoint_by_regs(ports, cif_port, -1); if (ep) {
simple_util_parse_convert(ep, NULL, &data);
of_node_put(ep);
}
of_node_put(ports);
}
if (data.convert_channels)
i2s->client_channels = data.convert_channels;
if (data.convert_sample_format)
i2s->client_sample_format = simple_util_get_sample_fmt(&data);
}
i2s->clk_i2s = devm_clk_get(dev, "i2s"); if (IS_ERR(i2s->clk_i2s)) {
dev_err(dev, "can't retrieve I2S bit clock\n"); return PTR_ERR(i2s->clk_i2s);
}
/* * Not an error, as this clock is needed only when some other I/O * requires input clock from current I2S instance, which is * configurable from DT.
*/
i2s->clk_sync_input = devm_clk_get(dev, "sync_input"); if (IS_ERR(i2s->clk_sync_input))
dev_dbg(dev, "can't retrieve I2S sync input clock\n");
regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(regs)) return PTR_ERR(regs);
/* Update the dais max channel as per soc */ for (id = 0; id < ARRAY_SIZE(tegra210_i2s_dais); id++) {
tegra210_i2s_dais[id].playback.channels_max = i2s->soc_data->max_ch;
tegra210_i2s_dais[id].capture.channels_max = i2s->soc_data->max_ch;
}
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.