audio: add new audio features

PD#145715: new audio features:
1. mute gpio
2. tas575x driver
3. multi-codec prefix name
4. tlv320adc3101 can revert bclk
5. set clkmsr to check mclk for AXG tdm

Change-Id: Ibd3d9ed8086715439c4bebeb574b998c1ffd1e45
Signed-off-by: Shuai Li <shuai.li@amlogic.com>
This commit is contained in:
Shuai Li
2017-06-06 20:41:42 -07:00
committed by Xing Wang
parent abb7e885c1
commit 189e32aa66
17 changed files with 711 additions and 110 deletions

View File

@@ -13925,6 +13925,7 @@ M: xing wang <xing.wang@amlogic.com>
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
@@ -13969,3 +13970,7 @@ F: drivers/amlogic/irblaster/irblaster.c
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

View File

@@ -471,7 +471,7 @@
mclk-fs = <256>;
continuous-clock;
//bitclock-inversion;
//frame-inversion;
frame-inversion;
bitclock-master = <&aml_tdmb>;
frame-master = <&aml_tdmb>;
cpu {
@@ -483,6 +483,8 @@
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>;
};
@@ -492,7 +494,7 @@
format = "i2s";
mclk-fs = <256>;
continuous-clock;
bitclock-inversion;
//bitclock-inversion;
frame-inversion;
//bitclock-master = <&aml_tdmc>;
//frame-master = <&aml_tdmc>;
@@ -505,6 +507,7 @@
system-clock-frequency = <12288000>;
};
codec {
prefix-names = "5707_A", "5707_B";
sound-dai = <&tas5707_36 &tas5707_3a
&dummy_codec>;
};
@@ -835,7 +838,8 @@
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>;
@@ -854,7 +858,7 @@
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>;

View File

@@ -330,7 +330,7 @@
//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;
@@ -354,8 +354,8 @@
format = "i2s";
mclk-fs = <256>;
continuous-clock;
bitclock-inversion;
//frame-inversion;
//bitclock-inversion;
frame-inversion;
bitclock-master = <&aml_tdmb>;
frame-master = <&aml_tdmb>;
cpu {
@@ -367,8 +367,7 @@
system-clock-frequency = <12288000>;
};
codec {
//sound-dai = <&dummy_codec &dummy_codec>;
sound-dai = <&tas5707_36>;
sound-dai = <&tas5707_36 &tlv320adc3101_32>;
};
};
@@ -376,8 +375,9 @@
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 {
@@ -389,8 +389,7 @@
system-clock-frequency = <12288000>;
};
codec {
//sound-dai = <&tas5707_36 &tas5707_3a>;
sound-dai = <&dummy_codec &dummy_codec>;
sound-dai = <&dummy_codec>;
};
};
@@ -629,7 +628,7 @@
compatible = "ti,tlv320adc3101";
#sound-dai-cells = <0>;
reg = <0x19>;
status = "disabled";
status = "okay";
};
tas5707_36: tas5707_36@36 {
@@ -663,19 +662,19 @@
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";
};
};
@@ -703,7 +702,8 @@
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>;
@@ -714,13 +714,14 @@
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>;
@@ -731,7 +732,7 @@
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 {
@@ -801,8 +802,7 @@
tdmout_b: tdmout_b {
mux {
pins = "GPIOA_8", "GPIOA_9", "GPIOA_10",
"GPIOA_11";
pins = "GPIOA_8", "GPIOA_9", "GPIOA_10";
function = "tdmb_out";
};
};
@@ -815,6 +815,12 @@
* };
*};
*/
tdmin_b: tdmin_b {
mux {
pins = "GPIOA_11";
function = "tdmb_in";
};
};
tdmc_mclk: tdmc_mclk {
mux {
@@ -825,20 +831,17 @@
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 {

View File

@@ -389,6 +389,7 @@ CONFIG_AMLOGIC_SND_SOC_TLV320ADC3101=y
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

View File

@@ -33,6 +33,7 @@
#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

View File

@@ -0,0 +1,23 @@
/*
* 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

View File

@@ -45,11 +45,15 @@ struct aml_card_data {
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"
@@ -151,6 +155,7 @@ static int aml_card_hw_params(struct snd_pcm_substream *substream,
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 =
@@ -158,7 +163,6 @@ static int aml_card_hw_params(struct snd_pcm_substream *substream,
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)
@@ -168,7 +172,7 @@ static int aml_card_hw_params(struct snd_pcm_substream *substream,
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);
@@ -176,6 +180,7 @@ static int aml_card_hw_params(struct snd_pcm_substream *substream,
if (ret && ret != -ENOTSUPP)
goto err;
}
ret = snd_soc_dai_set_sysclk(cpu_dai, 0, mclk,
SND_SOC_CLOCK_OUT);
if (ret && ret != -ENOTSUPP)
@@ -199,11 +204,15 @@ static int aml_card_dai_init(struct snd_soc_pcm_runtime *rtd)
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)
@@ -294,6 +303,10 @@ static int aml_card_dai_link_of(struct device_node *node,
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;
@@ -366,6 +379,78 @@ static int aml_card_parse_aux_devs(struct device_node *node,
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)
{
@@ -512,6 +597,11 @@ static int aml_card_probe(struct platform_device *pdev)
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:

View File

@@ -21,10 +21,10 @@
#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;
@@ -63,8 +63,8 @@ int aml_card_parse_daifmt(struct device *dev,
}
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;
@@ -85,7 +85,7 @@ int aml_card_set_dailink_name(struct device *dev,
}
int aml_card_parse_card_name(struct snd_soc_card *card,
char *prefix)
char *prefix)
{
char prop[128];
int ret;
@@ -104,8 +104,8 @@ int aml_card_parse_card_name(struct snd_soc_card *card,
}
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;
@@ -133,11 +133,11 @@ int aml_card_parse_clk(struct device_node *node,
}
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;
@@ -168,8 +168,50 @@ int aml_card_parse_dai(struct device_node *node,
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;
@@ -210,7 +252,7 @@ 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)
{
/*
* In soc_bind_dai_link() will check cpu name after
@@ -228,7 +270,8 @@ void aml_card_canonicalize_cpu(struct snd_soc_dai_link *dai_link,
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;
@@ -236,5 +279,12 @@ int aml_card_clean_reference(struct snd_soc_card *card)
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;
}

View File

@@ -31,24 +31,24 @@ struct aml_dai {
};
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) \
@@ -61,18 +61,20 @@ int aml_card_parse_clk(struct device_node *node,
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);

View File

@@ -31,6 +31,8 @@
#include <sound/soc.h>
#include <sound/pcm_params.h>
#include <linux/amlogic/clk_measure.h>
#include "ddr_mngr.h"
#include "tdm_hw.h"
@@ -91,10 +93,10 @@ static const struct snd_pcm_hardware aml_tdm_hardware = {
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,
@@ -142,7 +144,7 @@ static int snd_soc_of_get_slot_mask(struct device_node *np,
int i;
if (!of_slot_mask)
return 0;
return -EINVAL;
val /= sizeof(u32);
for (i = 0; i < val; i++)
@@ -330,10 +332,6 @@ struct snd_soc_platform_driver aml_tdm_platform = {
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;
}
@@ -434,8 +432,11 @@ static int aml_dai_tdm_trigger(struct snd_pcm_substream *substream, int cmd,
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);
@@ -444,6 +445,9 @@ static int aml_dai_tdm_trigger(struct snd_pcm_substream *substream, int cmd,
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:
@@ -451,10 +455,10 @@ static int aml_dai_tdm_trigger(struct snd_pcm_substream *substream, int cmd,
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;
@@ -468,14 +472,21 @@ static int aml_dai_tdm_trigger(struct snd_pcm_substream *substream, int cmd,
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;
}
@@ -600,11 +611,31 @@ static int aml_dai_set_tdm_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
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;
@@ -613,25 +644,17 @@ static void aml_tdm_set_mclk(struct aml_tdm *p_tdm)
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
}
}
}
@@ -913,6 +936,9 @@ static int aml_tdm_platform_probe(struct platform_device *pdev)
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);

View File

@@ -227,7 +227,9 @@ void aml_tdm_set_format(
// 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;

View File

@@ -114,4 +114,13 @@ config AMLOGIC_SND_SOC_SSM3515
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

View File

@@ -25,3 +25,5 @@ obj-$(CONFIG_AMLOGIC_SND_SOC_TLV320ADC3101) += snd-soc-tlv320adc3101.o
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

View File

@@ -773,7 +773,6 @@ static int pcm186x_hw_params(struct snd_pcm_substream *substream,
{
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);
@@ -786,7 +785,6 @@ static int pcm186x_hw_params(struct snd_pcm_substream *substream,
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);
@@ -821,14 +819,6 @@ static int pcm186x_hw_params(struct snd_pcm_substream *substream,
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

View File

@@ -0,0 +1,299 @@
/*
* 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");

View File

@@ -0,0 +1,82 @@
/*
* 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__ */

View File

@@ -322,6 +322,18 @@ static int adc3101_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
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: