F: sound/soc/amlogic/meson/*
F: sound/soc/amlogic/auge/*
F: sound/soc/codecs/amlogic/*
+F: sound/soc/codecs/amlogic/tas575x.c
F: include/dt-bindings/clock/amlogic,axg-audio-clk.h
AMLOGIC Security Support
F: drivers/amlogic/irblaster/irblaster.h
F: drivers/amlogic/irblaster/Kconfig
F: drivers/amlogic/irblaster/Makefile
+
+AMLOGIC AXG ADD CLKMSR INTERFACE
+M: wang xing <xing.wang@amlogic.com>
+F: include/linux/amlogic/clk_measure.h
\ No newline at end of file
mclk-fs = <256>;
continuous-clock;
//bitclock-inversion;
- //frame-inversion;
+ frame-inversion;
bitclock-master = <&aml_tdmb>;
frame-master = <&aml_tdmb>;
cpu {
system-clock-frequency = <12288000>;
};
codec {
+ prefix-names = "3101_A", "3101_B",
+ "3101_C", "3101_D";
sound-dai = <&tlv320adc3101_32 &tlv320adc3101_30
&tlv320adc3101_34 &tlv320adc3101_36>;
};
format = "i2s";
mclk-fs = <256>;
continuous-clock;
- bitclock-inversion;
+ //bitclock-inversion;
frame-inversion;
//bitclock-master = <&aml_tdmc>;
//frame-master = <&aml_tdmc>;
system-clock-frequency = <12288000>;
};
codec {
+ prefix-names = "5707_A", "5707_B";
sound-dai = <&tas5707_36 &tas5707_3a
&dummy_codec>;
};
aml_tdma: tdma {
compatible = "amlogic, snd-tdma";
#sound-dai-cells = <0>;
- dai-tdm-lane-slot-mask = <1>;
+ dai-tdm-lane-slot-mask-in = <1>;
+ dai-tdm-lane-slot-mask-out = <1>;
dai-tdm-clk-sel = <0>;
tdm_from_ddr = <0>;
tdm_to_ddr = <0>;
aml_tdmb: tdmb {
compatible = "amlogic, snd-tdmb";
#sound-dai-cells = <0>;
- dai-tdm-lane-slot-mask = <1 1 1 1>;
+ dai-tdm-lane-slot-mask-in = <1 1 1 1>;
dai-tdm-clk-sel = <1>;
tdm_from_ddr = <1>;
tdm_to_ddr = <1>;
//aml-audio-card,mclk-fs = <256>;
aml-audio-card,dai-link@0 {
- format = "i2s";//"dsp_a";
+ format = "dsp_a";
mclk-fs = <256>;//512
continuous-clock;
bitclock-inversion;
format = "i2s";
mclk-fs = <256>;
continuous-clock;
- bitclock-inversion;
- //frame-inversion;
+ //bitclock-inversion;
+ frame-inversion;
bitclock-master = <&aml_tdmb>;
frame-master = <&aml_tdmb>;
cpu {
system-clock-frequency = <12288000>;
};
codec {
- //sound-dai = <&dummy_codec &dummy_codec>;
- sound-dai = <&tas5707_36>;
+ sound-dai = <&tas5707_36 &tlv320adc3101_32>;
};
};
format = "i2s";
mclk-fs = <256>;
continuous-clock;
- bitclock-inversion;
- //frame-inversion;
+ /* tdmb clk using tdmc so no bclk-inv */
+ //bitclock-inversion;
+ frame-inversion;
bitclock-master = <&aml_tdmc>;
frame-master = <&aml_tdmc>;
cpu {
system-clock-frequency = <12288000>;
};
codec {
- //sound-dai = <&tas5707_36 &tas5707_3a>;
- sound-dai = <&dummy_codec &dummy_codec>;
+ sound-dai = <&dummy_codec>;
};
};
compatible = "ti,tlv320adc3101";
#sound-dai-cells = <0>;
reg = <0x19>;
- status = "disabled";
+ status = "okay";
};
tas5707_36: tas5707_36@36 {
compatible = "ti,tlv320adc3101";
#sound-dai-cells = <0>;
reg = <0x18>;
- status = "okay";
+ status = "disable";
};
tlv320adc3101_34: tlv320adc3101_34@30 {
compatible = "ti,tlv320adc3101";
#sound-dai-cells = <0>;
reg = <0x1a>;
- status = "okay";
+ status = "disable";
};
tlv320adc3101_36: tlv320adc3101_36@30 {
compatible = "ti,tlv320adc3101";
#sound-dai-cells = <0>;
reg = <0x1b>;
- status = "okay";
+ status = "disable";
};
};
aml_tdmb: tdmb {
compatible = "amlogic, snd-tdmb";
#sound-dai-cells = <0>;
- dai-tdm-lane-slot-mask = <1 1 1 1>;
+ dai-tdm-lane-slot-mask-out = <1 0>;
+ dai-tdm-lane-slot-mask-in = <0 1>;
dai-tdm-clk-sel = <2>;
tdm_from_ddr = <1>;
tdm_to_ddr = <1>;
GIC_SPI 89 IRQ_TYPE_EDGE_RISING>;
interrupt-names = "tdmin", "tdmout";
pinctrl-names = "tdm_pins";
- pinctrl-0 = <&tdmb_mclk &tdmout_b>;
+ pinctrl-0 = <&tdmb_mclk &tdmout_b &tdmin_b>;
};
aml_tdmc: tdmc {
compatible = "amlogic, snd-tdmc";
#sound-dai-cells = <0>;
- dai-tdm-lane-slot-mask = <1 1 1 1>;
+ dai-tdm-lane-slot-mask-out = <1 0>;
+ dai-tdm-lane-slot-mask-in = <0 1>;
dai-tdm-clk-sel = <2>;
tdm_from_ddr = <2>;
tdm_to_ddr = <2>;
GIC_SPI 90 IRQ_TYPE_EDGE_RISING>;
interrupt-names = "tdmin", "tdmout";
pinctrl-names = "tdm_pins";
- pinctrl-0 = <&tdmc_mclk &tdmout_c>;
+ pinctrl-0 = <&tdmc_mclk &tdmout_c &tdmin_c>;
};
aml_spdif: spdif {
tdmout_b: tdmout_b {
mux {
- pins = "GPIOA_8", "GPIOA_9", "GPIOA_10",
- "GPIOA_11";
+ pins = "GPIOA_8", "GPIOA_9", "GPIOA_10";
function = "tdmb_out";
};
};
* };
*};
*/
+ tdmin_b: tdmin_b {
+ mux {
+ pins = "GPIOA_11";
+ function = "tdmb_in";
+ };
+ };
tdmc_mclk: tdmc_mclk {
mux {
tdmout_c:tdmout_c {
mux {
- pins = "GPIOA_2", "GPIOA_3", "GPIOA_4",
- "GPIOA_5";
+ pins = "GPIOA_2", "GPIOA_3", "GPIOA_4";
function = "tdmc_out";
};
};
- /*
- *tdmin_c:tdmin_c {
- * mux {
- * pins = "GPIOA_4", "GPIOA_5", "GPIOA_6", "GPIOA_7";
- * function = "tdmc_in";
- * };
- *};
- */
+ tdmin_c:tdmin_c {
+ mux {
+ pins = "GPIOA_5";
+ function = "tdmc_in";
+ };
+ };
spdifout: spidfout {
mux {
CONFIG_AMLOGIC_SND_SOC_PCM186X=y
CONFIG_AMLOGIC_SND_SOC_SSM3515=y
CONFIG_AMLOGIC_SND_SOC_SSM3525=y
+CONFIG_AMLOGIC_SND_SOC_TAS575X=y
CONFIG_AMLOGIC_SND_SOC=y
CONFIG_AMLOGIC_SND_SOC_MESON=y
CONFIG_AMLOGIC_SND_SOC_AUGE=y
#include <linux/platform_device.h>
#include <linux/amlogic/cpu_version.h>
#include <linux/amlogic/iomap.h>
+#include <linux/amlogic/clk_measure.h>
#undef pr_fmt
#define pr_fmt(fmt) "clkmsr: " fmt
--- /dev/null
+/*
+ * include/linux/amlogic/clk_measure.h
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#ifndef __CLK_MEASURE_H__
+#define __CLK_MEASURE_H__
+
+extern int meson_clk_measure(unsigned int clk_mux);
+
+#endif
struct aml_jack hp_jack;
struct aml_jack mic_jack;
struct snd_soc_dai_link *dai_link;
+ int spk_mute_gpio;
+ bool spk_mute_active_low;
};
#define aml_priv_to_dev(priv) ((priv)->snd_card.dev)
#define aml_priv_to_link(priv, i) ((priv)->snd_card.dai_link + (i))
#define aml_priv_to_props(priv, i) ((priv)->dai_props + (i))
+#define aml_card_to_priv(card) \
+ (container_of(card, struct aml_card_data, snd_card))
#define DAI "sound-dai"
#define CELL "#sound-dai-cells"
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct aml_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
struct aml_dai_props *dai_props =
unsigned int mclk, mclk_fs = 0;
int i = 0, ret = 0;
- pr_info("%s ..numcodec:%d\n", __func__, rtd->num_codecs);
if (priv->mclk_fs)
mclk_fs = priv->mclk_fs;
else if (dai_props->mclk_fs)
mclk = params_rate(params) * mclk_fs;
for (i = 0; i < rtd->num_codecs; i++) {
- struct snd_soc_dai *codec_dai = rtd->codec_dais[i];
+ codec_dai = rtd->codec_dais[i];
ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk,
SND_SOC_CLOCK_IN);
if (ret && ret != -ENOTSUPP)
goto err;
}
+
ret = snd_soc_dai_set_sysclk(cpu_dai, 0, mclk,
SND_SOC_CLOCK_OUT);
if (ret && ret != -ENOTSUPP)
struct snd_soc_dai *cpu = rtd->cpu_dai;
struct aml_dai_props *dai_props =
aml_priv_to_props(priv, rtd->num);
- int ret;
+ int ret, i;
- ret = aml_card_init_dai(codec, &dai_props->codec_dai);
- if (ret < 0)
- return ret;
+ for (i = 0; i < rtd->num_codecs; i++) {
+ codec = rtd->codec_dais[i];
+
+ ret = aml_card_init_dai(codec, &dai_props->codec_dai);
+ if (ret < 0)
+ return ret;
+ }
ret = aml_card_init_dai(cpu, &dai_props->cpu_dai);
if (ret < 0)
if (ret < 0)
goto dai_link_of_err;
+ ret = aml_card_parse_codec_confs(codec, &priv->snd_card);
+ if (ret < 0)
+ goto dai_link_of_err;
+
ret = aml_card_parse_clk_cpu(cpu, dai_link, cpu_dai);
if (ret < 0)
goto dai_link_of_err;
return 0;
}
+static int spk_mute_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_card *soc_card = snd_kcontrol_chip(kcontrol);
+ struct aml_card_data *priv = aml_card_to_priv(soc_card);
+ int gpio = priv->spk_mute_gpio;
+ bool active_low = priv->spk_mute_active_low;
+ bool mute = ucontrol->value.integer.value[0];
+
+ if (gpio_is_valid(gpio)) {
+ bool value = active_low ? !mute : mute;
+
+ gpio_set_value(gpio, value);
+ pr_info("spk_mute_set: mute flag = %d\n", mute);
+ }
+
+ return 0;
+}
+
+static int spk_mute_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_card *soc_card = snd_kcontrol_chip(kcontrol);
+ struct aml_card_data *priv = aml_card_to_priv(soc_card);
+ int gpio = priv->spk_mute_gpio;
+ bool active_low = priv->spk_mute_active_low;
+
+ if (gpio_is_valid(gpio)) {
+ bool value = gpio_get_value(gpio);
+ bool mute = active_low ? !value : value;
+
+ ucontrol->value.integer.value[0] = mute;
+ }
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new card_controls[] = {
+ SOC_SINGLE_BOOL_EXT("SPK mute", 0,
+ spk_mute_get,
+ spk_mute_set),
+};
+
+static int aml_card_parse_gpios(struct device_node *node,
+ struct aml_card_data *priv)
+{
+ struct device *dev = aml_priv_to_dev(priv);
+ struct snd_soc_card *soc_card = &priv->snd_card;
+ enum of_gpio_flags flags;
+ int gpio;
+ bool active_low;
+ int ret;
+
+ gpio = of_get_named_gpio_flags(node, "spk_mute", 0, &flags);
+ priv->spk_mute_gpio = gpio;
+
+ if (gpio_is_valid(gpio)) {
+ active_low = !!(flags & OF_GPIO_ACTIVE_LOW);
+ flags = active_low ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW;
+ priv->spk_mute_active_low = active_low;
+
+ ret = devm_gpio_request_one(dev, gpio, flags, "spk_mute");
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_add_card_controls(soc_card, card_controls,
+ ARRAY_SIZE(card_controls));
+ }
+
+ return 0;
+}
+
static int aml_card_parse_of(struct device_node *node,
struct aml_card_data *priv)
{
snd_soc_card_set_drvdata(&priv->snd_card, priv);
ret = devm_snd_soc_register_card(&pdev->dev, &priv->snd_card);
+ if (ret < 0) {
+ goto err;
+ }
+
+ ret = aml_card_parse_gpios(np, priv);
if (ret >= 0)
return ret;
err:
#include "card_utils.h"
int aml_card_parse_daifmt(struct device *dev,
- struct device_node *node,
- struct device_node *codec,
- char *prefix,
- unsigned int *retfmt)
+ struct device_node *node,
+ struct device_node *codec,
+ char *prefix,
+ unsigned int *retfmt)
{
struct device_node *bitclkmaster = NULL;
struct device_node *framemaster = NULL;
}
int aml_card_set_dailink_name(struct device *dev,
- struct snd_soc_dai_link *dai_link,
- const char *fmt, ...)
+ struct snd_soc_dai_link *dai_link,
+ const char *fmt, ...)
{
va_list ap;
char *name = NULL;
}
int aml_card_parse_card_name(struct snd_soc_card *card,
- char *prefix)
+ char *prefix)
{
char prop[128];
int ret;
}
int aml_card_parse_clk(struct device_node *node,
- struct device_node *dai_of_node,
- struct aml_dai *aml_dai)
+ struct device_node *dai_of_node,
+ struct aml_dai *aml_dai)
{
struct clk *clk;
u32 val;
}
int aml_card_parse_dai(struct device_node *node,
- struct device_node **dai_of_node,
- const char **dai_name,
- const char *list_name,
- const char *cells_name,
- int *is_single_link)
+ struct device_node **dai_of_node,
+ const char **dai_name,
+ const char *list_name,
+ const char *cells_name,
+ int *is_single_link)
{
struct of_phandle_args args;
int ret;
return 0;
}
+int aml_card_parse_codec_confs(struct device_node *codec_np,
+ struct snd_soc_card *card)
+{
+ struct snd_soc_codec_conf *confs;
+ int num_confs;
+ int i = 0, ret = 0;
+
+ num_confs = of_property_count_strings(codec_np, "prefix-names");
+ if (num_confs <= 0)
+ return 0;
+
+ confs = devm_kzalloc(card->dev,
+ sizeof(*confs) * num_confs, GFP_KERNEL);
+ if (!confs) {
+ ret = -ENOMEM;
+ goto codec_confs_end;
+ }
+
+ card->codec_conf = confs;
+ card->num_configs = num_confs;
+ /*
+ * parse "prefix-names" and "sound-dai" pair
+ * add codec_conf by these two items
+ */
+ for (i = 0; i < num_confs; i++) {
+ ret = of_property_read_string_index(codec_np, "prefix-names", i,
+ &confs[i].name_prefix);
+ if (ret < 0)
+ goto codec_confs_end;
+
+ confs[i].of_node = of_parse_phandle(codec_np, "sound-dai", i);
+ if (!confs[i].of_node) {
+ ret = -ENODATA;
+ goto codec_confs_end;
+ }
+ }
+
+codec_confs_end:
+
+ return ret;
+}
+
int aml_card_init_dai(struct snd_soc_dai *dai,
- struct aml_dai *aml_dai)
+ struct aml_dai *aml_dai)
{
int ret;
}
void aml_card_canonicalize_cpu(struct snd_soc_dai_link *dai_link,
- int is_single_links)
+ int is_single_links)
{
/*
* In soc_bind_dai_link() will check cpu name after
int aml_card_clean_reference(struct snd_soc_card *card)
{
struct snd_soc_dai_link *dai_link;
- int num_links;
+ struct snd_soc_codec_conf *codec_conf;
+ int num_links, num_confs;
for (num_links = 0, dai_link = card->dai_link;
num_links < card->num_links;
of_node_put(dai_link->cpu_of_node);
of_node_put(dai_link->codec_of_node);
}
+
+ for (num_confs = 0, codec_conf = card->codec_conf;
+ num_confs < card->num_configs;
+ num_confs++, codec_conf++) {
+ of_node_put(codec_conf->of_node);
+ }
+
return 0;
}
};
int aml_card_parse_daifmt(struct device *dev,
- struct device_node *node,
- struct device_node *codec,
- char *prefix,
- unsigned int *retfmt);
+ struct device_node *node,
+ struct device_node *codec,
+ char *prefix,
+ unsigned int *retfmt);
__printf(3, 4)
int aml_card_set_dailink_name(struct device *dev,
- struct snd_soc_dai_link *dai_link,
- const char *fmt, ...);
+ struct snd_soc_dai_link *dai_link,
+ const char *fmt, ...);
int aml_card_parse_card_name(struct snd_soc_card *card,
- char *prefix);
+ char *prefix);
#define aml_card_parse_clk_cpu(node, dai_link, aml_dai) \
aml_card_parse_clk(node, dai_link->cpu_of_node, aml_dai)
#define aml_card_parse_clk_codec(node, dai_link, aml_dai) \
aml_card_parse_clk(node, dai_link->codec_of_node, aml_dai)
int aml_card_parse_clk(struct device_node *node,
- struct device_node *dai_of_node,
- struct aml_dai *aml_dai);
+ struct device_node *dai_of_node,
+ struct aml_dai *aml_dai);
#define aml_card_parse_cpu(node, dai_link, \
list_name, cells_name, is_single_link) \
aml_card_parse_dai(node, &dai_link->platform_of_node, \
NULL, list_name, cells_name, NULL)
int aml_card_parse_dai(struct device_node *node,
- struct device_node **endpoint_np,
- const char **dai_name,
- const char *list_name,
- const char *cells_name,
- int *is_single_links);
+ struct device_node **endpoint_np,
+ const char **dai_name,
+ const char *list_name,
+ const char *cells_name,
+ int *is_single_links);
+int aml_card_parse_codec_confs(struct device_node *codec_np,
+ struct snd_soc_card *card);
int aml_card_init_dai(struct snd_soc_dai *dai,
- struct aml_dai *aml_dai);
+ struct aml_dai *aml_dai);
int aml_card_canonicalize_dailink(struct snd_soc_dai_link *dai_link);
void aml_card_canonicalize_cpu(struct snd_soc_dai_link *dai_link,
- int is_single_links);
+ int is_single_links);
int aml_card_clean_reference(struct snd_soc_card *card);
#include <sound/soc.h>
#include <sound/pcm_params.h>
+#include <linux/amlogic/clk_measure.h>
+
#include "ddr_mngr.h"
#include "tdm_hw.h"
SNDRV_PCM_FMTBIT_S32_LE,
.period_bytes_min = 64,
- .period_bytes_max = 128 * 1024,
+ .period_bytes_max = 256 * 1024,
.periods_min = 2,
.periods_max = 1024,
- .buffer_bytes_max = 256 * 1024,
+ .buffer_bytes_max = 512 * 1024,
.rate_min = 8000,
.rate_max = 48000,
int i;
if (!of_slot_mask)
- return 0;
+ return -EINVAL;
val /= sizeof(u32);
for (i = 0; i < val; i++)
static int aml_dai_tdm_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *cpu_dai)
{
- struct aml_tdm *p_tdm = snd_soc_dai_get_drvdata(cpu_dai);
-
- aml_tdm_fifo_reset(p_tdm->actrl, substream->stream, p_tdm->id);
-
return 0;
}
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- aml_tdm_enable(p_tdm->actrl,
- substream->stream, p_tdm->id, true);
+ /* reset fifo here.
+ * If not, xrun will cause channel mapping mismatch
+ */
+ aml_tdm_fifo_reset(p_tdm->actrl, substream->stream, p_tdm->id);
+
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
dev_info(substream->pcm->card->dev, "tdm playback enable\n");
aml_frddr_enable(p_tdm->fddr, 1);
aml_toddr_enable(p_tdm->tddr, 1);
}
+ aml_tdm_enable(p_tdm->actrl,
+ substream->stream, p_tdm->id, true);
+
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
aml_tdm_enable(p_tdm->actrl,
substream->stream, p_tdm->id, false);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- dev_info(substream->pcm->card->dev, "tdm playback enable\n");
+ dev_info(substream->pcm->card->dev, "tdm playback stop\n");
aml_frddr_enable(p_tdm->fddr, 0);
} else {
- dev_info(substream->pcm->card->dev, "tdm capture enable\n");
+ dev_info(substream->pcm->card->dev, "tdm capture stop\n");
aml_toddr_enable(p_tdm->tddr, 0);
}
break;
static int pcm_setting_init(struct pcm_setting *setting, unsigned int rate,
unsigned int channels)
{
-
+ unsigned int ratio = 0;
setting->lrclk = rate;
setting->bclk_lrclk_ratio = setting->slots * setting->slot_width;
setting->bclk = setting->lrclk * setting->bclk_lrclk_ratio;
/* calculate mclk */
- setting->sysclk_bclk_ratio = 4;
- setting->sysclk = 4 * setting->bclk;
+ if (setting->pcm_mode == SND_SOC_DAIFMT_DSP_A ||
+ setting->pcm_mode == SND_SOC_DAIFMT_DSP_B) {
+ /* for some TDM codec, mclk limites */
+ ratio = 2;
+ } else {
+ ratio = 4;
+ }
+ setting->sysclk_bclk_ratio = ratio;
+ setting->sysclk = ratio * setting->bclk;
return 0;
}
return 0;
}
+/* mpll clk range from 5M to 500M */
+#define AML_MPLL_FREQ_MIN 5000000
+static unsigned int aml_mpll_mclk_ratio(unsigned int freq)
+{
+ unsigned int i, ratio = 2;
+ unsigned int mpll_freq = 0;
+
+ for (i = 1; i < 15; i++) {
+ ratio = 1 << i;
+ mpll_freq = freq * ratio;
+
+ if (mpll_freq > AML_MPLL_FREQ_MIN)
+ break;
+ }
+
+ pr_info("TDM mpll/mclk = %d\n", ratio);
+
+ return ratio;
+}
+
static void aml_tdm_set_mclk(struct aml_tdm *p_tdm)
{
struct aml_audio_controller *actrl = p_tdm->actrl;
unsigned int clk_id, offset;
- unsigned int mpll_freq = 0;
+ unsigned int mpll_freq = 0, sysclk_freq = 0;
offset = p_tdm->clk_sel;
return;
clk_id = p_tdm->id;
+ sysclk_freq = p_tdm->setting.sysclk;
- if (p_tdm->setting.sysclk) {
- unsigned int mul = 4;
+ if (sysclk_freq) {
+ unsigned int mul = aml_mpll_mclk_ratio(sysclk_freq);
- mpll_freq = p_tdm->setting.sysclk * mul;
+ mpll_freq = sysclk_freq * mul;
clk_set_rate(p_tdm->clk, mpll_freq);
aml_audiobus_write(actrl, EE_AUDIO_MCLK_A_CTRL + offset,
1 << 31 | //clk enable
clk_id << 24 | // clk src
(mul - 1)); //clk_div mclk
-
- if (offset == 2) {
- //enable another mclka also;
- offset = 0;
- aml_audiobus_write(actrl, EE_AUDIO_MCLK_A_CTRL + offset,
- 1 << 31 | //clk enable
- clk_id << 24 | // clk src
- (mul - 1)); //clk_div mclk
- }
}
}
return -EINVAL;
}
+ /* complete mclk for tdm */
+ meson_clk_measure((1<<16) | 0x67);
+
/* parse DTS configured ddr */
ret = of_property_read_u32(node, "tdm_from_ddr",
&p_tdm->from_ddr_num);
// sclk_ph0 (pad) invert
off_set = EE_AUDIO_MST_B_SCLK_CTRL1 - EE_AUDIO_MST_A_SCLK_CTRL1;
reg_out = EE_AUDIO_MST_A_SCLK_CTRL1 + off_set * id;
- aml_audiobus_update_bits(actrl, reg_out, 0x3f, binv);
+ aml_audiobus_update_bits(actrl, reg_out, 0x3f, !binv);
+ if (!binv)
+ bclkin_skew = 4;
off_set = EE_AUDIO_TDMOUT_B_CTRL0 - EE_AUDIO_TDMOUT_A_CTRL0;
reg_out = EE_AUDIO_TDMOUT_A_CTRL0 + off_set * id;
Enable support for SSM3515 CODEC.
Select this if SSM3515 is connected via an I2C bus.
+config AMLOGIC_SND_SOC_TAS575X
+ bool "Texas Instruments TAS575X"
+ depends on AMLOGIC_SND_SOC_CODECS
+ depends on I2C
+ default n
+ help
+ Enable Support for Texas INstruments TAS575X CODEC.
+ Select this if your TAS575X is connected via an I2C bus.
+
#endif #AMLOGIC_SND_SOC_CODECS
obj-$(CONFIG_AMLOGIC_SND_SOC_PCM186X) += snd-soc-pcm186x.o
obj-$(CONFIG_AMLOGIC_SND_SOC_SSM3515) += snd-soc-ssm3515.o
obj-$(CONFIG_AMLOGIC_SND_SOC_SSM3525) += snd-soc-ssm3525.o
+obj-$(CONFIG_AMLOGIC_SND_SOC_TAS575X) += tas575x.o
+
{
struct snd_soc_codec *codec = dai->codec;
struct pcm186x_priv *priv = snd_soc_codec_get_drvdata(codec);
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
unsigned int rate = params_rate(params);
unsigned int format = params_format(params);
unsigned int width = params_width(params);
unsigned int tdm_offset;
unsigned int tdm_tx_sel;
int ret;
- unsigned int fmt;
dev_dbg(codec->dev, "%s() rate=%u format=0x%x width=%u channels=%u\n",
__func__, rate, format, width, channels);
return ret;
}
- fmt = SND_SOC_DAIFMT_DSP_A |
- SND_SOC_DAIFMT_CBS_CFS |
- SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CONT;
-
- ret = snd_soc_runtime_set_dai_fmt(rtd, fmt);
- if (ret)
- return ret;
/*
* In DSP/TDM mode, the LRCLK divider must be 256 as per datasheet.
* Also, complete the codec serial audio interface format configuration
--- /dev/null
+/*
+ * sound/soc/codecs/amlogic/tas575x.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * Author: Shuai Li <shuai.li@amlogic.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/regulator/consumer.h>
+#include <linux/delay.h>
+
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+#include "tas575x.h"
+
+struct tas575x_private {
+ //const struct tas575x_chip *chip;
+ struct regmap *regmap;
+ unsigned int format;
+ unsigned int tx_slots_mask;
+ unsigned int slot_width;
+};
+
+ /* Power-up register defaults */
+struct reg_default tas575x_reg_defaults[] = {
+ {TAS575X_MUTE, 0},
+};
+
+static int tas575x_set_dai_fmt(struct snd_soc_dai *dai, unsigned int format)
+{
+ struct tas575x_private *priv = snd_soc_codec_get_drvdata(dai->codec);
+
+ pr_info("%s, format:0x%x\n", __func__, format);
+ priv->format = format;
+
+ return 0;
+}
+
+static int tas575x_set_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct tas575x_private *priv = snd_soc_codec_get_drvdata(dai->codec);
+ unsigned int first_slot, last_slot, tdm_offset;
+ int ret;
+
+ tx_mask = priv->tx_slots_mask;
+ first_slot = __ffs(tx_mask);
+ last_slot = __fls(tx_mask);
+
+ dev_dbg(codec->dev,
+ "%s() tx_mask=0x%x rx_mask=0x%x slots=%d slot_width=%d\n",
+ __func__, tx_mask, rx_mask, slots, slot_width);
+
+ if (last_slot - first_slot != hweight32(tx_mask) - 1) {
+ dev_err(codec->dev, "tdm tx mask must be contiguous\n");
+ return -EINVAL;
+ }
+
+ tdm_offset = first_slot * slot_width;
+
+ dev_info(codec->dev, "tdm_offset:%#x, width:%d\n",
+ tdm_offset, slot_width);
+
+ if (tdm_offset > 255) {
+ dev_err(codec->dev, "tdm tx slot selection out of bounds\n");
+ return -EINVAL;
+ }
+
+ priv->slot_width = slot_width;
+ tdm_offset += 1;
+ ret = regmap_write(priv->regmap, TAS575X_I2S_SHIFT, tdm_offset);
+ if (ret < 0)
+ dev_err(codec->dev, "failed to write register: %d\n", ret);
+ return ret;
+}
+
+static int tas575x_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct tas575x_private *priv = snd_soc_codec_get_drvdata(dai->codec);
+ u32 val, width;
+ bool is_dsp = false;
+
+ switch (priv->format & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ val = 0x00 << 4;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ case SND_SOC_DAIFMT_DSP_B:
+ val = 0x01 << 4;
+ is_dsp = true;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ val = 0x02 << 4;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ val = 0x03 << 4;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ width = params_width(params);
+ if (is_dsp) {
+ /* in dsp format, bit width is fixed */
+ width = priv->slot_width;
+ } else {
+ width = params_width(params);
+ }
+
+ switch (width) {
+ case 16:
+ val |= 0x00;
+ break;
+ case 20:
+ val |= 0x01;
+ break;
+ case 24:
+ val |= 0x02;
+ break;
+ case 32:
+ val |= 0x03;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ pr_info("%s, val:0x%x\n", __func__, val);
+ return snd_soc_update_bits(codec, TAS575X_I2S_FMT,
+ 0x33, val);
+}
+
+static int tas575x_probe(struct snd_soc_codec *codec)
+{
+ /* set stanby mode */
+ snd_soc_write(codec, TAS575X_STANDBY, 0x10);
+ /* reset */
+ snd_soc_write(codec, TAS575X_RESET, 0x01);
+ /* set for DAC path */
+ snd_soc_write(codec, TAS575X_DATA_PATH, 0x22);
+ /* vlome control default to -30db*/
+ snd_soc_write(codec, TAS575X_CH_B_DIG_VOL, 0x6c);
+ snd_soc_write(codec, TAS575X_CH_A_DIG_VOL, 0x6c);
+ /* exit stanby mode */
+ snd_soc_write(codec, TAS575X_STANDBY, 0x0);
+
+ return 0;
+}
+
+static DECLARE_TLV_DB_SCALE(dac_tlv, -10350, 50, 0);
+
+static const struct snd_kcontrol_new tas575x_snd_controls[] = {
+ SOC_SINGLE_TLV("Channel B Playback Volume",
+ TAS575X_CH_B_DIG_VOL, 0, 0xff, 0, dac_tlv),
+ SOC_SINGLE_TLV("Channel A Playback Volume",
+ TAS575X_CH_A_DIG_VOL, 0, 0xff, 0, dac_tlv),
+};
+
+static struct snd_soc_codec_driver soc_codec_dev_tas575x = {
+ .probe = tas575x_probe,
+ //.remove = tas575x_remove,
+ //.suspend = tas575x_suspend,
+ //.resume = tas575x_resume,
+
+ .component_driver = {
+ .controls = tas575x_snd_controls,
+ .num_controls = ARRAY_SIZE(tas575x_snd_controls),
+ //.dapm_widgets = tas575x_dapm_widgets,
+ //.num_dapm_widgets = ARRAY_SIZE(tas575x_dapm_widgets),
+ //.dapm_routes = tas575x_audio_route,
+ //.num_dapm_routes = ARRAY_SIZE(tas575x_audio_route),
+ },
+};
+
+static const struct snd_soc_dai_ops tas575x_dai_ops = {
+ .set_fmt = tas575x_set_dai_fmt,
+ .set_tdm_slot = tas575x_set_tdm_slot,
+ .hw_params = tas575x_hw_params,
+ //.digital_mute = tas575x_mute,
+};
+
+static struct snd_soc_dai_driver tas575x_dai = {
+ .name = "tas575x-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &tas575x_dai_ops,
+};
+
+static const struct regmap_config tas575x_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = 0xff,
+ .reg_defaults = tas575x_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(tas575x_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static const struct of_device_id tas575x_of_match[] = {
+ { .compatible = "ti, tas5754", 0 },
+ { .compatible = "ti, tas5756", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(of, tas575x_of_match);
+
+static int tas575x_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct tas575x_private *priv;
+ struct device *dev = &client->dev;
+ struct device_node *node = dev->of_node;
+ int ret;
+ //const struct of_device_id *of_id;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+ i2c_set_clientdata(client, priv);
+
+ priv->regmap = devm_regmap_init_i2c(client, &tas575x_regmap);
+ if (IS_ERR(priv->regmap)) {
+ ret = PTR_ERR(priv->regmap);
+ dev_err(&client->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ if (of_property_read_bool(node, "tx_slots_mask")) {
+ ret = of_property_read_u32(node,
+ "tx_slots_mask", &priv->tx_slots_mask);
+ pr_info("got tx_slot_mask %#x\n", priv->tx_slots_mask);
+ }
+
+ return snd_soc_register_codec(dev, &soc_codec_dev_tas575x,
+ &tas575x_dai, 1);
+
+}
+
+static int tas575x_i2c_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+
+ return 0;
+}
+
+static const struct i2c_device_id tas575x_i2c_id[] = {
+ { "tas5754", (kernel_ulong_t) 0 },
+ { "tas5756", (kernel_ulong_t) 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tas575x_i2c_id);
+
+static struct i2c_driver tas575x_i2c_driver = {
+ .driver = {
+ .name = "tas575x",
+ .of_match_table = of_match_ptr(tas575x_of_match),
+ },
+ .probe = tas575x_i2c_probe,
+ .remove = tas575x_i2c_remove,
+ .id_table = tas575x_i2c_id,
+};
+module_i2c_driver(tas575x_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC TAS575x driver");
+MODULE_AUTHOR("Shuai Li <shuai.li@amlogic.com>");
+MODULE_LICENSE("GPL");
--- /dev/null
+/*
+ * sound/soc/codecs/amlogic/tas575x.h
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * Author: Shuai Li <shuai.li@amlogic.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#ifndef __TAS575X_H__
+#define __TAS575X_H__
+
+/* Register Address Map */
+#define TAS575X_RESET 1
+#define TAS575X_STANDBY 2
+#define TAS575X_MUTE 3
+#define TAS575X_PLL 4
+#define TAS575X_DOUT 7
+#define TAS575X_GPIO 8
+#define TAS575X_CLK 9
+#define TAS575X_MASTR_CLK 12
+#define TAS575X_PLL_REF 13
+#define TAS575X_CLK_SRC 14
+#define TAS575X_GPIO_SRC 18
+#define TAS575X_SYNC_REQ 19
+#define TAS575X_PLL_DIV_P 20
+#define TAS575X_PLL_DIV_J 21
+#define TAS575X_PLL_DIV_D 22
+#define TAS575X_PLL_DIV_R 24
+#define TAS575X_DSP_DIV 27
+#define TAS575X_DAC_DIV 28
+#define TAS575X_NCP_DIV 29
+#define TAS575X_OSR_DIV 30
+#define TAS575X_SCLK_DIV 32
+#define TAS575X_LRCLK_DIV 33
+#define TAS575X_16X_INTERP 34
+#define TAS575X_DSP_CLK_CYCLM 35
+#define TAS575X_DSP_CLK_CYCLL 36
+#define TAS575X_IGNORES 37
+#define TAS575X_I2S_FMT 40
+#define TAS575X_I2S_SHIFT 41
+#define TAS575X_DATA_PATH 42
+#define TAS575X_DSP_PROG_SEL 43
+#define TAS575X_CLK_DET_PRD 44
+#define TAS575X_MUTE_TIME 59
+#define TAS575X_DIG_VOL_CTRL 60
+#define TAS575X_CH_B_DIG_VOL 61
+#define TAS575X_CH_A_DIG_VOL 62
+#define TAS575X_VOL_NORM_RAMP 63
+#define TAS575X_VOL_EMRG_RAMP 64
+#define TAS575X_AUTO_MUTE_CTRL 65
+#define TAS575X_GPIO1_SEL 82
+#define TAS575X_GPIO0_SEL 83
+#define TAS575X_GPIO2_SEL 85
+#define TAS575X_GPIO_CTRL 86
+#define TAS575X_GPIO_INV 87
+#define TAS575X_CHANL_OVFLOW 90
+#define TAS575X_MCLK_DET 91
+#define TAS575X_SCLK_DET_MSB 92
+#define TAS575X_SCLK_DET_LSB 93
+#define TAS575X_CLK_DET_STAT 94
+#define TAS575X_CLK_STAT 95
+#define TAS575X_ANLG_MUTE_STAT 108
+#define TAS575X_SHORT_STAT 109
+#define TAS575X_SPK_MUTE_STAT 114
+#define TAS575X_FS_SPD_MODE 115
+#define TAS575X_PWR_STAT 117
+#define TAS575X_GPIO_STAT 119
+#define TAS575X_AUTO_MUTE 120
+#define TAS575X_DAC_MODE 121
+
+#endif /* __TAS575X_H__ */
pr_err("adc3101: invalid DAI master/slave interface\n");
return -EINVAL;
}
+ /* set lrclk/bclk invertion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_IB_IF:
+ case SND_SOC_DAIFMT_IB_NF:
+ iface_reg_2 |= (1 << 3); /* invert bit clock */
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ default:
+ break;
+ }
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S: