mirror of
https://github.com/torvalds/linux
synced 2024-11-05 18:23:50 +00:00
Merge remote-tracking branches 'asoc/topic/sgtl5000', 'asoc/topic/simple', 'asoc/topic/stac9766', 'asoc/topic/sti' and 'asoc/topic/sunxi' into asoc-next
This commit is contained in:
commit
5b56b9bb6c
14 changed files with 343 additions and 239 deletions
|
@ -22,6 +22,8 @@ Optional properties:
|
|||
headphones are attached.
|
||||
- simple-audio-card,mic-det-gpio : Reference to GPIO that signals when
|
||||
a microphone is attached.
|
||||
- simple-audio-card,aux-devs : List of phandles pointing to auxiliary devices, such
|
||||
as amplifiers, to be added to the sound card.
|
||||
|
||||
Optional subnodes:
|
||||
|
||||
|
@ -162,3 +164,38 @@ sound {
|
|||
};
|
||||
};
|
||||
};
|
||||
|
||||
Example 3 - route audio from IMX6 SSI2 through TLV320DAC3100 codec
|
||||
through TPA6130A2 amplifier to headphones:
|
||||
|
||||
&i2c0 {
|
||||
codec: tlv320dac3100@18 {
|
||||
compatible = "ti,tlv320dac3100";
|
||||
...
|
||||
}
|
||||
|
||||
amp: tpa6130a2@60 {
|
||||
compatible = "ti,tpa6130a2";
|
||||
...
|
||||
}
|
||||
}
|
||||
|
||||
sound {
|
||||
compatible = "simple-audio-card";
|
||||
...
|
||||
simple-audio-card,widgets =
|
||||
"Headphone", "Headphone Jack";
|
||||
simple-audio-card,routing =
|
||||
"Headphone Jack", "HPLEFT",
|
||||
"Headphone Jack", "HPRIGHT",
|
||||
"LEFTIN", "HPL",
|
||||
"RIGHTIN", "HPR";
|
||||
simple-audio-card,aux-devs = <&>;
|
||||
simple-audio-card,cpu {
|
||||
sound-dai = <&ssi2>;
|
||||
};
|
||||
simple-audio-card,codec {
|
||||
sound-dai = <&codec>;
|
||||
clocks = ...
|
||||
};
|
||||
};
|
||||
|
|
|
@ -11,7 +11,9 @@ Documentation/devicetree/bindings/sound/simple-card.txt.
|
|||
---------------------------------------
|
||||
|
||||
Required properties:
|
||||
- compatible: "st,sti-uni-player" or "st,sti-uni-reader"
|
||||
- compatible: "st,stih407-uni-player-hdmi", "st,stih407-uni-player-pcm-out",
|
||||
"st,stih407-uni-player-dac", "st,stih407-uni-player-spdif",
|
||||
"st,stih407-uni-reader-pcm_in", "st,stih407-uni-reader-hdmi",
|
||||
|
||||
- st,syscfg: phandle to boot-device system configuration registers
|
||||
|
||||
|
@ -33,32 +35,24 @@ Required properties:
|
|||
"tx" for "st,sti-uni-player" compatibility
|
||||
"rx" for "st,sti-uni-reader" compatibility
|
||||
|
||||
- st,version: IP version integrated in SOC.
|
||||
|
||||
- dai-name: DAI name that describes the IP.
|
||||
|
||||
- st,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.
|
||||
|
||||
- st,uniperiph-id: internal SOC IP instance ID.
|
||||
|
||||
Optional properties:
|
||||
- pinctrl-0: defined for CPU_DAI@1 and CPU_DAI@4 to describe I2S PIOs for
|
||||
external codecs connection.
|
||||
|
||||
- pinctrl-names: should contain only one value - "default".
|
||||
|
||||
- st,tdm-mode: to declare to set TDM mode for unireader and uniplayer IPs.
|
||||
Only compartible with IPs in charge of the external I2S/TDM bus.
|
||||
Should be declared depending on associated codec.
|
||||
|
||||
Example:
|
||||
|
||||
sti_uni_player1: sti-uni-player@1 {
|
||||
compatible = "st,sti-uni-player";
|
||||
sti_uni_player1: sti-uni-player@0x8D81000 {
|
||||
compatible = "st,stih407-uni-player-hdmi";
|
||||
status = "okay";
|
||||
#sound-dai-cells = <0>;
|
||||
st,syscfg = <&syscfg_core>;
|
||||
|
@ -66,15 +60,12 @@ Example:
|
|||
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";
|
||||
st,tdm-mode = <1>;
|
||||
};
|
||||
|
||||
sti_uni_player2: sti-uni-player@2 {
|
||||
compatible = "st,sti-uni-player";
|
||||
sti_uni_player2: sti-uni-player@0x8D82000 {
|
||||
compatible = "st,stih407-uni-player-pcm-out";
|
||||
status = "okay";
|
||||
#sound-dai-cells = <0>;
|
||||
st,syscfg = <&syscfg_core>;
|
||||
|
@ -82,15 +73,11 @@ Example:
|
|||
reg = <0x8D82000 0x158>;
|
||||
interrupts = <GIC_SPI 86 IRQ_TYPE_NONE>;
|
||||
dmas = <&fdma0 4 0 1>;
|
||||
dai-name = "Uni Player #2 (DAC)";
|
||||
dma-names = "tx";
|
||||
st,uniperiph-id = <2>;
|
||||
st,version = <5>;
|
||||
st,mode = "PCM";
|
||||
};
|
||||
|
||||
sti_uni_player3: sti-uni-player@3 {
|
||||
compatible = "st,sti-uni-player";
|
||||
sti_uni_player3: sti-uni-player@0x8D85000 {
|
||||
compatible = "st,stih407-uni-player-spdif";
|
||||
status = "okay";
|
||||
#sound-dai-cells = <0>;
|
||||
st,syscfg = <&syscfg_core>;
|
||||
|
@ -99,14 +86,10 @@ Example:
|
|||
interrupts = <GIC_SPI 89 IRQ_TYPE_NONE>;
|
||||
dmas = <&fdma0 7 0 1>;
|
||||
dma-names = "tx";
|
||||
dai-name = "Uni Player #3 (SPDIF)";
|
||||
st,uniperiph-id = <3>;
|
||||
st,version = <5>;
|
||||
st,mode = "SPDIF";
|
||||
};
|
||||
|
||||
sti_uni_reader1: sti-uni-reader@1 {
|
||||
compatible = "st,sti-uni-reader";
|
||||
sti_uni_reader1: sti-uni-reader@0x8D84000 {
|
||||
compatible = "st,stih407-uni-reader-hdmi";
|
||||
status = "disabled";
|
||||
#sound-dai-cells = <0>;
|
||||
st,syscfg = <&syscfg_core>;
|
||||
|
@ -114,9 +97,6 @@ Example:
|
|||
interrupts = <GIC_SPI 88 IRQ_TYPE_NONE>;
|
||||
dmas = <&fdma0 6 0 1>;
|
||||
dma-names = "rx";
|
||||
dai-name = "Uni Reader #1 (HDMI RX)";
|
||||
st,version = <3>;
|
||||
st,mode = "PCM";
|
||||
};
|
||||
|
||||
2) sti-sas-codec: internal audio codec IPs driver
|
||||
|
|
|
@ -9,6 +9,7 @@ Required properties:
|
|||
|
||||
- compatible : should be one of the following:
|
||||
- "allwinner,sun4i-a10-spdif": for the Allwinner A10 SoC
|
||||
- "allwinner,sun6i-a31-spdif": for the Allwinner A31 SoC
|
||||
|
||||
- reg : Offset and length of the register set for the device.
|
||||
|
||||
|
@ -25,6 +26,8 @@ Required properties:
|
|||
"apb" clock for the spdif bus.
|
||||
"spdif" clock for spdif controller.
|
||||
|
||||
- resets : reset specifier for the ahb reset (A31 and newer only)
|
||||
|
||||
Example:
|
||||
|
||||
spdif: spdif@01c21000 {
|
||||
|
|
|
@ -27,6 +27,7 @@ int asoc_simple_card_parse_daifmt(struct device *dev,
|
|||
struct device_node *codec,
|
||||
char *prefix,
|
||||
unsigned int *retfmt);
|
||||
__printf(3, 4)
|
||||
int asoc_simple_card_set_dailink_name(struct device *dev,
|
||||
struct snd_soc_dai_link *dai_link,
|
||||
const char *fmt, ...);
|
||||
|
|
|
@ -411,6 +411,8 @@ static const struct snd_kcontrol_new sgtl5000_snd_controls[] = {
|
|||
0, 8,
|
||||
0x7f, 1,
|
||||
headphone_volume),
|
||||
SOC_SINGLE("Headphone Playback Switch", SGTL5000_CHIP_ANA_CTRL,
|
||||
4, 1, 1),
|
||||
SOC_SINGLE("Headphone Playback ZC Switch", SGTL5000_CHIP_ANA_CTRL,
|
||||
5, 1, 0),
|
||||
|
||||
|
@ -423,6 +425,7 @@ static const struct snd_kcontrol_new sgtl5000_snd_controls[] = {
|
|||
SGTL5000_LINE_OUT_VOL_RIGHT_SHIFT,
|
||||
0x1f, 1,
|
||||
lineout_volume),
|
||||
SOC_SINGLE("Lineout Playback Switch", SGTL5000_CHIP_ANA_CTRL, 8, 1, 1),
|
||||
};
|
||||
|
||||
/* mute the codec used by alsa core */
|
||||
|
|
|
@ -85,10 +85,10 @@ static SOC_ENUM_SINGLE_DECL(stac9766_boost2_enum,
|
|||
static SOC_ENUM_SINGLE_DECL(stac9766_stereo_mic_enum,
|
||||
AC97_STAC_STEREO_MIC, 2, stac9766_stereo_mic);
|
||||
|
||||
static const DECLARE_TLV_DB_LINEAR(master_tlv, -4600, 0);
|
||||
static const DECLARE_TLV_DB_LINEAR(record_tlv, 0, 2250);
|
||||
static const DECLARE_TLV_DB_LINEAR(beep_tlv, -4500, 0);
|
||||
static const DECLARE_TLV_DB_LINEAR(mix_tlv, -3450, 1200);
|
||||
static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(master_tlv, -4650, 150, 0);
|
||||
static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(record_tlv, 0, 150, 0);
|
||||
static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(beep_tlv, -4500, 300, 0);
|
||||
static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(mix_tlv, -3450, 150, 0);
|
||||
|
||||
static const struct snd_kcontrol_new stac9766_snd_ac97_controls[] = {
|
||||
SOC_DOUBLE_TLV("Speaker Volume", AC97_MASTER, 8, 0, 31, 1, master_tlv),
|
||||
|
|
|
@ -37,12 +37,12 @@ struct simple_card_data {
|
|||
unsigned int mclk_fs;
|
||||
struct asoc_simple_jack hp_jack;
|
||||
struct asoc_simple_jack mic_jack;
|
||||
struct snd_soc_dai_link dai_link[]; /* dynamically allocated */
|
||||
struct snd_soc_dai_link *dai_link;
|
||||
};
|
||||
|
||||
#define simple_priv_to_dev(priv) ((priv)->snd_card.dev)
|
||||
#define simple_priv_to_link(priv, i) ((priv)->snd_card.dai_link + i)
|
||||
#define simple_priv_to_props(priv, i) ((priv)->dai_props + i)
|
||||
#define simple_priv_to_link(priv, i) ((priv)->snd_card.dai_link + (i))
|
||||
#define simple_priv_to_props(priv, i) ((priv)->dai_props + (i))
|
||||
|
||||
#define DAI "sound-dai"
|
||||
#define CELL "#sound-dai-cells"
|
||||
|
@ -114,13 +114,13 @@ static int asoc_simple_card_startup(struct snd_pcm_substream *substream)
|
|||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
|
||||
struct simple_dai_props *dai_props =
|
||||
&priv->dai_props[rtd->num];
|
||||
simple_priv_to_props(priv, rtd->num);
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(dai_props->cpu_dai.clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
||||
ret = clk_prepare_enable(dai_props->codec_dai.clk);
|
||||
if (ret)
|
||||
clk_disable_unprepare(dai_props->cpu_dai.clk);
|
||||
|
@ -133,7 +133,7 @@ static void asoc_simple_card_shutdown(struct snd_pcm_substream *substream)
|
|||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
|
||||
struct simple_dai_props *dai_props =
|
||||
&priv->dai_props[rtd->num];
|
||||
simple_priv_to_props(priv, rtd->num);
|
||||
|
||||
clk_disable_unprepare(dai_props->cpu_dai.clk);
|
||||
|
||||
|
@ -147,7 +147,8 @@ static int asoc_simple_card_hw_params(struct snd_pcm_substream *substream,
|
|||
struct snd_soc_dai *codec_dai = rtd->codec_dai;
|
||||
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
||||
struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
|
||||
struct simple_dai_props *dai_props = &priv->dai_props[rtd->num];
|
||||
struct simple_dai_props *dai_props =
|
||||
simple_priv_to_props(priv, rtd->num);
|
||||
unsigned int mclk, mclk_fs = 0;
|
||||
int ret = 0;
|
||||
|
||||
|
@ -184,7 +185,8 @@ static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd)
|
|||
struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
|
||||
struct snd_soc_dai *codec = rtd->codec_dai;
|
||||
struct snd_soc_dai *cpu = rtd->cpu_dai;
|
||||
struct simple_dai_props *dai_props = &priv->dai_props[rtd->num];
|
||||
struct simple_dai_props *dai_props =
|
||||
simple_priv_to_props(priv, rtd->num);
|
||||
int ret;
|
||||
|
||||
ret = asoc_simple_card_init_dai(codec, &dai_props->codec_dai);
|
||||
|
@ -222,7 +224,6 @@ static int asoc_simple_card_dai_link_of(struct device_node *node,
|
|||
char prop[128];
|
||||
char *prefix = "";
|
||||
int ret, single_cpu;
|
||||
u32 val;
|
||||
|
||||
/* For single DAI link & old style of DT node */
|
||||
if (is_top_level_node)
|
||||
|
@ -248,8 +249,7 @@ static int asoc_simple_card_dai_link_of(struct device_node *node,
|
|||
if (ret < 0)
|
||||
goto dai_link_of_err;
|
||||
|
||||
if (!of_property_read_u32(node, "mclk-fs", &val))
|
||||
dai_props->mclk_fs = val;
|
||||
of_property_read_u32(node, "mclk-fs", &dai_props->mclk_fs);
|
||||
|
||||
ret = asoc_simple_card_parse_cpu(cpu, dai_link,
|
||||
DAI, CELL, &single_cpu);
|
||||
|
@ -318,22 +318,54 @@ static int asoc_simple_card_dai_link_of(struct device_node *node,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int asoc_simple_card_parse_aux_devs(struct device_node *node,
|
||||
struct simple_card_data *priv)
|
||||
{
|
||||
struct device *dev = simple_priv_to_dev(priv);
|
||||
struct device_node *aux_node;
|
||||
int i, n, len;
|
||||
|
||||
if (!of_find_property(node, PREFIX "aux-devs", &len))
|
||||
return 0; /* Ok to have no aux-devs */
|
||||
|
||||
n = len / sizeof(__be32);
|
||||
if (n <= 0)
|
||||
return -EINVAL;
|
||||
|
||||
priv->snd_card.aux_dev = devm_kzalloc(dev,
|
||||
n * sizeof(*priv->snd_card.aux_dev), GFP_KERNEL);
|
||||
if (!priv->snd_card.aux_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
aux_node = of_parse_phandle(node, PREFIX "aux-devs", i);
|
||||
if (!aux_node)
|
||||
return -EINVAL;
|
||||
priv->snd_card.aux_dev[i].codec_of_node = aux_node;
|
||||
}
|
||||
|
||||
priv->snd_card.num_aux_devs = n;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int asoc_simple_card_parse_of(struct device_node *node,
|
||||
struct simple_card_data *priv)
|
||||
{
|
||||
struct device *dev = simple_priv_to_dev(priv);
|
||||
u32 val;
|
||||
struct device_node *dai_link;
|
||||
int ret;
|
||||
|
||||
if (!node)
|
||||
return -EINVAL;
|
||||
|
||||
dai_link = of_get_child_by_name(node, PREFIX "dai-link");
|
||||
|
||||
/* The off-codec widgets */
|
||||
if (of_property_read_bool(node, PREFIX "widgets")) {
|
||||
ret = snd_soc_of_parse_audio_simple_widgets(&priv->snd_card,
|
||||
PREFIX "widgets");
|
||||
if (ret)
|
||||
return ret;
|
||||
goto card_parse_end;
|
||||
}
|
||||
|
||||
/* DAPM routes */
|
||||
|
@ -341,16 +373,14 @@ static int asoc_simple_card_parse_of(struct device_node *node,
|
|||
ret = snd_soc_of_parse_audio_routing(&priv->snd_card,
|
||||
PREFIX "routing");
|
||||
if (ret)
|
||||
return ret;
|
||||
goto card_parse_end;
|
||||
}
|
||||
|
||||
/* Factor to mclk, used in hw_params() */
|
||||
ret = of_property_read_u32(node, PREFIX "mclk-fs", &val);
|
||||
if (ret == 0)
|
||||
priv->mclk_fs = val;
|
||||
of_property_read_u32(node, PREFIX "mclk-fs", &priv->mclk_fs);
|
||||
|
||||
/* Single/Muti DAI link(s) & New style of DT node */
|
||||
if (of_get_child_by_name(node, PREFIX "dai-link")) {
|
||||
if (dai_link) {
|
||||
struct device_node *np = NULL;
|
||||
int i = 0;
|
||||
|
||||
|
@ -360,7 +390,7 @@ static int asoc_simple_card_parse_of(struct device_node *node,
|
|||
i, false);
|
||||
if (ret < 0) {
|
||||
of_node_put(np);
|
||||
return ret;
|
||||
goto card_parse_end;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
@ -368,51 +398,55 @@ static int asoc_simple_card_parse_of(struct device_node *node,
|
|||
/* For single DAI link & old style of DT node */
|
||||
ret = asoc_simple_card_dai_link_of(node, priv, 0, true);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto card_parse_end;
|
||||
}
|
||||
|
||||
ret = asoc_simple_card_parse_card_name(&priv->snd_card, PREFIX);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (ret < 0)
|
||||
goto card_parse_end;
|
||||
|
||||
return 0;
|
||||
ret = asoc_simple_card_parse_aux_devs(node, priv);
|
||||
|
||||
card_parse_end:
|
||||
of_node_put(dai_link);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int asoc_simple_card_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct simple_card_data *priv;
|
||||
struct snd_soc_dai_link *dai_link;
|
||||
struct simple_dai_props *dai_props;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct device *dev = &pdev->dev;
|
||||
int num_links, ret;
|
||||
int num, ret;
|
||||
|
||||
/* Get the number of DAI links */
|
||||
if (np && of_get_child_by_name(np, PREFIX "dai-link"))
|
||||
num_links = of_get_child_count(np);
|
||||
num = of_get_child_count(np);
|
||||
else
|
||||
num_links = 1;
|
||||
num = 1;
|
||||
|
||||
/* Allocate the private data and the DAI link array */
|
||||
priv = devm_kzalloc(dev,
|
||||
sizeof(*priv) + sizeof(*dai_link) * num_links,
|
||||
GFP_KERNEL);
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Init snd_soc_card */
|
||||
priv->snd_card.owner = THIS_MODULE;
|
||||
priv->snd_card.dev = dev;
|
||||
dai_link = priv->dai_link;
|
||||
priv->snd_card.dai_link = dai_link;
|
||||
priv->snd_card.num_links = num_links;
|
||||
|
||||
/* Get room for the other properties */
|
||||
priv->dai_props = devm_kzalloc(dev,
|
||||
sizeof(*priv->dai_props) * num_links,
|
||||
GFP_KERNEL);
|
||||
if (!priv->dai_props)
|
||||
dai_props = devm_kzalloc(dev, sizeof(*dai_props) * num, GFP_KERNEL);
|
||||
dai_link = devm_kzalloc(dev, sizeof(*dai_link) * num, GFP_KERNEL);
|
||||
if (!dai_props || !dai_link)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->dai_props = dai_props;
|
||||
priv->dai_link = dai_link;
|
||||
|
||||
/* Init snd_soc_card */
|
||||
priv->snd_card.owner = THIS_MODULE;
|
||||
priv->snd_card.dev = dev;
|
||||
priv->snd_card.dai_link = priv->dai_link;
|
||||
priv->snd_card.num_links = num;
|
||||
|
||||
if (np && of_device_is_available(np)) {
|
||||
|
||||
ret = asoc_simple_card_parse_of(np, priv);
|
||||
|
@ -453,7 +487,6 @@ static int asoc_simple_card_probe(struct platform_device *pdev)
|
|||
sizeof(priv->dai_props->cpu_dai));
|
||||
memcpy(&priv->dai_props->codec_dai, &cinfo->codec_dai,
|
||||
sizeof(priv->dai_props->codec_dai));
|
||||
|
||||
}
|
||||
|
||||
snd_soc_card_set_drvdata(&priv->snd_card, priv);
|
||||
|
@ -461,9 +494,9 @@ static int asoc_simple_card_probe(struct platform_device *pdev)
|
|||
ret = devm_snd_soc_register_card(&pdev->dev, &priv->snd_card);
|
||||
if (ret >= 0)
|
||||
return ret;
|
||||
|
||||
err:
|
||||
asoc_simple_card_clean_reference(&priv->snd_card);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -497,6 +530,6 @@ static struct platform_driver asoc_simple_card = {
|
|||
module_platform_driver(asoc_simple_card);
|
||||
|
||||
MODULE_ALIAS("platform:asoc-simple-card");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("ASoC Simple Sound Card");
|
||||
MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
|
||||
|
|
|
@ -19,6 +19,84 @@
|
|||
#define UNIPERIF_MAX_FRAME_SZ 0x20
|
||||
#define UNIPERIF_ALLOWED_FRAME_SZ (0x08 | 0x10 | 0x18 | UNIPERIF_MAX_FRAME_SZ)
|
||||
|
||||
struct sti_uniperiph_dev_data {
|
||||
unsigned int id; /* Nb available player instances */
|
||||
unsigned int version; /* player IP version */
|
||||
unsigned int stream;
|
||||
const char *dai_names;
|
||||
enum uniperif_type type;
|
||||
};
|
||||
|
||||
static const struct sti_uniperiph_dev_data sti_uniplayer_hdmi = {
|
||||
.id = 0,
|
||||
.version = SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0,
|
||||
.stream = SNDRV_PCM_STREAM_PLAYBACK,
|
||||
.dai_names = "Uni Player #0 (HDMI)",
|
||||
.type = SND_ST_UNIPERIF_TYPE_HDMI
|
||||
};
|
||||
|
||||
static const struct sti_uniperiph_dev_data sti_uniplayer_pcm_out = {
|
||||
.id = 1,
|
||||
.version = SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0,
|
||||
.stream = SNDRV_PCM_STREAM_PLAYBACK,
|
||||
.dai_names = "Uni Player #1 (PCM OUT)",
|
||||
.type = SND_ST_UNIPERIF_TYPE_PCM | SND_ST_UNIPERIF_TYPE_TDM,
|
||||
};
|
||||
|
||||
static const struct sti_uniperiph_dev_data sti_uniplayer_dac = {
|
||||
.id = 2,
|
||||
.version = SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0,
|
||||
.stream = SNDRV_PCM_STREAM_PLAYBACK,
|
||||
.dai_names = "Uni Player #2 (DAC)",
|
||||
.type = SND_ST_UNIPERIF_TYPE_PCM,
|
||||
};
|
||||
|
||||
static const struct sti_uniperiph_dev_data sti_uniplayer_spdif = {
|
||||
.id = 3,
|
||||
.version = SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0,
|
||||
.stream = SNDRV_PCM_STREAM_PLAYBACK,
|
||||
.dai_names = "Uni Player #3 (SPDIF)",
|
||||
.type = SND_ST_UNIPERIF_TYPE_SPDIF
|
||||
};
|
||||
|
||||
static const struct sti_uniperiph_dev_data sti_unireader_pcm_in = {
|
||||
.id = 0,
|
||||
.version = SND_ST_UNIPERIF_VERSION_UNI_RDR_1_0,
|
||||
.stream = SNDRV_PCM_STREAM_CAPTURE,
|
||||
.dai_names = "Uni Reader #0 (PCM IN)",
|
||||
.type = SND_ST_UNIPERIF_TYPE_PCM | SND_ST_UNIPERIF_TYPE_TDM,
|
||||
};
|
||||
|
||||
static const struct sti_uniperiph_dev_data sti_unireader_hdmi_in = {
|
||||
.id = 1,
|
||||
.version = SND_ST_UNIPERIF_VERSION_UNI_RDR_1_0,
|
||||
.stream = SNDRV_PCM_STREAM_CAPTURE,
|
||||
.dai_names = "Uni Reader #1 (HDMI IN)",
|
||||
.type = SND_ST_UNIPERIF_TYPE_PCM,
|
||||
};
|
||||
|
||||
static const struct of_device_id snd_soc_sti_match[] = {
|
||||
{ .compatible = "st,stih407-uni-player-hdmi",
|
||||
.data = &sti_uniplayer_hdmi
|
||||
},
|
||||
{ .compatible = "st,stih407-uni-player-pcm-out",
|
||||
.data = &sti_uniplayer_pcm_out
|
||||
},
|
||||
{ .compatible = "st,stih407-uni-player-dac",
|
||||
.data = &sti_uniplayer_dac
|
||||
},
|
||||
{ .compatible = "st,stih407-uni-player-spdif",
|
||||
.data = &sti_uniplayer_spdif
|
||||
},
|
||||
{ .compatible = "st,stih407-uni-reader-pcm_in",
|
||||
.data = &sti_unireader_pcm_in
|
||||
},
|
||||
{ .compatible = "st,stih407-uni-reader-hdmi",
|
||||
.data = &sti_unireader_hdmi_in
|
||||
},
|
||||
{},
|
||||
};
|
||||
|
||||
int sti_uniperiph_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
|
||||
unsigned int rx_mask, int slots,
|
||||
int slot_width)
|
||||
|
@ -167,8 +245,8 @@ static int sti_uniperiph_dai_create_ctrl(struct snd_soc_dai *dai)
|
|||
* Uniperipheral instance ID
|
||||
*/
|
||||
ctrl = &uni->snd_ctrls[i];
|
||||
ctrl->index = uni->info->id;
|
||||
ctrl->device = uni->info->id;
|
||||
ctrl->index = uni->id;
|
||||
ctrl->device = uni->id;
|
||||
}
|
||||
|
||||
return snd_soc_add_dai_controls(dai, uni->snd_ctrls, uni->num_ctrls);
|
||||
|
@ -186,7 +264,7 @@ int sti_uniperiph_dai_hw_params(struct snd_pcm_substream *substream,
|
|||
struct snd_dmaengine_dai_dma_data *dma_data;
|
||||
int transfer_size;
|
||||
|
||||
if (uni->info->type == SND_ST_UNIPERIF_TYPE_TDM)
|
||||
if (uni->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
|
||||
|
@ -235,7 +313,7 @@ static int sti_uniperiph_dai_resume(struct snd_soc_dai *dai)
|
|||
struct uniperif *uni = priv->dai_data.uni;
|
||||
int ret;
|
||||
|
||||
if (of_device_is_compatible(dai->dev->of_node, "st,sti-uni-player")) {
|
||||
if (priv->dai_data.stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
ret = uni_player_resume(uni);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
@ -256,7 +334,7 @@ static int sti_uniperiph_dai_probe(struct snd_soc_dai *dai)
|
|||
struct sti_uniperiph_dai *dai_data = &priv->dai_data;
|
||||
|
||||
/* DMA settings*/
|
||||
if (of_device_is_compatible(dai->dev->of_node, "st,sti-uni-player"))
|
||||
if (priv->dai_data.stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
snd_soc_dai_init_dma_data(dai, &dai_data->dma_data, NULL);
|
||||
else
|
||||
snd_soc_dai_init_dma_data(dai, NULL, &dai_data->dma_data);
|
||||
|
@ -280,25 +358,32 @@ static const struct snd_soc_component_driver sti_uniperiph_dai_component = {
|
|||
static int sti_uniperiph_cpu_dai_of(struct device_node *node,
|
||||
struct sti_uniperiph_data *priv)
|
||||
{
|
||||
const char *str;
|
||||
int ret;
|
||||
struct device *dev = &priv->pdev->dev;
|
||||
struct sti_uniperiph_dai *dai_data = &priv->dai_data;
|
||||
struct snd_soc_dai_driver *dai = priv->dai;
|
||||
struct snd_soc_pcm_stream *stream;
|
||||
struct uniperif *uni;
|
||||
const struct of_device_id *of_id;
|
||||
const struct sti_uniperiph_dev_data *dev_data;
|
||||
const char *mode;
|
||||
|
||||
/* Populate data structure depending on compatibility */
|
||||
of_id = of_match_node(snd_soc_sti_match, node);
|
||||
if (!of_id->data) {
|
||||
dev_err(dev, "data associated to device is missing");
|
||||
return -EINVAL;
|
||||
}
|
||||
dev_data = (struct sti_uniperiph_dev_data *)of_id->data;
|
||||
|
||||
uni = devm_kzalloc(dev, sizeof(*uni), GFP_KERNEL);
|
||||
if (!uni)
|
||||
return -ENOMEM;
|
||||
|
||||
uni->id = dev_data->id;
|
||||
uni->ver = dev_data->version;
|
||||
|
||||
*dai = sti_uniperiph_dai_template;
|
||||
ret = of_property_read_string(node, "dai-name", &str);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "%s: dai name missing.\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
dai->name = str;
|
||||
dai->name = dev_data->dai_names;
|
||||
|
||||
/* Get resources */
|
||||
uni->mem_region = platform_get_resource(priv->pdev, IORESOURCE_MEM, 0);
|
||||
|
@ -322,9 +407,20 @@ static int sti_uniperiph_cpu_dai_of(struct device_node *node,
|
|||
return -ENXIO;
|
||||
}
|
||||
|
||||
dai_data->uni = uni;
|
||||
uni->type = dev_data->type;
|
||||
|
||||
if (of_device_is_compatible(node, "st,sti-uni-player")) {
|
||||
/* check if player should be configured for tdm */
|
||||
if (dev_data->type & SND_ST_UNIPERIF_TYPE_TDM) {
|
||||
if (!of_property_read_string(node, "st,tdm-mode", &mode))
|
||||
uni->type = SND_ST_UNIPERIF_TYPE_TDM;
|
||||
else
|
||||
uni->type = SND_ST_UNIPERIF_TYPE_PCM;
|
||||
}
|
||||
|
||||
dai_data->uni = uni;
|
||||
dai_data->stream = dev_data->stream;
|
||||
|
||||
if (priv->dai_data.stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
uni_player_init(priv->pdev, uni);
|
||||
stream = &dai->playback;
|
||||
} else {
|
||||
|
@ -376,12 +472,6 @@ static int sti_uniperiph_probe(struct platform_device *pdev)
|
|||
&dmaengine_pcm_config, 0);
|
||||
}
|
||||
|
||||
static const struct of_device_id snd_soc_sti_match[] = {
|
||||
{ .compatible = "st,sti-uni-player", },
|
||||
{ .compatible = "st,sti-uni-reader", },
|
||||
{},
|
||||
};
|
||||
|
||||
static struct platform_driver sti_uniperiph_driver = {
|
||||
.driver = {
|
||||
.name = "sti-uniperiph-dai",
|
||||
|
|
|
@ -1220,16 +1220,16 @@
|
|||
#define UNIPERIF_FIFO_FRAMES 4 /* FDMA trigger limit in frames */
|
||||
|
||||
#define UNIPERIF_TYPE_IS_HDMI(p) \
|
||||
((p)->info->type == SND_ST_UNIPERIF_TYPE_HDMI)
|
||||
((p)->type == SND_ST_UNIPERIF_TYPE_HDMI)
|
||||
#define UNIPERIF_TYPE_IS_PCM(p) \
|
||||
((p)->info->type == SND_ST_UNIPERIF_TYPE_PCM)
|
||||
((p)->type == SND_ST_UNIPERIF_TYPE_PCM)
|
||||
#define UNIPERIF_TYPE_IS_SPDIF(p) \
|
||||
((p)->info->type == SND_ST_UNIPERIF_TYPE_SPDIF)
|
||||
((p)->type == SND_ST_UNIPERIF_TYPE_SPDIF)
|
||||
#define UNIPERIF_TYPE_IS_IEC958(p) \
|
||||
(UNIPERIF_TYPE_IS_HDMI(p) || \
|
||||
UNIPERIF_TYPE_IS_SPDIF(p))
|
||||
#define UNIPERIF_TYPE_IS_TDM(p) \
|
||||
((p)->info->type == SND_ST_UNIPERIF_TYPE_TDM)
|
||||
((p)->type == SND_ST_UNIPERIF_TYPE_TDM)
|
||||
|
||||
/*
|
||||
* Uniperipheral IP revisions
|
||||
|
@ -1249,11 +1249,11 @@ enum uniperif_version {
|
|||
};
|
||||
|
||||
enum uniperif_type {
|
||||
SND_ST_UNIPERIF_TYPE_NONE,
|
||||
SND_ST_UNIPERIF_TYPE_HDMI,
|
||||
SND_ST_UNIPERIF_TYPE_PCM,
|
||||
SND_ST_UNIPERIF_TYPE_SPDIF,
|
||||
SND_ST_UNIPERIF_TYPE_TDM
|
||||
SND_ST_UNIPERIF_TYPE_NONE = 0x00,
|
||||
SND_ST_UNIPERIF_TYPE_HDMI = 0x01,
|
||||
SND_ST_UNIPERIF_TYPE_PCM = 0x02,
|
||||
SND_ST_UNIPERIF_TYPE_SPDIF = 0x04,
|
||||
SND_ST_UNIPERIF_TYPE_TDM = 0x08
|
||||
};
|
||||
|
||||
enum uniperif_state {
|
||||
|
@ -1278,12 +1278,6 @@ enum uniperif_word_pos {
|
|||
WORD_MAX
|
||||
};
|
||||
|
||||
struct uniperif_info {
|
||||
int id; /* instance value of the uniperipheral IP */
|
||||
enum uniperif_type type;
|
||||
int underflow_enabled; /* Underflow recovery mode */
|
||||
};
|
||||
|
||||
struct uniperif_iec958_settings {
|
||||
enum uniperif_iec958_encoding_mode encoding_mode;
|
||||
struct snd_aes_iec958 iec958;
|
||||
|
@ -1298,8 +1292,10 @@ struct dai_tdm_slot {
|
|||
|
||||
struct uniperif {
|
||||
/* System information */
|
||||
struct uniperif_info *info;
|
||||
enum uniperif_type type;
|
||||
int underflow_enabled; /* Underflow recovery mode */
|
||||
struct device *dev;
|
||||
int id; /* instance value of the uniperipheral IP */
|
||||
int ver; /* IP version, used by register access macros */
|
||||
struct regmap_field *clk_sel;
|
||||
struct regmap_field *valid_sel;
|
||||
|
|
|
@ -100,7 +100,7 @@ static irqreturn_t uni_player_irq_handler(int irq, void *dev_id)
|
|||
dev_err(player->dev, "FIFO underflow error detected");
|
||||
|
||||
/* Interrupt is just for information when underflow recovery */
|
||||
if (player->info->underflow_enabled) {
|
||||
if (player->underflow_enabled) {
|
||||
/* Update state to underflow */
|
||||
player->state = UNIPERIF_STATE_UNDERFLOW;
|
||||
|
||||
|
@ -134,7 +134,7 @@ static irqreturn_t uni_player_irq_handler(int irq, void *dev_id)
|
|||
|
||||
/* Check for underflow recovery done */
|
||||
if (unlikely(status & UNIPERIF_ITM_UNDERFLOW_REC_DONE_MASK(player))) {
|
||||
if (!player->info->underflow_enabled) {
|
||||
if (!player->underflow_enabled) {
|
||||
dev_err(player->dev, "unexpected Underflow recovering");
|
||||
return -EPERM;
|
||||
}
|
||||
|
@ -764,7 +764,7 @@ static int uni_player_prepare(struct snd_pcm_substream *substream,
|
|||
}
|
||||
|
||||
/* Calculate transfer size (in fifo cells and bytes) for frame count */
|
||||
if (player->info->type == SND_ST_UNIPERIF_TYPE_TDM) {
|
||||
if (player->type == SND_ST_UNIPERIF_TYPE_TDM) {
|
||||
/* transfer size = user frame size (in 32 bits FIFO cell) */
|
||||
transfer_size =
|
||||
sti_uniperiph_get_user_frame_size(runtime) / 4;
|
||||
|
@ -794,7 +794,7 @@ static int uni_player_prepare(struct snd_pcm_substream *substream,
|
|||
SET_UNIPERIF_CONFIG_DMA_TRIG_LIMIT(player, trigger_limit);
|
||||
|
||||
/* Uniperipheral setup depends on player type */
|
||||
switch (player->info->type) {
|
||||
switch (player->type) {
|
||||
case SND_ST_UNIPERIF_TYPE_HDMI:
|
||||
ret = uni_player_prepare_iec958(player, runtime);
|
||||
break;
|
||||
|
@ -884,7 +884,7 @@ static int uni_player_start(struct uniperif *player)
|
|||
SET_UNIPERIF_ITM_BSET_FIFO_ERROR(player);
|
||||
|
||||
/* Enable underflow recovery interrupts */
|
||||
if (player->info->underflow_enabled) {
|
||||
if (player->underflow_enabled) {
|
||||
SET_UNIPERIF_ITM_BSET_UNDERFLOW_REC_DONE(player);
|
||||
SET_UNIPERIF_ITM_BSET_UNDERFLOW_REC_FAILED(player);
|
||||
}
|
||||
|
@ -893,8 +893,10 @@ static int uni_player_start(struct uniperif *player)
|
|||
SET_UNIPERIF_SOFT_RST_SOFT_RST(player);
|
||||
|
||||
ret = reset_player(player);
|
||||
if (ret < 0)
|
||||
if (ret < 0) {
|
||||
clk_disable_unprepare(player->clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Does not use IEC61937 features of the uniperipheral hardware.
|
||||
|
@ -1021,8 +1023,8 @@ static int uni_player_parse_dt_audio_glue(struct platform_device *pdev,
|
|||
struct reg_field regfield[2] = {
|
||||
/* PCM_CLK_SEL */
|
||||
REG_FIELD(SYS_CFG_AUDIO_GLUE,
|
||||
8 + player->info->id,
|
||||
8 + player->info->id),
|
||||
8 + player->id,
|
||||
8 + player->id),
|
||||
/* PCMP_VALID_SEL */
|
||||
REG_FIELD(SYS_CFG_AUDIO_GLUE, 0, 1)
|
||||
};
|
||||
|
@ -1040,60 +1042,6 @@ static int uni_player_parse_dt_audio_glue(struct platform_device *pdev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int uni_player_parse_dt(struct platform_device *pdev,
|
||||
struct uniperif *player)
|
||||
{
|
||||
struct uniperif_info *info;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *pnode = pdev->dev.of_node;
|
||||
const char *mode;
|
||||
|
||||
/* Allocate memory for the info structure */
|
||||
info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
|
||||
if (!info)
|
||||
return -ENOMEM;
|
||||
|
||||
if (of_property_read_u32(pnode, "st,version", &player->ver) ||
|
||||
player->ver == SND_ST_UNIPERIF_VERSION_UNKNOWN) {
|
||||
dev_err(dev, "Unknown uniperipheral version ");
|
||||
return -EINVAL;
|
||||
}
|
||||
/* Underflow recovery is only supported on later ip revisions */
|
||||
if (player->ver >= SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0)
|
||||
info->underflow_enabled = 1;
|
||||
|
||||
if (of_property_read_u32(pnode, "st,uniperiph-id", &info->id)) {
|
||||
dev_err(dev, "uniperipheral id not defined");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Read the device mode property */
|
||||
if (of_property_read_string(pnode, "st,mode", &mode)) {
|
||||
dev_err(dev, "uniperipheral mode not defined");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (strcasecmp(mode, "hdmi") == 0)
|
||||
info->type = SND_ST_UNIPERIF_TYPE_HDMI;
|
||||
else if (strcasecmp(mode, "pcm") == 0)
|
||||
info->type = SND_ST_UNIPERIF_TYPE_PCM;
|
||||
else if (strcasecmp(mode, "spdif") == 0)
|
||||
info->type = SND_ST_UNIPERIF_TYPE_SPDIF;
|
||||
else if (strcasecmp(mode, "tdm") == 0)
|
||||
info->type = SND_ST_UNIPERIF_TYPE_TDM;
|
||||
else
|
||||
info->type = SND_ST_UNIPERIF_TYPE_NONE;
|
||||
|
||||
/* Save the info structure */
|
||||
player->info = info;
|
||||
|
||||
/* Get PCM_CLK_SEL & PCMP_VALID_SEL from audio-glue-ctrl SoC reg */
|
||||
if (uni_player_parse_dt_audio_glue(pdev, player))
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_dai_ops uni_player_dai_ops = {
|
||||
.startup = uni_player_startup,
|
||||
.shutdown = uni_player_shutdown,
|
||||
|
@ -1114,13 +1062,18 @@ int uni_player_init(struct platform_device *pdev,
|
|||
player->state = UNIPERIF_STATE_STOPPED;
|
||||
player->dai_ops = &uni_player_dai_ops;
|
||||
|
||||
ret = uni_player_parse_dt(pdev, player);
|
||||
/* Get PCM_CLK_SEL & PCMP_VALID_SEL from audio-glue-ctrl SoC reg */
|
||||
ret = uni_player_parse_dt_audio_glue(pdev, player);
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(player->dev, "Failed to parse DeviceTree");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Underflow recovery is only supported on later ip revisions */
|
||||
if (player->ver >= SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0)
|
||||
player->underflow_enabled = 1;
|
||||
|
||||
if (UNIPERIF_TYPE_IS_TDM(player))
|
||||
player->hw = &uni_tdm_hw;
|
||||
else
|
||||
|
@ -1144,8 +1097,8 @@ int uni_player_init(struct platform_device *pdev,
|
|||
|
||||
/* connect to I2S/TDM TX bus */
|
||||
if (player->valid_sel &&
|
||||
(player->info->id == UNIPERIF_PLAYER_I2S_OUT)) {
|
||||
ret = regmap_field_write(player->valid_sel, player->info->id);
|
||||
(player->id == UNIPERIF_PLAYER_I2S_OUT)) {
|
||||
ret = regmap_field_write(player->valid_sel, player->id);
|
||||
if (ret) {
|
||||
dev_err(player->dev,
|
||||
"%s: unable to connect to tdm bus", __func__);
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
#include "uniperif.h"
|
||||
|
||||
#define UNIPERIF_READER_I2S_IN 0 /* reader id connected to I2S/TDM TX bus */
|
||||
/*
|
||||
* Note: snd_pcm_hardware is linked to DMA controller but is declared here to
|
||||
* integrate unireader capability in term of rate and supported channels
|
||||
|
@ -195,7 +196,7 @@ static int uni_reader_prepare(struct snd_pcm_substream *substream,
|
|||
}
|
||||
|
||||
/* Calculate transfer size (in fifo cells and bytes) for frame count */
|
||||
if (reader->info->type == SND_ST_UNIPERIF_TYPE_TDM) {
|
||||
if (reader->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;
|
||||
|
@ -280,7 +281,7 @@ static int uni_reader_prepare(struct snd_pcm_substream *substream,
|
|||
SET_UNIPERIF_ITM_BSET_MEM_BLK_READ(reader);
|
||||
|
||||
/* Enable underflow recovery interrupts */
|
||||
if (reader->info->underflow_enabled) {
|
||||
if (reader->underflow_enabled) {
|
||||
SET_UNIPERIF_ITM_BSET_UNDERFLOW_REC_DONE(reader);
|
||||
SET_UNIPERIF_ITM_BSET_UNDERFLOW_REC_FAILED(reader);
|
||||
}
|
||||
|
@ -394,41 +395,6 @@ static void uni_reader_shutdown(struct snd_pcm_substream *substream,
|
|||
}
|
||||
}
|
||||
|
||||
static int uni_reader_parse_dt(struct platform_device *pdev,
|
||||
struct uniperif *reader)
|
||||
{
|
||||
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);
|
||||
if (!info)
|
||||
return -ENOMEM;
|
||||
|
||||
if (of_property_read_u32(node, "st,version", &reader->ver) ||
|
||||
reader->ver == SND_ST_UNIPERIF_VERSION_UNKNOWN) {
|
||||
dev_err(&pdev->dev, "Unknown uniperipheral version ");
|
||||
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;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_dai_ops uni_reader_dai_ops = {
|
||||
.startup = uni_reader_startup,
|
||||
.shutdown = uni_reader_shutdown,
|
||||
|
@ -448,12 +414,6 @@ int uni_reader_init(struct platform_device *pdev,
|
|||
reader->state = UNIPERIF_STATE_STOPPED;
|
||||
reader->dai_ops = &uni_reader_dai_ops;
|
||||
|
||||
ret = uni_reader_parse_dt(pdev, reader);
|
||||
if (ret < 0) {
|
||||
dev_err(reader->dev, "Failed to parse DeviceTree");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (UNIPERIF_TYPE_IS_TDM(reader))
|
||||
reader->hw = &uni_tdm_hw;
|
||||
else
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
menu "Allwinner SoC Audio support"
|
||||
depends on ARCH_SUNXI || COMPILE_TEST
|
||||
|
||||
config SND_SUN4I_CODEC
|
||||
tristate "Allwinner A10 Codec Support"
|
||||
|
|
|
@ -96,8 +96,8 @@
|
|||
/* Other various ADC registers */
|
||||
#define SUN4I_CODEC_DAC_TXCNT (0x30)
|
||||
#define SUN4I_CODEC_ADC_RXCNT (0x34)
|
||||
#define SUN4I_CODEC_AC_SYS_VERI (0x38)
|
||||
#define SUN4I_CODEC_AC_MIC_PHONE_CAL (0x3c)
|
||||
#define SUN7I_CODEC_AC_DAC_CAL (0x38)
|
||||
#define SUN7I_CODEC_AC_MIC_PHONE_CAL (0x3c)
|
||||
|
||||
struct sun4i_codec {
|
||||
struct device *dev;
|
||||
|
@ -509,7 +509,7 @@ static const struct snd_kcontrol_new sun4i_codec_pa_mute =
|
|||
|
||||
static DECLARE_TLV_DB_SCALE(sun4i_codec_pa_volume_scale, -6300, 100, 1);
|
||||
|
||||
static const struct snd_kcontrol_new sun4i_codec_widgets[] = {
|
||||
static const struct snd_kcontrol_new sun4i_codec_controls[] = {
|
||||
SOC_SINGLE_TLV("Power Amplifier Volume", SUN4I_CODEC_DAC_ACTL,
|
||||
SUN4I_CODEC_DAC_ACTL_PA_VOL, 0x3F, 0,
|
||||
sun4i_codec_pa_volume_scale),
|
||||
|
@ -629,8 +629,8 @@ static const struct snd_soc_dapm_route sun4i_codec_codec_dapm_routes[] = {
|
|||
|
||||
static struct snd_soc_codec_driver sun4i_codec_codec = {
|
||||
.component_driver = {
|
||||
.controls = sun4i_codec_widgets,
|
||||
.num_controls = ARRAY_SIZE(sun4i_codec_widgets),
|
||||
.controls = sun4i_codec_controls,
|
||||
.num_controls = ARRAY_SIZE(sun4i_codec_controls),
|
||||
.dapm_widgets = sun4i_codec_codec_dapm_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(sun4i_codec_codec_dapm_widgets),
|
||||
.dapm_routes = sun4i_codec_codec_dapm_routes,
|
||||
|
@ -682,12 +682,37 @@ static const struct regmap_config sun4i_codec_regmap_config = {
|
|||
.reg_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.val_bits = 32,
|
||||
.max_register = SUN4I_CODEC_AC_MIC_PHONE_CAL,
|
||||
.max_register = SUN4I_CODEC_ADC_RXCNT,
|
||||
};
|
||||
|
||||
static const struct regmap_config sun7i_codec_regmap_config = {
|
||||
.reg_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.val_bits = 32,
|
||||
.max_register = SUN7I_CODEC_AC_MIC_PHONE_CAL,
|
||||
};
|
||||
|
||||
struct sun4i_codec_quirks {
|
||||
const struct regmap_config *regmap_config;
|
||||
};
|
||||
|
||||
static const struct sun4i_codec_quirks sun4i_codec_quirks = {
|
||||
.regmap_config = &sun4i_codec_regmap_config,
|
||||
};
|
||||
|
||||
static const struct sun4i_codec_quirks sun7i_codec_quirks = {
|
||||
.regmap_config = &sun7i_codec_regmap_config,
|
||||
};
|
||||
|
||||
static const struct of_device_id sun4i_codec_of_match[] = {
|
||||
{ .compatible = "allwinner,sun4i-a10-codec" },
|
||||
{ .compatible = "allwinner,sun7i-a20-codec" },
|
||||
{
|
||||
.compatible = "allwinner,sun4i-a10-codec",
|
||||
.data = &sun4i_codec_quirks,
|
||||
},
|
||||
{
|
||||
.compatible = "allwinner,sun7i-a20-codec",
|
||||
.data = &sun7i_codec_quirks,
|
||||
},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sun4i_codec_of_match);
|
||||
|
@ -760,6 +785,7 @@ static int sun4i_codec_probe(struct platform_device *pdev)
|
|||
{
|
||||
struct snd_soc_card *card;
|
||||
struct sun4i_codec *scodec;
|
||||
const struct sun4i_codec_quirks *quirks;
|
||||
struct resource *res;
|
||||
void __iomem *base;
|
||||
int ret;
|
||||
|
@ -777,8 +803,14 @@ static int sun4i_codec_probe(struct platform_device *pdev)
|
|||
return PTR_ERR(base);
|
||||
}
|
||||
|
||||
quirks = of_device_get_match_data(&pdev->dev);
|
||||
if (quirks == NULL) {
|
||||
dev_err(&pdev->dev, "Failed to determine the quirks to use\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
scodec->regmap = devm_regmap_init_mmio(&pdev->dev, base,
|
||||
&sun4i_codec_regmap_config);
|
||||
quirks->regmap_config);
|
||||
if (IS_ERR(scodec->regmap)) {
|
||||
dev_err(&pdev->dev, "Failed to create our regmap\n");
|
||||
return PTR_ERR(scodec->regmap);
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/reset.h>
|
||||
#include <sound/dmaengine_pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
|
@ -162,6 +163,7 @@ struct sun4i_spdif_dev {
|
|||
struct platform_device *pdev;
|
||||
struct clk *spdif_clk;
|
||||
struct clk *apb_clk;
|
||||
struct reset_control *rst;
|
||||
struct snd_soc_dai_driver cpu_dai_drv;
|
||||
struct regmap *regmap;
|
||||
struct snd_dmaengine_dai_dma_data dma_params_tx;
|
||||
|
@ -411,6 +413,7 @@ static const struct snd_soc_dapm_route dit_routes[] = {
|
|||
|
||||
static const struct of_device_id sun4i_spdif_of_match[] = {
|
||||
{ .compatible = "allwinner,sun4i-a10-spdif", },
|
||||
{ .compatible = "allwinner,sun6i-a31-spdif", },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sun4i_spdif_of_match);
|
||||
|
@ -482,11 +485,23 @@ static int sun4i_spdif_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
host->dma_params_tx.addr = res->start + SUN4I_SPDIF_TXFIFO;
|
||||
host->dma_params_tx.maxburst = 4;
|
||||
host->dma_params_tx.maxburst = 8;
|
||||
host->dma_params_tx.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
|
||||
|
||||
platform_set_drvdata(pdev, host);
|
||||
|
||||
if (of_device_is_compatible(pdev->dev.of_node,
|
||||
"allwinner,sun6i-a31-spdif")) {
|
||||
host->rst = devm_reset_control_get_optional(&pdev->dev, NULL);
|
||||
if (IS_ERR(host->rst) && PTR_ERR(host->rst) == -EPROBE_DEFER) {
|
||||
ret = -EPROBE_DEFER;
|
||||
dev_err(&pdev->dev, "Failed to get reset: %d\n", ret);
|
||||
goto err_disable_apb_clk;
|
||||
}
|
||||
if (!IS_ERR(host->rst))
|
||||
reset_control_deassert(host->rst);
|
||||
}
|
||||
|
||||
ret = devm_snd_soc_register_component(&pdev->dev,
|
||||
&sun4i_spdif_component, &sun4i_spdif_dai, 1);
|
||||
if (ret)
|
||||
|
|
Loading…
Reference in a new issue