mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-08 20:07:46 +09:00
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:
@@ -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
|
||||
@@ -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>;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
23
include/linux/amlogic/clk_measure.h
Normal file
23
include/linux/amlogic/clk_measure.h
Normal 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
|
||||
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
299
sound/soc/codecs/amlogic/tas575x.c
Normal file
299
sound/soc/codecs/amlogic/tas575x.c
Normal 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");
|
||||
82
sound/soc/codecs/amlogic/tas575x.h
Normal file
82
sound/soc/codecs/amlogic/tas575x.h
Normal 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__ */
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user