Commit 3066987e authored by Mark Brown's avatar Mark Brown

ASoC: audio_graph_card2: Support variable slot widths

Merge series from Richard Fitzgerald <rf@opensource.cirrus.com>:

This adds support for I2S/TDM links where the slot width varies
depending on the sample width, in a way that cannot be guessed by
component hw_params().

A typical example is:

- 16-bit samples use 16-bit slots
- 24-bit samples use 32-bit slots

There is no way for a component hw_params() to deduce from the information
it is passed that 24-bit samples will be in 32-bit slots.

Some audio hardware cannot support a fixed slot width or a slot width
equal to the sample width in all cases. This is usually due either to
limitations of the audio serial port or system clocking restrictions.
parents fc14fac2 1e974e5b
......@@ -71,4 +71,24 @@ patternProperties:
description: CPU to Codec rate channels.
$ref: /schemas/types.yaml#/definitions/uint32
dai-tdm-slot-width-map:
description: Mapping of sample widths to slot widths. For hardware
that cannot support a fixed slot width or a slot width always
equal to sample width. A matrix of one or more 3-tuples.
$ref: /schemas/types.yaml#/definitions/uint32-matrix
items:
items:
-
description: Sample width in bits
minimum: 8
maximum: 64
-
description: Slot width in bits
minimum: 8
maximum: 256
-
description: Slot count
minimum: 1
maximum: 64
additionalProperties: true
......@@ -16,6 +16,12 @@
#define asoc_simple_init_mic(card, sjack, prefix) \
asoc_simple_init_jack(card, sjack, 0, prefix, NULL)
struct asoc_simple_tdm_width_map {
u8 sample_bits;
u8 slot_count;
u16 slot_width;
};
struct asoc_simple_dai {
const char *name;
unsigned int sysclk;
......@@ -26,6 +32,8 @@ struct asoc_simple_dai {
unsigned int rx_slot_mask;
struct clk *clk;
bool clk_fixed;
struct asoc_simple_tdm_width_map *tdm_width_map;
int n_tdm_widths;
};
struct asoc_simple_data {
......@@ -132,6 +140,9 @@ int asoc_simple_parse_daifmt(struct device *dev,
struct device_node *codec,
char *prefix,
unsigned int *retfmt);
int asoc_simple_parse_tdm_width_map(struct device *dev, struct device_node *np,
struct asoc_simple_dai *dai);
__printf(3, 4)
int asoc_simple_set_dailink_name(struct device *dev,
struct snd_soc_dai_link *dai_link,
......
......@@ -503,6 +503,10 @@ static int __graph_parse_node(struct asoc_simple_priv *priv,
if (ret < 0)
return ret;
ret = asoc_simple_parse_tdm_width_map(dev, ep, dai);
if (ret < 0)
return ret;
ret = asoc_simple_parse_clk(dev, ep, dai, dlc);
if (ret < 0)
return ret;
......
......@@ -12,6 +12,7 @@
#include <linux/of_gpio.h>
#include <linux/of_graph.h>
#include <sound/jack.h>
#include <sound/pcm_params.h>
#include <sound/simple_card_utils.h>
void asoc_simple_convert_fixup(struct asoc_simple_data *data,
......@@ -87,6 +88,51 @@ int asoc_simple_parse_daifmt(struct device *dev,
}
EXPORT_SYMBOL_GPL(asoc_simple_parse_daifmt);
int asoc_simple_parse_tdm_width_map(struct device *dev, struct device_node *np,
struct asoc_simple_dai *dai)
{
u32 *array_values, *p;
int n, i, ret;
if (!of_property_read_bool(np, "dai-tdm-slot-width-map"))
return 0;
n = of_property_count_elems_of_size(np, "dai-tdm-slot-width-map", sizeof(u32));
if (n % 3) {
dev_err(dev, "Invalid number of cells for dai-tdm-slot-width-map\n");
return -EINVAL;
}
dai->tdm_width_map = devm_kcalloc(dev, n, sizeof(*dai->tdm_width_map), GFP_KERNEL);
if (!dai->tdm_width_map)
return -ENOMEM;
array_values = kcalloc(n, sizeof(*array_values), GFP_KERNEL);
if (!array_values)
return -ENOMEM;
ret = of_property_read_u32_array(np, "dai-tdm-slot-width-map", array_values, n);
if (ret < 0) {
dev_err(dev, "Could not read dai-tdm-slot-width-map: %d\n", ret);
goto out;
}
p = array_values;
for (i = 0; i < n / 3; ++i) {
dai->tdm_width_map[i].sample_bits = *p++;
dai->tdm_width_map[i].slot_width = *p++;
dai->tdm_width_map[i].slot_count = *p++;
}
dai->n_tdm_widths = i;
ret = 0;
out:
kfree(array_values);
return ret;
}
EXPORT_SYMBOL_GPL(asoc_simple_parse_tdm_width_map);
int asoc_simple_set_dailink_name(struct device *dev,
struct snd_soc_dai_link *dai_link,
const char *fmt, ...)
......@@ -309,6 +355,42 @@ static int asoc_simple_set_clk_rate(struct device *dev,
return clk_set_rate(simple_dai->clk, rate);
}
static int asoc_simple_set_tdm(struct snd_soc_dai *dai,
struct asoc_simple_dai *simple_dai,
struct snd_pcm_hw_params *params)
{
int sample_bits = params_width(params);
int slot_width = simple_dai->slot_width;
int slot_count = simple_dai->slots;
int i, ret;
if (!simple_dai || !simple_dai->tdm_width_map)
return 0;
if (slot_width == 0)
slot_width = sample_bits;
for (i = 0; i < simple_dai->n_tdm_widths; ++i) {
if (simple_dai->tdm_width_map[i].sample_bits == sample_bits) {
slot_width = simple_dai->tdm_width_map[i].slot_width;
slot_count = simple_dai->tdm_width_map[i].slot_count;
break;
}
}
ret = snd_soc_dai_set_tdm_slot(dai,
simple_dai->tx_slot_mask,
simple_dai->rx_slot_mask,
slot_count,
slot_width);
if (ret && ret != -ENOTSUPP) {
dev_err(dai->dev, "simple-card: set_tdm_slot error: %d\n", ret);
return ret;
}
return 0;
}
int asoc_simple_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
......@@ -362,6 +444,21 @@ int asoc_simple_hw_params(struct snd_pcm_substream *substream,
return ret;
}
}
for_each_prop_dai_codec(props, i, pdai) {
sdai = asoc_rtd_to_codec(rtd, i);
ret = asoc_simple_set_tdm(sdai, pdai, params);
if (ret < 0)
return ret;
}
for_each_prop_dai_cpu(props, i, pdai) {
sdai = asoc_rtd_to_cpu(rtd, i);
ret = asoc_simple_set_tdm(sdai, pdai, params);
if (ret < 0)
return ret;
}
return 0;
}
EXPORT_SYMBOL_GPL(asoc_simple_hw_params);
......
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