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:
Mark Brown 2016-09-29 12:44:41 -07:00
14 changed files with 343 additions and 239 deletions

View file

@ -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 = <&amp>;
simple-audio-card,cpu {
sound-dai = <&ssi2>;
};
simple-audio-card,codec {
sound-dai = <&codec>;
clocks = ...
};
};

View file

@ -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

View file

@ -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 {

View file

@ -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, ...);

View file

@ -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 */

View file

@ -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),

View file

@ -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>");

View file

@ -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",

View file

@ -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;

View file

@ -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__);

View file

@ -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

View file

@ -1,4 +1,5 @@
menu "Allwinner SoC Audio support"
depends on ARCH_SUNXI || COMPILE_TEST
config SND_SUN4I_CODEC
tristate "Allwinner A10 Codec Support"

View file

@ -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);

View file

@ -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)