From: Shuai Li Date: Wed, 7 Jun 2017 03:41:42 +0000 (-0700) Subject: audio: add new audio features X-Git-Tag: khadas-vims-v0.9.6-release~3043 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=189e32aa662d3fef7ce2b24dadc5956766dd5dd7;p=platform%2Fkernel%2Flinux-amlogic.git 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 --- diff --git a/MAINTAINERS b/MAINTAINERS index 92ce515..42e1b49 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13925,6 +13925,7 @@ M: xing wang 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 +F: include/linux/amlogic/clk_measure.h \ No newline at end of file diff --git a/arch/arm64/boot/dts/amlogic/axg_s400.dts b/arch/arm64/boot/dts/amlogic/axg_s400.dts index ecb54b8e..298bbc4 100644 --- a/arch/arm64/boot/dts/amlogic/axg_s400.dts +++ b/arch/arm64/boot/dts/amlogic/axg_s400.dts @@ -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>; diff --git a/arch/arm64/boot/dts/amlogic/axg_s420.dts b/arch/arm64/boot/dts/amlogic/axg_s420.dts index c357107..710c2c4 100644 --- a/arch/arm64/boot/dts/amlogic/axg_s420.dts +++ b/arch/arm64/boot/dts/amlogic/axg_s420.dts @@ -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 { diff --git a/arch/arm64/configs/meson64_defconfig b/arch/arm64/configs/meson64_defconfig index c622932..ea86d33 100644 --- a/arch/arm64/configs/meson64_defconfig +++ b/arch/arm64/configs/meson64_defconfig @@ -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 diff --git a/drivers/amlogic/clk/clk_measure.c b/drivers/amlogic/clk/clk_measure.c index 2b60aae..87ab692 100644 --- a/drivers/amlogic/clk/clk_measure.c +++ b/drivers/amlogic/clk/clk_measure.c @@ -33,6 +33,7 @@ #include #include #include +#include #undef pr_fmt #define pr_fmt(fmt) "clkmsr: " fmt diff --git a/include/linux/amlogic/clk_measure.h b/include/linux/amlogic/clk_measure.h new file mode 100644 index 0000000..0a615a4 --- /dev/null +++ b/include/linux/amlogic/clk_measure.h @@ -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 diff --git a/sound/soc/amlogic/auge/card.c b/sound/soc/amlogic/auge/card.c index f889069..5ec2464a4 100644 --- a/sound/soc/amlogic/auge/card.c +++ b/sound/soc/amlogic/auge/card.c @@ -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: diff --git a/sound/soc/amlogic/auge/card_utils.c b/sound/soc/amlogic/auge/card_utils.c index 3b6b8e1..68cea02 100644 --- a/sound/soc/amlogic/auge/card_utils.c +++ b/sound/soc/amlogic/auge/card_utils.c @@ -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; } diff --git a/sound/soc/amlogic/auge/card_utils.h b/sound/soc/amlogic/auge/card_utils.h index c88dcf88..49dafd6 100644 --- a/sound/soc/amlogic/auge/card_utils.h +++ b/sound/soc/amlogic/auge/card_utils.h @@ -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); diff --git a/sound/soc/amlogic/auge/tdm.c b/sound/soc/amlogic/auge/tdm.c index 659500e..6ba4b42 100644 --- a/sound/soc/amlogic/auge/tdm.c +++ b/sound/soc/amlogic/auge/tdm.c @@ -31,6 +31,8 @@ #include #include +#include + #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); diff --git a/sound/soc/amlogic/auge/tdm_hw.c b/sound/soc/amlogic/auge/tdm_hw.c index 25f24e4..6cf3b14 100644 --- a/sound/soc/amlogic/auge/tdm_hw.c +++ b/sound/soc/amlogic/auge/tdm_hw.c @@ -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; diff --git a/sound/soc/codecs/amlogic/Kconfig b/sound/soc/codecs/amlogic/Kconfig index 4dcd663..eafdb2a 100644 --- a/sound/soc/codecs/amlogic/Kconfig +++ b/sound/soc/codecs/amlogic/Kconfig @@ -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 diff --git a/sound/soc/codecs/amlogic/Makefile b/sound/soc/codecs/amlogic/Makefile index 9c3fc357..81ba8e5 100644 --- a/sound/soc/codecs/amlogic/Makefile +++ b/sound/soc/codecs/amlogic/Makefile @@ -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 + diff --git a/sound/soc/codecs/amlogic/pcm186x.c b/sound/soc/codecs/amlogic/pcm186x.c index ad9e99d..4fdc83a 100644 --- a/sound/soc/codecs/amlogic/pcm186x.c +++ b/sound/soc/codecs/amlogic/pcm186x.c @@ -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 diff --git a/sound/soc/codecs/amlogic/tas575x.c b/sound/soc/codecs/amlogic/tas575x.c new file mode 100644 index 0000000..37de371 --- /dev/null +++ b/sound/soc/codecs/amlogic/tas575x.c @@ -0,0 +1,299 @@ +/* + * sound/soc/codecs/amlogic/tas575x.c + * + * Copyright (C) 2017 Amlogic, Inc. All rights reserved. + * + * Author: Shuai Li + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#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 "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/amlogic/tas575x.h b/sound/soc/codecs/amlogic/tas575x.h new file mode 100644 index 0000000..ff80ee8 --- /dev/null +++ b/sound/soc/codecs/amlogic/tas575x.h @@ -0,0 +1,82 @@ +/* + * sound/soc/codecs/amlogic/tas575x.h + * + * Copyright (C) 2017 Amlogic, Inc. All rights reserved. + * + * Author: Shuai Li + * + * 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__ */ diff --git a/sound/soc/codecs/amlogic/tlv320adc3101.c b/sound/soc/codecs/amlogic/tlv320adc3101.c index 67830f2..b377816 100644 --- a/sound/soc/codecs/amlogic/tlv320adc3101.c +++ b/sound/soc/codecs/amlogic/tlv320adc3101.c @@ -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: