Commit 77c92d2b authored by Mark Brown's avatar Mark Brown

Merge remote-tracking branch 'asoc/topic/sti' into asoc-next

parents 38e3c63d ee4c879b
......@@ -37,17 +37,18 @@ Required properties:
- dai-name: DAI name that describes the IP.
- IP mode: IP working mode depending on associated codec.
"HDMI" connected to HDMI codec and support IEC HDMI formats (player only).
"SPDIF" connected to SPDIF codec and support SPDIF formats (player only).
"PCM" PCM standard mode for I2S or TDM bus.
"TDM" TDM mode for TDM bus.
Required properties ("st,sti-uni-player" compatibility only):
- clocks: CPU_DAI IP clock source, listed in the same order than the
CPU_DAI properties.
- uniperiph-id: internal SOC IP instance ID.
- IP mode: IP working mode depending on associated codec.
"HDMI" connected to HDMI codec IP and IEC HDMI formats.
"SPDIF"connected to SPDIF codec and support SPDIF formats.
"PCM" PCM standard mode for I2S or TDM bus.
Optional properties:
- pinctrl-0: defined for CPU_DAI@1 and CPU_DAI@4 to describe I2S PIOs for
external codecs connection.
......@@ -56,6 +57,22 @@ Optional properties:
Example:
sti_uni_player1: sti-uni-player@1 {
compatible = "st,sti-uni-player";
status = "okay";
#sound-dai-cells = <0>;
st,syscfg = <&syscfg_core>;
clocks = <&clk_s_d0_flexgen CLK_PCM_1>;
reg = <0x8D81000 0x158>;
interrupts = <GIC_SPI 85 IRQ_TYPE_NONE>;
dmas = <&fdma0 3 0 1>;
st,dai-name = "Uni Player #1 (I2S)";
dma-names = "tx";
st,uniperiph-id = <1>;
st,version = <5>;
st,mode = "TDM";
};
sti_uni_player2: sti-uni-player@2 {
compatible = "st,sti-uni-player";
status = "okay";
......@@ -65,7 +82,7 @@ Example:
reg = <0x8D82000 0x158>;
interrupts = <GIC_SPI 86 IRQ_TYPE_NONE>;
dmas = <&fdma0 4 0 1>;
dai-name = "Uni Player #1 (DAC)";
dai-name = "Uni Player #2 (DAC)";
dma-names = "tx";
uniperiph-id = <2>;
version = <5>;
......@@ -82,7 +99,7 @@ Example:
interrupts = <GIC_SPI 89 IRQ_TYPE_NONE>;
dmas = <&fdma0 7 0 1>;
dma-names = "tx";
dai-name = "Uni Player #1 (PIO)";
dai-name = "Uni Player #3 (SPDIF)";
uniperiph-id = <3>;
version = <5>;
mode = "SPDIF";
......@@ -99,6 +116,7 @@ Example:
dma-names = "rx";
dai-name = "Uni Reader #1 (HDMI RX)";
version = <3>;
st,mode = "PCM";
};
2) sti-sas-codec: internal audio codec IPs driver
......@@ -152,4 +170,20 @@ Example of audio card declaration:
sound-dai = <&sti_sasg_codec 0>;
};
};
simple-audio-card,dai-link@2 {
/* TDM playback */
format = "left_j";
frame-inversion = <1>;
cpu {
sound-dai = <&sti_uni_player1>;
dai-tdm-slot-num = <16>;
dai-tdm-slot-width = <16>;
dai-tdm-slot-tx-mask =
<1 1 1 1 0 0 0 0 0 0 1 1 0 0 1 1>;
};
codec {
sound-dai = <&sti_sasg_codec 3>;
};
};
};
......@@ -10,6 +10,142 @@
#include "uniperif.h"
/*
* User frame size shall be 2, 4, 6 or 8 32-bits words length
* (i.e. 8, 16, 24 or 32 bytes)
* This constraint comes from allowed values for
* UNIPERIF_I2S_FMT_NUM_CH register
*/
#define UNIPERIF_MAX_FRAME_SZ 0x20
#define UNIPERIF_ALLOWED_FRAME_SZ (0x08 | 0x10 | 0x18 | UNIPERIF_MAX_FRAME_SZ)
int sti_uniperiph_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
unsigned int rx_mask, int slots,
int slot_width)
{
struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai);
struct uniperif *uni = priv->dai_data.uni;
int i, frame_size, avail_slots;
if (!UNIPERIF_TYPE_IS_TDM(uni)) {
dev_err(uni->dev, "cpu dai not in tdm mode\n");
return -EINVAL;
}
/* store info in unip context */
uni->tdm_slot.slots = slots;
uni->tdm_slot.slot_width = slot_width;
/* unip is unidirectionnal */
uni->tdm_slot.mask = (tx_mask != 0) ? tx_mask : rx_mask;
/* number of available timeslots */
for (i = 0, avail_slots = 0; i < uni->tdm_slot.slots; i++) {
if ((uni->tdm_slot.mask >> i) & 0x01)
avail_slots++;
}
uni->tdm_slot.avail_slots = avail_slots;
/* frame size in bytes */
frame_size = uni->tdm_slot.avail_slots * uni->tdm_slot.slot_width / 8;
/* check frame size is allowed */
if ((frame_size > UNIPERIF_MAX_FRAME_SZ) ||
(frame_size & ~(int)UNIPERIF_ALLOWED_FRAME_SZ)) {
dev_err(uni->dev, "frame size not allowed: %d bytes\n",
frame_size);
return -EINVAL;
}
return 0;
}
int sti_uniperiph_fix_tdm_chan(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule)
{
struct uniperif *uni = rule->private;
struct snd_interval t;
t.min = uni->tdm_slot.avail_slots;
t.max = uni->tdm_slot.avail_slots;
t.openmin = 0;
t.openmax = 0;
t.integer = 0;
return snd_interval_refine(hw_param_interval(params, rule->var), &t);
}
int sti_uniperiph_fix_tdm_format(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule)
{
struct uniperif *uni = rule->private;
struct snd_mask *maskp = hw_param_mask(params, rule->var);
u64 format;
switch (uni->tdm_slot.slot_width) {
case 16:
format = SNDRV_PCM_FMTBIT_S16_LE;
break;
case 32:
format = SNDRV_PCM_FMTBIT_S32_LE;
break;
default:
dev_err(uni->dev, "format not supported: %d bits\n",
uni->tdm_slot.slot_width);
return -EINVAL;
}
maskp->bits[0] &= (u_int32_t)format;
maskp->bits[1] &= (u_int32_t)(format >> 32);
/* clear remaining indexes */
memset(maskp->bits + 2, 0, (SNDRV_MASK_MAX - 64) / 8);
if (!maskp->bits[0] && !maskp->bits[1])
return -EINVAL;
return 0;
}
int sti_uniperiph_get_tdm_word_pos(struct uniperif *uni,
unsigned int *word_pos)
{
int slot_width = uni->tdm_slot.slot_width / 8;
int slots_num = uni->tdm_slot.slots;
unsigned int slots_mask = uni->tdm_slot.mask;
int i, j, k;
unsigned int word16_pos[4];
/* word16_pos:
* word16_pos[0] = WORDX_LSB
* word16_pos[1] = WORDX_MSB,
* word16_pos[2] = WORDX+1_LSB
* word16_pos[3] = WORDX+1_MSB
*/
/* set unip word position */
for (i = 0, j = 0, k = 0; (i < slots_num) && (k < WORD_MAX); i++) {
if ((slots_mask >> i) & 0x01) {
word16_pos[j] = i * slot_width;
if (slot_width == 4) {
word16_pos[j + 1] = word16_pos[j] + 2;
j++;
}
j++;
if (j > 3) {
word_pos[k] = word16_pos[1] |
(word16_pos[0] << 8) |
(word16_pos[3] << 16) |
(word16_pos[2] << 24);
j = 0;
k++;
}
}
}
return 0;
}
/*
* sti_uniperiph_dai_create_ctrl
* This function is used to create Ctrl associated to DAI but also pcm device.
......@@ -45,10 +181,16 @@ int sti_uniperiph_dai_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai);
struct uniperif *uni = priv->dai_data.uni;
struct snd_dmaengine_dai_dma_data *dma_data;
int transfer_size;
transfer_size = params_channels(params) * UNIPERIF_FIFO_FRAMES;
if (uni->info->type == SND_ST_UNIPERIF_TYPE_TDM)
/* transfer size = user frame size (in 32-bits FIFO cell) */
transfer_size = snd_soc_params_to_frame_size(params) / 32;
else
transfer_size = params_channels(params) * UNIPERIF_FIFO_FRAMES;
dma_data = snd_soc_dai_get_dma_data(dai, substream);
dma_data->maxburst = transfer_size;
......
This diff is collapsed.
This diff is collapsed.
......@@ -73,55 +73,10 @@ static irqreturn_t uni_reader_irq_handler(int irq, void *dev_id)
return ret;
}
static int uni_reader_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
static int uni_reader_prepare_pcm(struct snd_pcm_runtime *runtime,
struct uniperif *reader)
{
struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai);
struct uniperif *reader = priv->dai_data.uni;
struct snd_pcm_runtime *runtime = substream->runtime;
int transfer_size, trigger_limit;
int slot_width;
int count = 10;
/* The reader should be stopped */
if (reader->state != UNIPERIF_STATE_STOPPED) {
dev_err(reader->dev, "%s: invalid reader state %d", __func__,
reader->state);
return -EINVAL;
}
/* Calculate transfer size (in fifo cells and bytes) for frame count */
transfer_size = runtime->channels * UNIPERIF_FIFO_FRAMES;
/* Calculate number of empty cells available before asserting DREQ */
if (reader->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0)
trigger_limit = UNIPERIF_FIFO_SIZE - transfer_size;
else
/*
* Since SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0
* FDMA_TRIGGER_LIMIT also controls when the state switches
* from OFF or STANDBY to AUDIO DATA.
*/
trigger_limit = transfer_size;
/* Trigger limit must be an even number */
if ((!trigger_limit % 2) ||
(trigger_limit != 1 && transfer_size % 2) ||
(trigger_limit > UNIPERIF_CONFIG_DMA_TRIG_LIMIT_MASK(reader))) {
dev_err(reader->dev, "invalid trigger limit %d", trigger_limit);
return -EINVAL;
}
SET_UNIPERIF_CONFIG_DMA_TRIG_LIMIT(reader, trigger_limit);
switch (reader->daifmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_IB_IF:
case SND_SOC_DAIFMT_NB_IF:
SET_UNIPERIF_I2S_FMT_LR_POL_HIG(reader);
break;
default:
SET_UNIPERIF_I2S_FMT_LR_POL_LOW(reader);
}
/* Force slot width to 32 in I2S mode */
if ((reader->daifmt & SND_SOC_DAIFMT_FORMAT_MASK)
......@@ -173,6 +128,109 @@ static int uni_reader_prepare(struct snd_pcm_substream *substream,
return -EINVAL;
}
/* Number of channels must be even */
if ((runtime->channels % 2) || (runtime->channels < 2) ||
(runtime->channels > 10)) {
dev_err(reader->dev, "%s: invalid nb of channels", __func__);
return -EINVAL;
}
SET_UNIPERIF_I2S_FMT_NUM_CH(reader, runtime->channels / 2);
SET_UNIPERIF_I2S_FMT_ORDER_MSB(reader);
return 0;
}
static int uni_reader_prepare_tdm(struct snd_pcm_runtime *runtime,
struct uniperif *reader)
{
int frame_size; /* user tdm frame size in bytes */
/* default unip TDM_WORD_POS_X_Y */
unsigned int word_pos[4] = {
0x04060002, 0x0C0E080A, 0x14161012, 0x1C1E181A};
frame_size = sti_uniperiph_get_user_frame_size(runtime);
/* fix 16/0 format */
SET_UNIPERIF_CONFIG_MEM_FMT_16_0(reader);
SET_UNIPERIF_I2S_FMT_DATA_SIZE_32(reader);
/* number of words inserted on the TDM line */
SET_UNIPERIF_I2S_FMT_NUM_CH(reader, frame_size / 4 / 2);
SET_UNIPERIF_I2S_FMT_ORDER_MSB(reader);
SET_UNIPERIF_I2S_FMT_ALIGN_LEFT(reader);
SET_UNIPERIF_TDM_ENABLE_TDM_ENABLE(reader);
/*
* set the timeslots allocation for words in FIFO
*
* HW bug: (LSB word < MSB word) => this config is not possible
* So if we want (LSB word < MSB) word, then it shall be
* handled by user
*/
sti_uniperiph_get_tdm_word_pos(reader, word_pos);
SET_UNIPERIF_TDM_WORD_POS(reader, 1_2, word_pos[WORD_1_2]);
SET_UNIPERIF_TDM_WORD_POS(reader, 3_4, word_pos[WORD_3_4]);
SET_UNIPERIF_TDM_WORD_POS(reader, 5_6, word_pos[WORD_5_6]);
SET_UNIPERIF_TDM_WORD_POS(reader, 7_8, word_pos[WORD_7_8]);
return 0;
}
static int uni_reader_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai);
struct uniperif *reader = priv->dai_data.uni;
struct snd_pcm_runtime *runtime = substream->runtime;
int transfer_size, trigger_limit, ret;
int count = 10;
/* The reader should be stopped */
if (reader->state != UNIPERIF_STATE_STOPPED) {
dev_err(reader->dev, "%s: invalid reader state %d", __func__,
reader->state);
return -EINVAL;
}
/* Calculate transfer size (in fifo cells and bytes) for frame count */
if (reader->info->type == SND_ST_UNIPERIF_TYPE_TDM) {
/* transfer size = unip frame size (in 32 bits FIFO cell) */
transfer_size =
sti_uniperiph_get_user_frame_size(runtime) / 4;
} else {
transfer_size = runtime->channels * UNIPERIF_FIFO_FRAMES;
}
/* Calculate number of empty cells available before asserting DREQ */
if (reader->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0)
trigger_limit = UNIPERIF_FIFO_SIZE - transfer_size;
else
/*
* Since SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0
* FDMA_TRIGGER_LIMIT also controls when the state switches
* from OFF or STANDBY to AUDIO DATA.
*/
trigger_limit = transfer_size;
/* Trigger limit must be an even number */
if ((!trigger_limit % 2) ||
(trigger_limit != 1 && transfer_size % 2) ||
(trigger_limit > UNIPERIF_CONFIG_DMA_TRIG_LIMIT_MASK(reader))) {
dev_err(reader->dev, "invalid trigger limit %d", trigger_limit);
return -EINVAL;
}
SET_UNIPERIF_CONFIG_DMA_TRIG_LIMIT(reader, trigger_limit);
if (UNIPERIF_TYPE_IS_TDM(reader))
ret = uni_reader_prepare_tdm(runtime, reader);
else
ret = uni_reader_prepare_pcm(runtime, reader);
if (ret)
return ret;
switch (reader->daifmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
SET_UNIPERIF_I2S_FMT_ALIGN_LEFT(reader);
......@@ -191,21 +249,26 @@ static int uni_reader_prepare(struct snd_pcm_substream *substream,
return -EINVAL;
}
SET_UNIPERIF_I2S_FMT_ORDER_MSB(reader);
/* Data clocking (changing) on the rising edge */
SET_UNIPERIF_I2S_FMT_SCLK_EDGE_RISING(reader);
/* Number of channels must be even */
if ((runtime->channels % 2) || (runtime->channels < 2) ||
(runtime->channels > 10)) {
dev_err(reader->dev, "%s: invalid nb of channels", __func__);
return -EINVAL;
/* Data clocking (changing) on the rising/falling edge */
switch (reader->daifmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
SET_UNIPERIF_I2S_FMT_LR_POL_LOW(reader);
SET_UNIPERIF_I2S_FMT_SCLK_EDGE_RISING(reader);
break;
case SND_SOC_DAIFMT_NB_IF:
SET_UNIPERIF_I2S_FMT_LR_POL_HIG(reader);
SET_UNIPERIF_I2S_FMT_SCLK_EDGE_RISING(reader);
break;
case SND_SOC_DAIFMT_IB_NF:
SET_UNIPERIF_I2S_FMT_LR_POL_LOW(reader);
SET_UNIPERIF_I2S_FMT_SCLK_EDGE_FALLING(reader);
break;
case SND_SOC_DAIFMT_IB_IF:
SET_UNIPERIF_I2S_FMT_LR_POL_HIG(reader);
SET_UNIPERIF_I2S_FMT_SCLK_EDGE_FALLING(reader);
break;
}
SET_UNIPERIF_I2S_FMT_NUM_CH(reader, runtime->channels / 2);
/* Clear any pending interrupts */
SET_UNIPERIF_ITS_BCLR(reader, GET_UNIPERIF_ITS(reader));
......@@ -293,6 +356,32 @@ static int uni_reader_trigger(struct snd_pcm_substream *substream,
}
}
static int uni_reader_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai);
struct uniperif *reader = priv->dai_data.uni;
int ret;
if (!UNIPERIF_TYPE_IS_TDM(reader))
return 0;
/* refine hw constraint in tdm mode */
ret = snd_pcm_hw_rule_add(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_CHANNELS,
sti_uniperiph_fix_tdm_chan,
reader, SNDRV_PCM_HW_PARAM_CHANNELS,
-1);
if (ret < 0)
return ret;
return snd_pcm_hw_rule_add(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_FORMAT,
sti_uniperiph_fix_tdm_format,
reader, SNDRV_PCM_HW_PARAM_FORMAT,
-1);
}
static void uni_reader_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
......@@ -310,6 +399,7 @@ static int uni_reader_parse_dt(struct platform_device *pdev,
{
struct uniperif_info *info;
struct device_node *node = pdev->dev.of_node;
const char *mode;
/* Allocate memory for the info structure */
info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
......@@ -322,6 +412,17 @@ static int uni_reader_parse_dt(struct platform_device *pdev,
return -EINVAL;
}
/* Read the device mode property */
if (of_property_read_string(node, "st,mode", &mode)) {
dev_err(&pdev->dev, "uniperipheral mode not defined");
return -EINVAL;
}
if (strcasecmp(mode, "tdm") == 0)
info->type = SND_ST_UNIPERIF_TYPE_TDM;
else
info->type = SND_ST_UNIPERIF_TYPE_PCM;
/* Save the info structure */
reader->info = info;
......@@ -329,11 +430,13 @@ static int uni_reader_parse_dt(struct platform_device *pdev,
}
static const struct snd_soc_dai_ops uni_reader_dai_ops = {
.startup = uni_reader_startup,
.shutdown = uni_reader_shutdown,
.prepare = uni_reader_prepare,
.trigger = uni_reader_trigger,
.hw_params = sti_uniperiph_dai_hw_params,
.set_fmt = sti_uniperiph_dai_set_fmt,
.set_tdm_slot = sti_uniperiph_set_tdm_slot
};
int uni_reader_init(struct platform_device *pdev,
......@@ -343,7 +446,6 @@ int uni_reader_init(struct platform_device *pdev,
reader->dev = &pdev->dev;
reader->state = UNIPERIF_STATE_STOPPED;
reader->hw = &uni_reader_pcm_hw;
reader->dai_ops = &uni_reader_dai_ops;
ret = uni_reader_parse_dt(pdev, reader);
......@@ -352,6 +454,11 @@ int uni_reader_init(struct platform_device *pdev,
return ret;
}
if (UNIPERIF_TYPE_IS_TDM(reader))
reader->hw = &uni_tdm_hw;
else
reader->hw = &uni_reader_pcm_hw;
ret = devm_request_irq(&pdev->dev, reader->irq,
uni_reader_irq_handler, IRQF_SHARED,
dev_name(&pdev->dev), reader);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment