mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2025-01-19 04:14:49 +08:00
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.
This commit is contained in:
commit
3066987e11
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user