Try to both reduce useless verbosity and keep useful error reports.
Pierre-Louis Bossart (3):
ASoC: soc-core: reduce verbosity of BE override message
ASoC: soc-pcm: improve error messages in soc_pcm_new()
ASoC: soc-pcm/compress: reduce verbosity on mapping ok messages
sound/soc/soc-compress.c | 4 ++--
sound/soc/soc-core.c | 4 ++--
sound/soc/soc-pcm.c | 13 +++++++------
3 files changed, 11 insertions(+), 10 deletions(-)
--
2.20.1
-Maxim MAX98357A audio DAC
+Maxim MAX98357A/MAX98360A audio DAC
-This node models the Maxim MAX98357A DAC.
+This node models the Maxim MAX98357A/MAX98360A DAC.
Required properties:
-- compatible : "maxim,max98357a"
+- compatible : "maxim,max98357a" for MAX98357A.
+ "maxim,max98360a" for MAX98360A.
Optional properties:
- sdmode-gpios : GPIO specifier for the chip's SD_MODE pin.
compatible = "maxim,max98357a";
sdmode-gpios = <&qcom_pinmux 25 0>;
};
+
+max98360a {
+ compatible = "maxim,max98360a";
+ sdmode-gpios = <&qcom_pinmux 25 0>;
+};
--- /dev/null
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/maxim,max98390.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Maxim Integrated MAX98390 Speaker Amplifier with Integrated Dynamic Speaker Management
+
+maintainers:
+ - Steve Lee <steves.lee@maximintegrated.com>
+
+properties:
+ compatible:
+ const: maxim,max98390
+
+ reg:
+ maxItems: 1
+ description: I2C address of the device.
+
+ maxim,temperature_calib:
+ allOf:
+ - $ref: /schemas/types.yaml#/definitions/uint32
+ description: The calculated temperature data was measured while doing the calibration.
+ minimum: 0
+ maximum: 65535
+
+ maxim,r0_calib:
+ allOf:
+ - $ref: /schemas/types.yaml#/definitions/uint32
+ description: This is r0 calibration data which was measured in factory mode.
+ minimum: 1
+ maximum: 8388607
+
+required:
+ - compatible
+ - reg
+
+additionalProperties: false
+
+examples:
+ - |
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ max98390: amplifier@38 {
+ compatible = "maxim,max98390";
+ reg = <0x38>;
+ maxim,temperature_calib = <1024>;
+ maxim,r0_calib = <100232>;
+ };
+ };
- compatible : "mediatek,mt6358-sound".
- Avdd-supply : power source of AVDD
+Optional properties:
+- mediatek,dmic-mode : Indicates how many data pins are used to transmit two
+ channels of PDM signal. 0 means two wires, 1 means one wire. Default
+ value is 0.
+
Example:
mt6358_snd {
compatible = "mediatek,mt6358-sound";
Avdd-supply = <&mt6358_vaud28_reg>;
+ mediatek,dmic-mode = <0>;
};
--- /dev/null
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/samsung,aries-wm8994.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Samsung Aries audio complex with WM8994 codec
+
+maintainers:
+ - Jonathan Bakker <xc-racer2@live.ca>
+
+properties:
+ compatible:
+ oneOf:
+ - const: samsung,aries-wm8994
+ description: With FM radio and modem master
+
+ - const: samsung,fascinate4g-wm8994
+ description: Without FM radio and modem slave
+
+ model:
+ $ref: /schemas/types.yaml#/definitions/string
+ description: The user-visible name of this sound complex.
+
+ cpu:
+ type: object
+ properties:
+ sound-dai:
+ minItems: 2
+ maxItems: 2
+ $ref: /schemas/types.yaml#/definitions/phandle-array
+ description: |
+ phandles to the I2S controller and bluetooth codec,
+ in that order
+
+ codec:
+ type: object
+ properties:
+ sound-dai:
+ $ref: /schemas/types.yaml#/definitions/phandle-array
+ description: phandle to the WM8994 CODEC
+
+ samsung,audio-routing:
+ $ref: /schemas/types.yaml#/definitions/non-unique-string-array
+ description: |
+ List of the connections between audio
+ components; each entry is a pair of strings, the first being the
+ connection's sink, the second being the connection's source;
+ valid names for sources and sinks are the WM8994's pins (as
+ documented in its binding), and the jacks on the board -
+ For samsung,aries-wm8994: HP, SPK, RCV, LINE, Main Mic, Headset Mic,
+ or FM In
+ For samsung,fascinate4g-wm8994: HP, SPK, RCV, LINE, Main Mic,
+ or HeadsetMic
+
+ extcon:
+ description: Extcon phandle for dock detection
+
+ main-micbias-supply:
+ description: Supply for the micbias on the main mic
+
+ headset-micbias-supply:
+ description: Supply for the micbias on the headset mic
+
+ earpath-sel-gpios:
+ description: GPIO for switching between tv-out and mic paths
+
+ headset-detect-gpios:
+ description: GPIO for detection of headset insertion
+
+ headset-key-gpios:
+ description: GPIO for detection of headset key press
+
+ io-channels:
+ maxItems: 1
+ description: IO channel to read micbias voltage for headset detection
+
+ io-channel-names:
+ const: headset-detect
+
+required:
+ - compatible
+ - model
+ - cpu
+ - codec
+ - samsung,audio-routing
+ - extcon
+ - main-micbias-supply
+ - headset-micbias-supply
+ - earpath-sel-gpios
+ - headset-detect-gpios
+ - headset-key-gpios
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+
+ sound {
+ compatible = "samsung,fascinate4g-wm8994";
+
+ model = "Fascinate4G";
+
+ extcon = <&fsa9480>;
+
+ main-micbias-supply = <&main_micbias_reg>;
+ headset-micbias-supply = <&headset_micbias_reg>;
+
+ earpath-sel-gpios = <&gpj2 6 GPIO_ACTIVE_HIGH>;
+
+ io-channels = <&adc 3>;
+ io-channel-names = "headset-detect";
+ headset-detect-gpios = <&gph0 6 GPIO_ACTIVE_HIGH>;
+ headset-key-gpios = <&gph3 6 GPIO_ACTIVE_HIGH>;
+
+ samsung,audio-routing =
+ "HP", "HPOUT1L",
+ "HP", "HPOUT1R",
+
+ "SPK", "SPKOUTLN",
+ "SPK", "SPKOUTLP",
+
+ "RCV", "HPOUT2N",
+ "RCV", "HPOUT2P",
+
+ "LINE", "LINEOUT2N",
+ "LINE", "LINEOUT2P",
+
+ "IN1LP", "Main Mic",
+ "IN1LN", "Main Mic",
+
+ "IN1RP", "Headset Mic",
+ "IN1RN", "Headset Mic";
+
+ pinctrl-names = "default";
+ pinctrl-0 = <&headset_det &earpath_sel>;
+
+ cpu {
+ sound-dai = <&i2s0>, <&bt_codec>;
+ };
+
+ codec {
+ sound-dai = <&wm8994>;
+ };
+ };
+
enabled and disabled together with HP_L and HP_R pins in response to jack
detect events.
+ - wlf,hp-cfg: A list of headphone jack detect configuration register values.
+ The list must be 3 entries long.
+ hp-cfg[0]: HPSEL[1:0] of R48 (Additional Control 4).
+ hp-cfg[1]: {HPSWEN:HPSWPOL} of R24 (Additional Control 2).
+ hp-cfg[2]: {TOCLKSEL:TOEN} of R23 (Additional Control 1).
+
+ - wlf,gpio-cfg: A list of GPIO configuration register values.
+ The list must be 2 entries long.
+ gpio-cfg[0]: ALRCGPIO of R9 (Audio interface)
+ gpio-cfg[1]: {GPIOPOL:GPIOSEL[2:0]} of R48 (Additional Control 4).
+
Example:
wm8960: codec@1a {
- wlf,csnaddr-pd : If present enable the internal pull-down resistor on
the CS/ADDR pin.
+Pins on the device (for linking into audio routes):
+
+ * IN1LN
+ * IN1LP
+ * IN2LN
+ * IN2LP:VXRN
+ * IN1RN
+ * IN1RP
+ * IN2RN
+ * IN2RP:VXRP
+ * SPKOUTLP
+ * SPKOUTLN
+ * SPKOUTRP
+ * SPKOUTRN
+ * HPOUT1L
+ * HPOUT1R
+ * HPOUT2P
+ * HPOUT2N
+ * LINEOUT1P
+ * LINEOUT1N
+ * LINEOUT2P
+ * LINEOUT2N
+
Example:
wm8994: codec@1a {
*
* soc-component.h
*
- * Copyright (c) 2019 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ * Copyright (C) 2019 Renesas Electronics Corp.
+ * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
*/
#ifndef __SOC_COMPONENT_H
#define __SOC_COMPONENT_H
return regcache_sync(component->regmap);
}
+int snd_soc_component_initialize(struct snd_soc_component *component,
+ const struct snd_soc_component_driver *driver,
+ struct device *dev, const char *name);
+void snd_soc_component_set_aux(struct snd_soc_component *component,
+ struct snd_soc_aux_dev *aux);
+int snd_soc_component_init(struct snd_soc_component *component);
+
/* component IO */
int snd_soc_component_read(struct snd_soc_component *component,
unsigned int reg, unsigned int *val);
int snd_soc_component_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level);
+void snd_soc_component_setup_regmap(struct snd_soc_component *component);
#ifdef CONFIG_REGMAP
void snd_soc_component_init_regmap(struct snd_soc_component *component,
struct regmap *regmap);
struct snd_pcm_substream *substream);
int snd_soc_component_close(struct snd_soc_component *component,
struct snd_pcm_substream *substream);
-int snd_soc_component_prepare(struct snd_soc_component *component,
- struct snd_pcm_substream *substream);
-int snd_soc_component_hw_params(struct snd_soc_component *component,
- struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params);
-int snd_soc_component_hw_free(struct snd_soc_component *component,
- struct snd_pcm_substream *substream);
-int snd_soc_component_trigger(struct snd_soc_component *component,
- struct snd_pcm_substream *substream,
- int cmd);
void snd_soc_component_suspend(struct snd_soc_component *component);
void snd_soc_component_resume(struct snd_soc_component *component);
int snd_soc_component_is_suspended(struct snd_soc_component *component);
struct vm_area_struct *vma);
int snd_soc_pcm_component_new(struct snd_soc_pcm_runtime *rtd);
void snd_soc_pcm_component_free(struct snd_soc_pcm_runtime *rtd);
+int snd_soc_pcm_component_prepare(struct snd_pcm_substream *substream);
+int snd_soc_pcm_component_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_component **last);
+void snd_soc_pcm_component_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_component *last);
+int snd_soc_pcm_component_trigger(struct snd_pcm_substream *substream,
+ int cmd);
#endif /* __SOC_COMPONENT_H */
bool capless; /* Headphone outputs configured in capless mode */
bool shared_lrclk; /* DAC and ADC LRCLKs are wired together */
+
+ /*
+ * Setup for headphone detection
+ *
+ * hp_cfg[0]: HPSEL[1:0] of R48 (Additional Control 4)
+ * hp_cfg[1]: {HPSWEN:HPSWPOL} of R24 (Additional Control 2).
+ * hp_cfg[2]: {TOCLKSEL:TOEN} of R23 (Additional Control 1).
+ */
+ u32 hp_cfg[3];
+
+ /*
+ * Setup for gpio configuration
+ *
+ * gpio_cfg[0]: ALRCGPIO of R9 (Audio interface)
+ * gpio_cfg[1]: {GPIOPOL:GPIOSEL[2:0]} of R48 (Additional Control 4).
+ */
+ u32 gpio_cfg[2];
};
#endif
# SPDX-License-Identifier: GPL-2.0
snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o soc-utils.o soc-dai.o soc-component.o
-snd-soc-core-objs += soc-pcm.o soc-io.o soc-devres.o soc-ops.o soc-link.o soc-card.o
+snd-soc-core-objs += soc-pcm.o soc-devres.o soc-ops.o soc-link.o soc-card.o
snd-soc-core-$(CONFIG_SND_SOC_COMPRESS) += soc-compress.o
ifneq ($(CONFIG_SND_SOC_TOPOLOGY),)
machine->cap_i2s_instance = I2S_BT_INSTANCE;
snd_soc_dai_set_bclk_ratio(codec_dai, 64);
- if (dmic_sel)
- gpiod_set_value(dmic_sel, 0);
return rt5682_clk_enable(substream);
}
-static int acp3x_ec_dmic1_startup(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_card *card = rtd->card;
- struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
- struct acp3x_platform_info *machine = snd_soc_card_get_drvdata(card);
+static int dmic_switch;
- machine->cap_i2s_instance = I2S_BT_INSTANCE;
- snd_soc_dai_set_bclk_ratio(codec_dai, 64);
- if (dmic_sel)
- gpiod_set_value(dmic_sel, 1);
+static int dmic_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = dmic_switch;
+ return 0;
+}
- return rt5682_clk_enable(substream);
+static int dmic_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ if (dmic_sel) {
+ dmic_switch = ucontrol->value.integer.value[0];
+ gpiod_set_value(dmic_sel, dmic_switch);
+ }
+ return 0;
}
static void rt5682_shutdown(struct snd_pcm_substream *substream)
.shutdown = rt5682_shutdown,
};
-static const struct snd_soc_ops acp3x_ec_cap1_ops = {
- .startup = acp3x_ec_dmic1_startup,
- .shutdown = rt5682_shutdown,
-};
-
SND_SOC_DAILINK_DEF(acp3x_i2s,
DAILINK_COMP_ARRAY(COMP_CPU("acp3x_i2s_playcap.0")));
SND_SOC_DAILINK_DEF(acp3x_bt,
.ops = &acp3x_ec_cap0_ops,
SND_SOC_DAILINK_REG(acp3x_bt, cros_ec, platform),
},
- {
- .name = "acp3x-ec-dmic1-capture",
- .stream_name = "Capture DMIC1",
- .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBS_CFS,
- .dpcm_capture = 1,
- .ops = &acp3x_ec_cap1_ops,
- SND_SOC_DAILINK_REG(acp3x_bt, cros_ec, platform),
- },
};
+static const char * const dmic_mux_text[] = {
+ "Front Mic",
+ "Rear Mic",
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ acp3x_dmic_enum, SND_SOC_NOPM, 0, dmic_mux_text);
+
+static const struct snd_kcontrol_new acp3x_dmic_mux_control =
+ SOC_DAPM_ENUM_EXT("DMIC Select Mux", acp3x_dmic_enum,
+ dmic_get, dmic_set);
+
static const struct snd_soc_dapm_widget acp3x_widgets[] = {
SND_SOC_DAPM_HP("Headphone Jack", NULL),
SND_SOC_DAPM_SPK("Spk", NULL),
SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_MUX("Dmic Mux", SND_SOC_NOPM, 0, 0,
+ &acp3x_dmic_mux_control),
};
static const struct snd_soc_dapm_route acp3x_audio_route[] = {
{"Headphone Jack", NULL, "HPOR"},
{"IN1P", NULL, "Headset Mic"},
{"Spk", NULL, "Speaker"},
+ {"Dmic Mux", "Front Mic", "DMIC"},
+ {"Dmic Mux", "Rear Mic", "DMIC"},
};
static const struct snd_kcontrol_new acp3x_mc_controls[] = {
#ifdef CONFIG_OF
static const struct of_device_id max98357a_device_id[] = {
{ .compatible = "maxim,max98357a" },
+ { .compatible = "maxim,max98360a" },
{}
};
MODULE_DEVICE_TABLE(of, max98357a_device_id);
return 0;
}
+static void max98390_init_regs(struct snd_soc_component *component)
+{
+ struct max98390_priv *max98390 =
+ snd_soc_component_get_drvdata(component);
+
+ regmap_write(max98390->regmap, MAX98390_CLK_MON, 0x6f);
+ regmap_write(max98390->regmap, MAX98390_DAT_MON, 0x00);
+ regmap_write(max98390->regmap, MAX98390_PWR_GATE_CTL, 0x00);
+ regmap_write(max98390->regmap, MAX98390_PCM_RX_EN_A, 0x03);
+ regmap_write(max98390->regmap, MAX98390_ENV_TRACK_VOUT_HEADROOM, 0x0e);
+ regmap_write(max98390->regmap, MAX98390_BOOST_BYPASS1, 0x46);
+ regmap_write(max98390->regmap, MAX98390_FET_SCALING3, 0x03);
+}
+
static int max98390_probe(struct snd_soc_component *component)
{
struct max98390_priv *max98390 =
/* Update dsm bin param */
max98390_dsm_init(component);
- /* Amp Setting */
- regmap_write(max98390->regmap, MAX98390_CLK_MON, 0x6f);
- regmap_write(max98390->regmap, MAX98390_PCM_RX_EN_A, 0x03);
- regmap_write(max98390->regmap, MAX98390_PWR_GATE_CTL, 0x2d);
- regmap_write(max98390->regmap, MAX98390_ENV_TRACK_VOUT_HEADROOM, 0x0e);
- regmap_write(max98390->regmap, MAX98390_BOOST_BYPASS1, 0x46);
- regmap_write(max98390->regmap, MAX98390_FET_SCALING3, 0x03);
+ /* Amp init setting */
+ max98390_init_regs(component);
/* Dsm Setting */
- regmap_write(max98390->regmap, DSM_VOL_CTRL, 0x94);
- regmap_write(max98390->regmap, DSMIG_EN, 0x19);
- regmap_write(max98390->regmap, MAX98390_R203A_AMP_EN, 0x80);
if (max98390->ref_rdc_value) {
regmap_write(max98390->regmap, DSM_TPROT_RECIP_RDC_ROOM_BYTE0,
max98390->ref_rdc_value & 0x000000ff);
struct regulator *avdd_reg;
int wov_enabled;
+
+ unsigned int dmic_one_wire_mode;
};
int mt6358_set_mtkaif_protocol(struct snd_soc_component *cmpnt,
mt6358_mtkaif_tx_enable(priv);
/* UL dmic setting */
- regmap_write(priv->regmap, MT6358_AFE_UL_SRC_CON0_H, 0x0080);
+ if (priv->dmic_one_wire_mode)
+ regmap_write(priv->regmap, MT6358_AFE_UL_SRC_CON0_H, 0x0400);
+ else
+ regmap_write(priv->regmap, MT6358_AFE_UL_SRC_CON0_H, 0x0080);
/* UL turn on */
regmap_write(priv->regmap, MT6358_AFE_UL_SRC_CON0_L, 0x0003);
.num_dapm_routes = ARRAY_SIZE(mt6358_dapm_routes),
};
+static void mt6358_parse_dt(struct mt6358_priv *priv)
+{
+ int ret;
+ struct device *dev = priv->dev;
+
+ ret = of_property_read_u32(dev->of_node, "mediatek,dmic-mode",
+ &priv->dmic_one_wire_mode);
+ if (ret) {
+ dev_warn(priv->dev, "%s() failed to read dmic-mode\n",
+ __func__);
+ priv->dmic_one_wire_mode = 0;
+ }
+}
+
static int mt6358_platform_driver_probe(struct platform_device *pdev)
{
struct mt6358_priv *priv;
if (IS_ERR(priv->regmap))
return PTR_ERR(priv->regmap);
+ mt6358_parse_dt(priv);
+
dev_info(priv->dev, "%s(), dev name %s\n",
__func__, dev_name(&pdev->dev));
static const struct pll_calc_map pll_preset_table[] = {
{19200000, 4096000, 23, 14, 1, false, false},
{19200000, 24576000, 3, 30, 3, false, false},
+ {48000000, 3840000, 23, 2, 0, false, false},
{3840000, 24576000, 3, 30, 0, true, false},
+ {3840000, 22579200, 3, 5, 0, true, false},
};
static unsigned int find_best_div(unsigned int in,
{
struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component);
struct rl6231_pll_code pll_code, pll2f_code, pll2b_code;
- unsigned int pll2_fout1;
+ unsigned int pll2_fout1, pll2_ps_val;
int ret;
if (source == rt5682->pll_src[pll_id] &&
pll2b_code.n_code);
snd_soc_component_write(component, RT5682_PLL2_CTRL_3,
pll2f_code.n_code << RT5682_PLL2F_N_SFT);
+
+ if (freq_out == 22579200)
+ pll2_ps_val = 1 << RT5682_PLL2B_SEL_PS_SFT;
+ else
+ pll2_ps_val = 1 << RT5682_PLL2B_PS_BYP_SFT;
snd_soc_component_update_bits(component, RT5682_PLL2_CTRL_4,
+ RT5682_PLL2B_SEL_PS_MASK | RT5682_PLL2B_PS_BYP_MASK |
RT5682_PLL2B_M_BP_MASK | RT5682_PLL2F_M_BP_MASK | 0xf,
+ pll2_ps_val |
(pll2b_code.m_bp ? 1 : 0) << RT5682_PLL2B_M_BP_SFT |
(pll2f_code.m_bp ? 1 : 0) << RT5682_PLL2F_M_BP_SFT |
0xf);
#ifdef CONFIG_COMMON_CLK
#define CLK_PLL2_FIN 48000000
-#define CLK_PLL2_FOUT 24576000
#define CLK_48 48000
+#define CLK_44 44100
static bool rt5682_clk_check(struct rt5682_priv *rt5682)
{
struct rt5682_priv *rt5682 =
container_of(hw, struct rt5682_priv,
dai_clks_hw[RT5682_DAI_WCLK_IDX]);
+ struct snd_soc_component *component = rt5682->component;
+ const char * const clk_name = __clk_get_name(hw->clk);
if (!rt5682_clk_check(rt5682))
return 0;
/*
- * Only accept to set wclk rate to 48kHz temporarily.
+ * Only accept to set wclk rate to 44.1k or 48kHz.
*/
- return CLK_48;
+ if (rt5682->lrck[RT5682_AIF1] != CLK_48 &&
+ rt5682->lrck[RT5682_AIF1] != CLK_44) {
+ dev_warn(component->dev, "%s: clk %s only support %d or %d Hz output\n",
+ __func__, clk_name, CLK_44, CLK_48);
+ return 0;
+ }
+
+ return rt5682->lrck[RT5682_AIF1];
}
static long rt5682_wclk_round_rate(struct clk_hw *hw, unsigned long rate,
struct rt5682_priv *rt5682 =
container_of(hw, struct rt5682_priv,
dai_clks_hw[RT5682_DAI_WCLK_IDX]);
+ struct snd_soc_component *component = rt5682->component;
+ const char * const clk_name = __clk_get_name(hw->clk);
if (!rt5682_clk_check(rt5682))
return -EINVAL;
/*
- * Only accept to set wclk rate to 48kHz temporarily.
+ * Only accept to set wclk rate to 44.1k or 48kHz.
+ * It will force to 48kHz if not both.
*/
- return CLK_48;
+ if (rate != CLK_48 && rate != CLK_44) {
+ dev_warn(component->dev, "%s: clk %s only support %d or %d Hz output\n",
+ __func__, clk_name, CLK_44, CLK_48);
+ rate = CLK_48;
+ }
+
+ return rate;
}
static int rt5682_wclk_set_rate(struct clk_hw *hw, unsigned long rate,
struct clk *parent_clk;
const char * const clk_name = __clk_get_name(hw->clk);
int pre_div;
+ unsigned int clk_pll2_out;
if (!rt5682_clk_check(rt5682))
return -EINVAL;
clk_name, CLK_PLL2_FIN);
/*
- * It's a temporary limitation. Only accept to set wclk rate to 48kHz.
- * It will force wclk to 48kHz even it's not.
- */
- if (rate != CLK_48) {
- dev_warn(component->dev, "clk %s only support %d Hz output\n",
- clk_name, CLK_48);
- rate = CLK_48;
- }
-
- /*
- * To achieve the rate conversion from 48MHz to 48kHz, PLL2 is needed.
+ * To achieve the rate conversion from 48MHz to 44.1k or 48kHz,
+ * PLL2 is needed.
*/
+ clk_pll2_out = rate * 512;
rt5682_set_component_pll(component, RT5682_PLL2, RT5682_PLL2_S_MCLK,
- CLK_PLL2_FIN, CLK_PLL2_FOUT);
+ CLK_PLL2_FIN, clk_pll2_out);
rt5682_set_component_sysclk(component, RT5682_SCLK_S_PLL2, 0,
- CLK_PLL2_FOUT, SND_SOC_CLOCK_IN);
+ clk_pll2_out, SND_SOC_CLOCK_IN);
+
+ rt5682->lrck[RT5682_AIF1] = rate;
pre_div = rl6231_get_clk_info(rt5682->sysclk, rate);
#define RT5682_PLL2F_N_SFT 8
/* PLL2 M/N/K Code Control 2 (0x009e) */
+#define RT5682_PLL2B_SEL_PS_MASK (0x1 << 13)
+#define RT5682_PLL2B_SEL_PS_SFT 13
+#define RT5682_PLL2B_PS_BYP_MASK (0x1 << 12)
+#define RT5682_PLL2B_PS_BYP_SFT 12
#define RT5682_PLL2B_M_BP_MASK (0x1 << 11)
#define RT5682_PLL2B_M_BP_SFT 11
#define RT5682_PLL2F_M_BP_MASK (0x1 << 7)
dev_dbg(component->dev, "Downloading %zu byte stage 2 loader\n", fw->size);
/* Copy to local buffer first as vmalloc causes problems for dma */
- img = kzalloc(fw->size, GFP_KERNEL | GFP_DMA);
+ img = kmemdup(&fw->data[0], fw->size, GFP_KERNEL | GFP_DMA);
if (!img) {
ret = -ENOMEM;
goto abort2;
goto abort1;
}
- memcpy(img, &fw->data[0], fw->size);
-
spi_message_init(&m);
memset(&t, 0, sizeof(t));
t.rx_buf = out;
if (of_property_read_bool(np, "wlf,shared-lrclk"))
pdata->shared_lrclk = true;
+
+ of_property_read_u32_array(np, "wlf,gpio-cfg", pdata->gpio_cfg,
+ ARRAY_SIZE(pdata->gpio_cfg));
+
+ of_property_read_u32_array(np, "wlf,hp-cfg", pdata->hp_cfg,
+ ARRAY_SIZE(pdata->hp_cfg));
}
static int wm8960_i2c_probe(struct i2c_client *i2c,
regmap_update_bits(wm8960->regmap, WM8960_LOUT2, 0x100, 0x100);
regmap_update_bits(wm8960->regmap, WM8960_ROUT2, 0x100, 0x100);
+ /* ADCLRC pin configured as GPIO. */
+ regmap_update_bits(wm8960->regmap, WM8960_IFACE2, 1 << 6,
+ wm8960->pdata.gpio_cfg[0] << 6);
+ regmap_update_bits(wm8960->regmap, WM8960_ADDCTL4, 0xF << 4,
+ wm8960->pdata.gpio_cfg[1] << 4);
+
+ /* Enable headphone jack detect */
+ regmap_update_bits(wm8960->regmap, WM8960_ADDCTL4, 3 << 2,
+ wm8960->pdata.hp_cfg[0] << 2);
+ regmap_update_bits(wm8960->regmap, WM8960_ADDCTL2, 3 << 5,
+ wm8960->pdata.hp_cfg[1] << 5);
+ regmap_update_bits(wm8960->regmap, WM8960_ADDCTL1, 3,
+ wm8960->pdata.hp_cfg[2]);
+
i2c_set_clientdata(i2c, wm8960);
ret = devm_snd_soc_register_component(&i2c->dev,
chan_control_mask = IMG_I2S_IN_CH_CTL_CLK_TRANS_MASK;
ret = pm_runtime_get_sync(i2s->dev);
- if (ret < 0)
+ if (ret < 0) {
+ pm_runtime_put_noidle(i2s->dev);
return ret;
+ }
for (i = 0; i < i2s->active_channels; i++)
img_i2s_in_ch_disable(i2s, i);
}
ret = pm_runtime_get_sync(prl->dev);
- if (ret < 0)
+ if (ret < 0) {
+ pm_runtime_put_noidle(prl->dev);
return ret;
+ }
reg = img_prl_out_readl(prl, IMG_PRL_OUT_CTL);
reg = (reg & ~IMG_PRL_OUT_CTL_EDGE_MASK) | control_set;
help
Say Y if you want to add support for SoC audio on the TM2 board.
+config SND_SOC_SAMSUNG_ARIES_WM8994
+ tristate "SoC I2S Audio support for WM8994 on Aries"
+ depends on SND_SOC_SAMSUNG && MFD_WM8994 && IIO && EXTCON
+ select SND_SOC_BT_SCO
+ select SND_SOC_WM8994
+ select SND_SAMSUNG_I2S
+ help
+ Say Y if you want to add support for SoC audio on Aries boards,
+ which has a WM8994 codec connected to a BT codec, a cellular
+ modem, and the Samsung I2S controller. Jack detection is done
+ via ADC, GPIOs, and an extcon device. Switching between the Mic
+ and TV-Out path is also handled.
+
endif #SND_SOC_SAMSUNG
snd-soc-odroid-objs := odroid.o
snd-soc-arndale-objs := arndale.o
snd-soc-tm2-wm5110-objs := tm2_wm5110.o
+snd-soc-aries-wm8994-objs := aries_wm8994.o
obj-$(CONFIG_SND_SOC_SAMSUNG_JIVE_WM8750) += snd-soc-jive-wm8750.o
obj-$(CONFIG_SND_SOC_SAMSUNG_NEO1973_WM8753) += snd-soc-neo1973-wm8753.o
obj-$(CONFIG_SND_SOC_ODROID) += snd-soc-odroid.o
obj-$(CONFIG_SND_SOC_ARNDALE) += snd-soc-arndale.o
obj-$(CONFIG_SND_SOC_SAMSUNG_TM2_WM5110) += snd-soc-tm2-wm5110.o
+obj-$(CONFIG_SND_SOC_SAMSUNG_ARIES_WM8994) += snd-soc-aries-wm8994.o
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0+
+#include <linux/extcon.h>
+#include <linux/iio/consumer.h>
+#include <linux/iio/iio.h>
+#include <linux/input-event-codes.h>
+#include <linux/mfd/wm8994/registers.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/regulator/consumer.h>
+#include <sound/jack.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include "i2s.h"
+#include "../codecs/wm8994.h"
+
+#define ARIES_MCLK1_FREQ 24000000
+
+struct aries_wm8994_variant {
+ unsigned int modem_dai_fmt;
+ bool has_fm_radio;
+};
+
+struct aries_wm8994_data {
+ struct extcon_dev *usb_extcon;
+ struct regulator *reg_main_micbias;
+ struct regulator *reg_headset_micbias;
+ struct gpio_desc *gpio_headset_detect;
+ struct gpio_desc *gpio_headset_key;
+ struct gpio_desc *gpio_earpath_sel;
+ struct iio_channel *adc;
+ const struct aries_wm8994_variant *variant;
+};
+
+/* USB dock */
+static struct snd_soc_jack aries_dock;
+
+static struct snd_soc_jack_pin dock_pins[] = {
+ {
+ .pin = "LINE",
+ .mask = SND_JACK_LINEOUT,
+ },
+};
+
+static int aries_extcon_notifier(struct notifier_block *this,
+ unsigned long connected, void *_cmd)
+{
+ if (connected)
+ snd_soc_jack_report(&aries_dock, SND_JACK_LINEOUT,
+ SND_JACK_LINEOUT);
+ else
+ snd_soc_jack_report(&aries_dock, 0, SND_JACK_LINEOUT);
+
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block aries_extcon_notifier_block = {
+ .notifier_call = aries_extcon_notifier,
+};
+
+/* Headset jack */
+static struct snd_soc_jack aries_headset;
+
+static struct snd_soc_jack_pin jack_pins[] = {
+ {
+ .pin = "HP",
+ .mask = SND_JACK_HEADPHONE,
+ }, {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+static struct snd_soc_jack_zone headset_zones[] = {
+ {
+ .min_mv = 0,
+ .max_mv = 241,
+ .jack_type = SND_JACK_HEADPHONE,
+ }, {
+ .min_mv = 242,
+ .max_mv = 2980,
+ .jack_type = SND_JACK_HEADSET,
+ }, {
+ .min_mv = 2981,
+ .max_mv = UINT_MAX,
+ .jack_type = SND_JACK_HEADPHONE,
+ },
+};
+
+static irqreturn_t headset_det_irq_thread(int irq, void *data)
+{
+ struct aries_wm8994_data *priv = (struct aries_wm8994_data *) data;
+ int ret = 0;
+ int time_left_ms = 300;
+ int adc;
+
+ while (time_left_ms > 0) {
+ if (!gpiod_get_value(priv->gpio_headset_detect)) {
+ snd_soc_jack_report(&aries_headset, 0,
+ SND_JACK_HEADSET);
+ gpiod_set_value(priv->gpio_earpath_sel, 0);
+ return IRQ_HANDLED;
+ }
+ msleep(20);
+ time_left_ms -= 20;
+ }
+
+ /* Temporarily enable micbias and earpath selector */
+ ret = regulator_enable(priv->reg_headset_micbias);
+ if (ret)
+ pr_err("%s failed to enable micbias: %d", __func__, ret);
+
+ gpiod_set_value(priv->gpio_earpath_sel, 1);
+
+ ret = iio_read_channel_processed(priv->adc, &adc);
+ if (ret < 0) {
+ /* failed to read ADC, so assume headphone */
+ pr_err("%s failed to read ADC, assuming headphones", __func__);
+ snd_soc_jack_report(&aries_headset, SND_JACK_HEADPHONE,
+ SND_JACK_HEADSET);
+ } else {
+ snd_soc_jack_report(&aries_headset,
+ snd_soc_jack_get_type(&aries_headset, adc),
+ SND_JACK_HEADSET);
+ }
+
+ ret = regulator_disable(priv->reg_headset_micbias);
+ if (ret)
+ pr_err("%s failed disable micbias: %d", __func__, ret);
+
+ /* Disable earpath selector when no mic connected */
+ if (!(aries_headset.status & SND_JACK_MICROPHONE))
+ gpiod_set_value(priv->gpio_earpath_sel, 0);
+
+ return IRQ_HANDLED;
+}
+
+static int headset_button_check(void *data)
+{
+ struct aries_wm8994_data *priv = (struct aries_wm8994_data *) data;
+
+ /* Filter out keypresses when 4 pole jack not detected */
+ if (gpiod_get_value_cansleep(priv->gpio_headset_key) &&
+ aries_headset.status & SND_JACK_MICROPHONE)
+ return SND_JACK_BTN_0;
+
+ return 0;
+}
+
+static struct snd_soc_jack_gpio headset_button_gpio[] = {
+ {
+ .name = "Media Button",
+ .report = SND_JACK_BTN_0,
+ .debounce_time = 30,
+ .jack_status_check = headset_button_check,
+ },
+};
+
+static int aries_spk_cfg(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_card *card = w->dapm->card;
+ struct snd_soc_pcm_runtime *rtd;
+ struct snd_soc_component *component;
+ int ret = 0;
+
+ rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]);
+ component = asoc_rtd_to_codec(rtd, 0)->component;
+
+ /**
+ * We have an odd setup - the SPKMODE pin is pulled up so
+ * we only have access to the left side SPK configs,
+ * but SPKOUTR isn't bridged so when playing back in
+ * stereo, we only get the left hand channel. The only
+ * option we're left with is to force the AIF into mono
+ * mode.
+ */
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ ret = snd_soc_component_update_bits(component,
+ WM8994_AIF1_DAC1_FILTERS_1,
+ WM8994_AIF1DAC1_MONO, WM8994_AIF1DAC1_MONO);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ ret = snd_soc_component_update_bits(component,
+ WM8994_AIF1_DAC1_FILTERS_1,
+ WM8994_AIF1DAC1_MONO, 0);
+ break;
+ }
+
+ return ret;
+}
+
+static int aries_main_bias(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_card *card = w->dapm->card;
+ struct aries_wm8994_data *priv = snd_soc_card_get_drvdata(card);
+ int ret = 0;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ ret = regulator_enable(priv->reg_main_micbias);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ ret = regulator_disable(priv->reg_main_micbias);
+ break;
+ }
+
+ return ret;
+}
+
+static int aries_headset_bias(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_card *card = w->dapm->card;
+ struct aries_wm8994_data *priv = snd_soc_card_get_drvdata(card);
+ int ret = 0;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ ret = regulator_enable(priv->reg_headset_micbias);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ ret = regulator_disable(priv->reg_headset_micbias);
+ break;
+ }
+
+ return ret;
+}
+
+static const struct snd_kcontrol_new aries_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Modem In"),
+ SOC_DAPM_PIN_SWITCH("Modem Out"),
+};
+
+static const struct snd_soc_dapm_widget aries_dapm_widgets[] = {
+ SND_SOC_DAPM_HP("HP", NULL),
+
+ SND_SOC_DAPM_SPK("SPK", aries_spk_cfg),
+ SND_SOC_DAPM_SPK("RCV", NULL),
+
+ SND_SOC_DAPM_LINE("LINE", NULL),
+
+ SND_SOC_DAPM_MIC("Main Mic", aries_main_bias),
+ SND_SOC_DAPM_MIC("Headset Mic", aries_headset_bias),
+
+ SND_SOC_DAPM_MIC("Bluetooth Mic", NULL),
+ SND_SOC_DAPM_SPK("Bluetooth SPK", NULL),
+
+ SND_SOC_DAPM_LINE("Modem In", NULL),
+ SND_SOC_DAPM_LINE("Modem Out", NULL),
+
+ /* This must be last as it is conditionally not used */
+ SND_SOC_DAPM_LINE("FM In", NULL),
+};
+
+static int aries_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 = asoc_rtd_to_codec(rtd, 0);
+ unsigned int pll_out;
+ int ret;
+
+ /* AIF1CLK should be >=3MHz for optimal performance */
+ if (params_width(params) == 24)
+ pll_out = params_rate(params) * 384;
+ else if (params_rate(params) == 8000 || params_rate(params) == 11025)
+ pll_out = params_rate(params) * 512;
+ else
+ pll_out = params_rate(params) * 256;
+
+ ret = snd_soc_dai_set_pll(codec_dai, WM8994_FLL1, WM8994_FLL_SRC_MCLK1,
+ ARIES_MCLK1_FREQ, pll_out);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_FLL1,
+ pll_out, SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int aries_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+ int ret;
+
+ /* Switch sysclk to MCLK1 */
+ ret = snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_MCLK1,
+ ARIES_MCLK1_FREQ, SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ return ret;
+
+ /* Stop PLL */
+ ret = snd_soc_dai_set_pll(codec_dai, WM8994_FLL1, WM8994_FLL_SRC_MCLK1,
+ ARIES_MCLK1_FREQ, 0);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+/*
+ * Main DAI operations
+ */
+static struct snd_soc_ops aries_ops = {
+ .hw_params = aries_hw_params,
+ .hw_free = aries_hw_free,
+};
+
+static int aries_baseband_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+ unsigned int pll_out;
+ int ret;
+
+ pll_out = 8000 * 512;
+
+ /* Set the codec FLL */
+ ret = snd_soc_dai_set_pll(codec_dai, WM8994_FLL2, WM8994_FLL_SRC_MCLK1,
+ ARIES_MCLK1_FREQ, pll_out);
+ if (ret < 0)
+ return ret;
+
+ /* Set the codec system clock */
+ ret = snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_FLL2,
+ pll_out, SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int aries_late_probe(struct snd_soc_card *card)
+{
+ struct aries_wm8994_data *priv = snd_soc_card_get_drvdata(card);
+ int ret, irq;
+
+ ret = snd_soc_card_jack_new(card, "Dock", SND_JACK_LINEOUT,
+ &aries_dock, dock_pins, ARRAY_SIZE(dock_pins));
+ if (ret)
+ return ret;
+
+ ret = devm_extcon_register_notifier(card->dev,
+ priv->usb_extcon, EXTCON_JACK_LINE_OUT,
+ &aries_extcon_notifier_block);
+ if (ret)
+ return ret;
+
+ if (extcon_get_state(priv->usb_extcon,
+ EXTCON_JACK_LINE_OUT) > 0)
+ snd_soc_jack_report(&aries_dock, SND_JACK_LINEOUT,
+ SND_JACK_LINEOUT);
+ else
+ snd_soc_jack_report(&aries_dock, 0, SND_JACK_LINEOUT);
+
+ ret = snd_soc_card_jack_new(card, "Headset",
+ SND_JACK_HEADSET | SND_JACK_BTN_0,
+ &aries_headset,
+ jack_pins, ARRAY_SIZE(jack_pins));
+ if (ret)
+ return ret;
+
+ ret = snd_soc_jack_add_zones(&aries_headset, ARRAY_SIZE(headset_zones),
+ headset_zones);
+ if (ret)
+ return ret;
+
+ irq = gpiod_to_irq(priv->gpio_headset_detect);
+ if (irq < 0) {
+ dev_err(card->dev, "Failed to map headset detect gpio to irq");
+ return -EINVAL;
+ }
+
+ ret = devm_request_threaded_irq(card->dev, irq, NULL,
+ headset_det_irq_thread,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
+ IRQF_ONESHOT, "headset_detect", priv);
+ if (ret) {
+ dev_err(card->dev, "Failed to request headset detect irq");
+ return ret;
+ }
+
+ headset_button_gpio[0].data = priv;
+ headset_button_gpio[0].desc = priv->gpio_headset_key;
+
+ snd_jack_set_key(aries_headset.jack, SND_JACK_BTN_0, KEY_MEDIA);
+
+ return snd_soc_jack_add_gpios(&aries_headset,
+ ARRAY_SIZE(headset_button_gpio), headset_button_gpio);
+}
+
+static const struct snd_soc_pcm_stream baseband_params = {
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rate_min = 8000,
+ .rate_max = 8000,
+ .channels_min = 1,
+ .channels_max = 1,
+};
+
+static const struct snd_soc_pcm_stream bluetooth_params = {
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rate_min = 8000,
+ .rate_max = 8000,
+ .channels_min = 1,
+ .channels_max = 2,
+};
+
+static const struct snd_soc_dapm_widget aries_modem_widgets[] = {
+ SND_SOC_DAPM_INPUT("Modem RX"),
+ SND_SOC_DAPM_OUTPUT("Modem TX"),
+};
+
+static const struct snd_soc_dapm_route aries_modem_routes[] = {
+ { "Modem Capture", NULL, "Modem RX" },
+ { "Modem TX", NULL, "Modem Playback" },
+};
+
+static const struct snd_soc_component_driver aries_component = {
+ .name = "aries-audio",
+ .dapm_widgets = aries_modem_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(aries_modem_widgets),
+ .dapm_routes = aries_modem_routes,
+ .num_dapm_routes = ARRAY_SIZE(aries_modem_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+ .non_legacy_dai_naming = 1,
+};
+
+static struct snd_soc_dai_driver aries_ext_dai[] = {
+ {
+ .name = "Voice call",
+ .playback = {
+ .stream_name = "Modem Playback",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rate_min = 8000,
+ .rate_max = 8000,
+ .rates = SNDRV_PCM_RATE_8000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .stream_name = "Modem Capture",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rate_min = 8000,
+ .rate_max = 8000,
+ .rates = SNDRV_PCM_RATE_8000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ },
+};
+
+SND_SOC_DAILINK_DEFS(aif1,
+ DAILINK_COMP_ARRAY(COMP_CPU(SAMSUNG_I2S_DAI)),
+ DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "wm8994-aif1")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(baseband,
+ DAILINK_COMP_ARRAY(COMP_CPU("Voice call")),
+ DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "wm8994-aif2")));
+
+SND_SOC_DAILINK_DEFS(bluetooth,
+ DAILINK_COMP_ARRAY(COMP_CPU("bt-sco-pcm")),
+ DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "wm8994-aif3")));
+
+static struct snd_soc_dai_link aries_dai[] = {
+ {
+ .name = "WM8994 AIF1",
+ .stream_name = "HiFi",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBM_CFM,
+ .ops = &aries_ops,
+ SND_SOC_DAILINK_REG(aif1),
+ },
+ {
+ .name = "WM8994 AIF2",
+ .stream_name = "Baseband",
+ .init = &aries_baseband_init,
+ .params = &baseband_params,
+ .ignore_suspend = 1,
+ SND_SOC_DAILINK_REG(baseband),
+ },
+ {
+ .name = "WM8994 AIF3",
+ .stream_name = "Bluetooth",
+ .params = &bluetooth_params,
+ .ignore_suspend = 1,
+ SND_SOC_DAILINK_REG(bluetooth),
+ },
+};
+
+static struct snd_soc_card aries_card = {
+ .name = "ARIES",
+ .owner = THIS_MODULE,
+ .dai_link = aries_dai,
+ .num_links = ARRAY_SIZE(aries_dai),
+ .controls = aries_controls,
+ .num_controls = ARRAY_SIZE(aries_controls),
+ .dapm_widgets = aries_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(aries_dapm_widgets),
+ .late_probe = aries_late_probe,
+};
+
+static const struct aries_wm8994_variant fascinate4g_variant = {
+ .modem_dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBS_CFS
+ | SND_SOC_DAIFMT_IB_NF,
+ .has_fm_radio = false,
+};
+
+static const struct aries_wm8994_variant aries_variant = {
+ .modem_dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBM_CFM
+ | SND_SOC_DAIFMT_IB_NF,
+ .has_fm_radio = true,
+};
+
+static const struct of_device_id samsung_wm8994_of_match[] = {
+ {
+ .compatible = "samsung,fascinate4g-wm8994",
+ .data = &fascinate4g_variant,
+ },
+ {
+ .compatible = "samsung,aries-wm8994",
+ .data = &aries_variant,
+ },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, samsung_wm8994_of_match);
+
+static int aries_audio_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct device_node *cpu, *codec, *extcon_np;
+ struct device *dev = &pdev->dev;
+ struct snd_soc_card *card = &aries_card;
+ struct aries_wm8994_data *priv;
+ struct snd_soc_dai_link *dai_link;
+ const struct of_device_id *match;
+ int ret, i;
+
+ if (!np)
+ return -EINVAL;
+
+ card->dev = dev;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ snd_soc_card_set_drvdata(card, priv);
+
+ match = of_match_node(samsung_wm8994_of_match, np);
+ priv->variant = match->data;
+
+ /* Remove FM widget if not present */
+ if (!priv->variant->has_fm_radio)
+ card->num_dapm_widgets--;
+
+ priv->reg_main_micbias = devm_regulator_get(dev, "main-micbias");
+ if (IS_ERR(priv->reg_main_micbias)) {
+ dev_err(dev, "Failed to get main micbias regulator\n");
+ return PTR_ERR(priv->reg_main_micbias);
+ }
+
+ priv->reg_headset_micbias = devm_regulator_get(dev, "headset-micbias");
+ if (IS_ERR(priv->reg_headset_micbias)) {
+ dev_err(dev, "Failed to get headset micbias regulator\n");
+ return PTR_ERR(priv->reg_headset_micbias);
+ }
+
+ priv->gpio_earpath_sel = devm_gpiod_get(dev, "earpath-sel",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(priv->gpio_earpath_sel)) {
+ dev_err(dev, "Failed to get earpath selector gpio");
+ return PTR_ERR(priv->gpio_earpath_sel);
+ }
+
+ extcon_np = of_parse_phandle(np, "extcon", 0);
+ priv->usb_extcon = extcon_find_edev_by_node(extcon_np);
+ if (IS_ERR(priv->usb_extcon)) {
+ if (PTR_ERR(priv->usb_extcon) != -EPROBE_DEFER)
+ dev_err(dev, "Failed to get extcon device");
+ return PTR_ERR(priv->usb_extcon);
+ }
+ of_node_put(extcon_np);
+
+ priv->adc = devm_iio_channel_get(dev, "headset-detect");
+ if (IS_ERR(priv->adc)) {
+ if (PTR_ERR(priv->adc) != -EPROBE_DEFER)
+ dev_err(dev, "Failed to get ADC channel");
+ return PTR_ERR(priv->adc);
+ }
+ if (priv->adc->channel->type != IIO_VOLTAGE)
+ return -EINVAL;
+
+ priv->gpio_headset_key = devm_gpiod_get(dev, "headset-key",
+ GPIOD_IN);
+ if (IS_ERR(priv->gpio_headset_key)) {
+ dev_err(dev, "Failed to get headset key gpio");
+ return PTR_ERR(priv->gpio_headset_key);
+ }
+
+ priv->gpio_headset_detect = devm_gpiod_get(dev,
+ "headset-detect", GPIOD_IN);
+ if (IS_ERR(priv->gpio_headset_detect)) {
+ dev_err(dev, "Failed to get headset detect gpio");
+ return PTR_ERR(priv->gpio_headset_detect);
+ }
+
+ /* Update card-name if provided through DT, else use default name */
+ snd_soc_of_parse_card_name(card, "model");
+
+ ret = snd_soc_of_parse_audio_routing(card, "samsung,audio-routing");
+ if (ret < 0) {
+ dev_err(dev, "Audio routing invalid/unspecified\n");
+ return ret;
+ }
+
+ aries_dai[1].dai_fmt = priv->variant->modem_dai_fmt;
+
+ cpu = of_get_child_by_name(dev->of_node, "cpu");
+ if (!cpu)
+ return -EINVAL;
+
+ codec = of_get_child_by_name(dev->of_node, "codec");
+ if (!codec)
+ return -EINVAL;
+
+ for_each_card_prelinks(card, i, dai_link) {
+ dai_link->codecs->of_node = of_parse_phandle(codec,
+ "sound-dai", 0);
+ if (!dai_link->codecs->of_node) {
+ ret = -EINVAL;
+ goto out;
+ }
+ }
+
+ /* Set CPU and platform of_node for main DAI */
+ aries_dai[0].cpus->of_node = of_parse_phandle(cpu,
+ "sound-dai", 0);
+ if (!aries_dai[0].cpus->of_node) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ aries_dai[0].platforms->of_node = aries_dai[0].cpus->of_node;
+
+ /* Set CPU of_node for BT DAI */
+ aries_dai[2].cpus->of_node = of_parse_phandle(cpu,
+ "sound-dai", 1);
+ if (!aries_dai[2].cpus->of_node) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = devm_snd_soc_register_component(dev, &aries_component,
+ aries_ext_dai, ARRAY_SIZE(aries_ext_dai));
+ if (ret < 0) {
+ dev_err(dev, "Failed to register component: %d\n", ret);
+ goto out;
+ }
+
+ ret = devm_snd_soc_register_card(dev, card);
+ if (ret)
+ dev_err(dev, "snd_soc_register_card() failed:%d\n", ret);
+
+out:
+ of_node_put(cpu);
+ of_node_put(codec);
+
+ return ret;
+}
+
+static struct platform_driver aries_audio_driver = {
+ .driver = {
+ .name = "aries-audio-wm8994",
+ .of_match_table = of_match_ptr(samsung_wm8994_of_match),
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = aries_audio_probe,
+};
+
+module_platform_driver(aries_audio_driver);
+
+MODULE_DESCRIPTION("ALSA SoC ARIES WM8994");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:aries-audio-wm8994");
//
// soc-component.c
//
+// Copyright 2009-2011 Wolfson Microelectronics PLC.
// Copyright (C) 2019 Renesas Electronics Corp.
+//
+// Mark Brown <broonie@opensource.wolfsonmicro.com>
// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
//
#include <linux/module.h>
#include <sound/soc.h>
+#define soc_component_ret(dai, ret) _soc_component_ret(dai, __func__, ret)
+static inline int _soc_component_ret(struct snd_soc_component *component,
+ const char *func, int ret)
+{
+ /* Positive/Zero values are not errors */
+ if (ret >= 0)
+ return ret;
+
+ /* Negative values might be errors */
+ switch (ret) {
+ case -EPROBE_DEFER:
+ case -ENOTSUPP:
+ break;
+ default:
+ dev_err(component->dev,
+ "ASoC: error at %s on %s: %d\n",
+ func, component->name, ret);
+ }
+
+ return ret;
+}
+
+int snd_soc_component_initialize(struct snd_soc_component *component,
+ const struct snd_soc_component_driver *driver,
+ struct device *dev, const char *name)
+{
+ INIT_LIST_HEAD(&component->dai_list);
+ INIT_LIST_HEAD(&component->dobj_list);
+ INIT_LIST_HEAD(&component->card_list);
+ mutex_init(&component->io_mutex);
+
+ component->name = name;
+ component->dev = dev;
+ component->driver = driver;
+
+ return 0;
+}
+
+void snd_soc_component_set_aux(struct snd_soc_component *component,
+ struct snd_soc_aux_dev *aux)
+{
+ component->init = (aux) ? aux->init : NULL;
+}
+
+int snd_soc_component_init(struct snd_soc_component *component)
+{
+ int ret = 0;
+
+ if (component->init)
+ ret = component->init(component);
+
+ return soc_component_ret(component, ret);
+}
+
/**
* snd_soc_component_set_sysclk - configure COMPONENT system or master clock.
* @component: COMPONENT
int clk_id, int source, unsigned int freq,
int dir)
{
+ int ret = -ENOTSUPP;
+
if (component->driver->set_sysclk)
- return component->driver->set_sysclk(component, clk_id, source,
+ ret = component->driver->set_sysclk(component, clk_id, source,
freq, dir);
- return -ENOTSUPP;
+ return soc_component_ret(component, ret);
}
EXPORT_SYMBOL_GPL(snd_soc_component_set_sysclk);
int source, unsigned int freq_in,
unsigned int freq_out)
{
+ int ret = -EINVAL;
+
if (component->driver->set_pll)
- return component->driver->set_pll(component, pll_id, source,
+ ret = component->driver->set_pll(component, pll_id, source,
freq_in, freq_out);
- return -EINVAL;
+ return soc_component_ret(component, ret);
}
EXPORT_SYMBOL_GPL(snd_soc_component_set_pll);
int snd_soc_component_stream_event(struct snd_soc_component *component,
int event)
{
+ int ret = 0;
+
if (component->driver->stream_event)
- return component->driver->stream_event(component, event);
+ ret = component->driver->stream_event(component, event);
- return 0;
+ return soc_component_ret(component, ret);
}
int snd_soc_component_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
+ int ret = 0;
+
if (component->driver->set_bias_level)
- return component->driver->set_bias_level(component, level);
+ ret = component->driver->set_bias_level(component, level);
- return 0;
+ return soc_component_ret(component, ret);
}
-int snd_soc_component_enable_pin(struct snd_soc_component *component,
- const char *pin)
+static int soc_component_pin(struct snd_soc_component *component,
+ const char *pin,
+ int (*pin_func)(struct snd_soc_dapm_context *dapm,
+ const char *pin))
{
struct snd_soc_dapm_context *dapm =
snd_soc_component_get_dapm(component);
char *full_name;
int ret;
- if (!component->name_prefix)
- return snd_soc_dapm_enable_pin(dapm, pin);
+ if (!component->name_prefix) {
+ ret = pin_func(dapm, pin);
+ goto end;
+ }
full_name = kasprintf(GFP_KERNEL, "%s %s", component->name_prefix, pin);
- if (!full_name)
- return -ENOMEM;
+ if (!full_name) {
+ ret = -ENOMEM;
+ goto end;
+ }
- ret = snd_soc_dapm_enable_pin(dapm, full_name);
+ ret = pin_func(dapm, full_name);
kfree(full_name);
+end:
+ return soc_component_ret(component, ret);
+}
- return ret;
+int snd_soc_component_enable_pin(struct snd_soc_component *component,
+ const char *pin)
+{
+ return soc_component_pin(component, pin, snd_soc_dapm_enable_pin);
}
EXPORT_SYMBOL_GPL(snd_soc_component_enable_pin);
int snd_soc_component_enable_pin_unlocked(struct snd_soc_component *component,
const char *pin)
{
- struct snd_soc_dapm_context *dapm =
- snd_soc_component_get_dapm(component);
- char *full_name;
- int ret;
-
- if (!component->name_prefix)
- return snd_soc_dapm_enable_pin_unlocked(dapm, pin);
-
- full_name = kasprintf(GFP_KERNEL, "%s %s", component->name_prefix, pin);
- if (!full_name)
- return -ENOMEM;
-
- ret = snd_soc_dapm_enable_pin_unlocked(dapm, full_name);
- kfree(full_name);
-
- return ret;
+ return soc_component_pin(component, pin, snd_soc_dapm_enable_pin_unlocked);
}
EXPORT_SYMBOL_GPL(snd_soc_component_enable_pin_unlocked);
int snd_soc_component_disable_pin(struct snd_soc_component *component,
const char *pin)
{
- struct snd_soc_dapm_context *dapm =
- snd_soc_component_get_dapm(component);
- char *full_name;
- int ret;
-
- if (!component->name_prefix)
- return snd_soc_dapm_disable_pin(dapm, pin);
-
- full_name = kasprintf(GFP_KERNEL, "%s %s", component->name_prefix, pin);
- if (!full_name)
- return -ENOMEM;
-
- ret = snd_soc_dapm_disable_pin(dapm, full_name);
- kfree(full_name);
-
- return ret;
+ return soc_component_pin(component, pin, snd_soc_dapm_disable_pin);
}
EXPORT_SYMBOL_GPL(snd_soc_component_disable_pin);
int snd_soc_component_disable_pin_unlocked(struct snd_soc_component *component,
const char *pin)
{
- struct snd_soc_dapm_context *dapm =
- snd_soc_component_get_dapm(component);
- char *full_name;
- int ret;
-
- if (!component->name_prefix)
- return snd_soc_dapm_disable_pin_unlocked(dapm, pin);
-
- full_name = kasprintf(GFP_KERNEL, "%s %s", component->name_prefix, pin);
- if (!full_name)
- return -ENOMEM;
-
- ret = snd_soc_dapm_disable_pin_unlocked(dapm, full_name);
- kfree(full_name);
-
- return ret;
+ return soc_component_pin(component, pin, snd_soc_dapm_disable_pin_unlocked);
}
EXPORT_SYMBOL_GPL(snd_soc_component_disable_pin_unlocked);
int snd_soc_component_nc_pin(struct snd_soc_component *component,
const char *pin)
{
- struct snd_soc_dapm_context *dapm =
- snd_soc_component_get_dapm(component);
- char *full_name;
- int ret;
-
- if (!component->name_prefix)
- return snd_soc_dapm_nc_pin(dapm, pin);
-
- full_name = kasprintf(GFP_KERNEL, "%s %s", component->name_prefix, pin);
- if (!full_name)
- return -ENOMEM;
-
- ret = snd_soc_dapm_nc_pin(dapm, full_name);
- kfree(full_name);
-
- return ret;
+ return soc_component_pin(component, pin, snd_soc_dapm_nc_pin);
}
EXPORT_SYMBOL_GPL(snd_soc_component_nc_pin);
int snd_soc_component_nc_pin_unlocked(struct snd_soc_component *component,
const char *pin)
{
- struct snd_soc_dapm_context *dapm =
- snd_soc_component_get_dapm(component);
- char *full_name;
- int ret;
-
- if (!component->name_prefix)
- return snd_soc_dapm_nc_pin_unlocked(dapm, pin);
-
- full_name = kasprintf(GFP_KERNEL, "%s %s", component->name_prefix, pin);
- if (!full_name)
- return -ENOMEM;
-
- ret = snd_soc_dapm_nc_pin_unlocked(dapm, full_name);
- kfree(full_name);
-
- return ret;
+ return soc_component_pin(component, pin, snd_soc_dapm_nc_pin_unlocked);
}
EXPORT_SYMBOL_GPL(snd_soc_component_nc_pin_unlocked);
int snd_soc_component_get_pin_status(struct snd_soc_component *component,
const char *pin)
{
- struct snd_soc_dapm_context *dapm =
- snd_soc_component_get_dapm(component);
- char *full_name;
- int ret;
-
- if (!component->name_prefix)
- return snd_soc_dapm_get_pin_status(dapm, pin);
-
- full_name = kasprintf(GFP_KERNEL, "%s %s", component->name_prefix, pin);
- if (!full_name)
- return -ENOMEM;
-
- ret = snd_soc_dapm_get_pin_status(dapm, full_name);
- kfree(full_name);
-
- return ret;
+ return soc_component_pin(component, pin, snd_soc_dapm_get_pin_status);
}
EXPORT_SYMBOL_GPL(snd_soc_component_get_pin_status);
int snd_soc_component_force_enable_pin(struct snd_soc_component *component,
const char *pin)
{
- struct snd_soc_dapm_context *dapm =
- snd_soc_component_get_dapm(component);
- char *full_name;
- int ret;
-
- if (!component->name_prefix)
- return snd_soc_dapm_force_enable_pin(dapm, pin);
-
- full_name = kasprintf(GFP_KERNEL, "%s %s", component->name_prefix, pin);
- if (!full_name)
- return -ENOMEM;
-
- ret = snd_soc_dapm_force_enable_pin(dapm, full_name);
- kfree(full_name);
-
- return ret;
+ return soc_component_pin(component, pin, snd_soc_dapm_force_enable_pin);
}
EXPORT_SYMBOL_GPL(snd_soc_component_force_enable_pin);
struct snd_soc_component *component,
const char *pin)
{
- struct snd_soc_dapm_context *dapm =
- snd_soc_component_get_dapm(component);
- char *full_name;
- int ret;
-
- if (!component->name_prefix)
- return snd_soc_dapm_force_enable_pin_unlocked(dapm, pin);
-
- full_name = kasprintf(GFP_KERNEL, "%s %s", component->name_prefix, pin);
- if (!full_name)
- return -ENOMEM;
-
- ret = snd_soc_dapm_force_enable_pin_unlocked(dapm, full_name);
- kfree(full_name);
-
- return ret;
+ return soc_component_pin(component, pin, snd_soc_dapm_force_enable_pin_unlocked);
}
EXPORT_SYMBOL_GPL(snd_soc_component_force_enable_pin_unlocked);
int snd_soc_component_set_jack(struct snd_soc_component *component,
struct snd_soc_jack *jack, void *data)
{
+ int ret = -ENOTSUPP;
+
if (component->driver->set_jack)
- return component->driver->set_jack(component, jack, data);
+ ret = component->driver->set_jack(component, jack, data);
- return -ENOTSUPP;
+ return soc_component_ret(component, ret);
}
EXPORT_SYMBOL_GPL(snd_soc_component_set_jack);
int snd_soc_component_module_get(struct snd_soc_component *component,
int upon_open)
{
+ int ret = 0;
+
if (component->driver->module_get_upon_open == !!upon_open &&
!try_module_get(component->dev->driver->owner))
- return -ENODEV;
+ ret = -ENODEV;
- return 0;
+ return soc_component_ret(component, ret);
}
void snd_soc_component_module_put(struct snd_soc_component *component,
int snd_soc_component_open(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
+ int ret = 0;
+
if (component->driver->open)
- return component->driver->open(component, substream);
- return 0;
+ ret = component->driver->open(component, substream);
+
+ return soc_component_ret(component, ret);
}
int snd_soc_component_close(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
- if (component->driver->close)
- return component->driver->close(component, substream);
- return 0;
-}
-
-int snd_soc_component_prepare(struct snd_soc_component *component,
- struct snd_pcm_substream *substream)
-{
- if (component->driver->prepare)
- return component->driver->prepare(component, substream);
- return 0;
-}
+ int ret = 0;
-int snd_soc_component_hw_params(struct snd_soc_component *component,
- struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- if (component->driver->hw_params)
- return component->driver->hw_params(component,
- substream, params);
- return 0;
-}
-
-int snd_soc_component_hw_free(struct snd_soc_component *component,
- struct snd_pcm_substream *substream)
-{
- if (component->driver->hw_free)
- return component->driver->hw_free(component, substream);
- return 0;
-}
+ if (component->driver->close)
+ ret = component->driver->close(component, substream);
-int snd_soc_component_trigger(struct snd_soc_component *component,
- struct snd_pcm_substream *substream,
- int cmd)
-{
- if (component->driver->trigger)
- return component->driver->trigger(component, substream, cmd);
- return 0;
+ return soc_component_ret(component, ret);
}
void snd_soc_component_suspend(struct snd_soc_component *component)
int snd_soc_component_probe(struct snd_soc_component *component)
{
+ int ret = 0;
+
if (component->driver->probe)
- return component->driver->probe(component);
+ ret = component->driver->probe(component);
- return 0;
+ return soc_component_ret(component, ret);
}
void snd_soc_component_remove(struct snd_soc_component *component)
int snd_soc_component_of_xlate_dai_id(struct snd_soc_component *component,
struct device_node *ep)
{
+ int ret = -ENOTSUPP;
+
if (component->driver->of_xlate_dai_id)
- return component->driver->of_xlate_dai_id(component, ep);
+ ret = component->driver->of_xlate_dai_id(component, ep);
- return -ENOTSUPP;
+ return soc_component_ret(component, ret);
}
int snd_soc_component_of_xlate_dai_name(struct snd_soc_component *component,
struct of_phandle_args *args,
const char **dai_name)
{
+ int ret = -ENOTSUPP;
+
if (component->driver->of_xlate_dai_name)
- return component->driver->of_xlate_dai_name(component,
- args, dai_name);
- return -ENOTSUPP;
+ ret = component->driver->of_xlate_dai_name(component,
+ args, dai_name);
+
+ return soc_component_ret(component, ret);
+}
+
+void snd_soc_component_setup_regmap(struct snd_soc_component *component)
+{
+ int val_bytes = regmap_get_val_bytes(component->regmap);
+
+ /* Errors are legitimate for non-integer byte multiples */
+ if (val_bytes > 0)
+ component->val_bytes = val_bytes;
+}
+
+#ifdef CONFIG_REGMAP
+
+/**
+ * snd_soc_component_init_regmap() - Initialize regmap instance for the
+ * component
+ * @component: The component for which to initialize the regmap instance
+ * @regmap: The regmap instance that should be used by the component
+ *
+ * This function allows deferred assignment of the regmap instance that is
+ * associated with the component. Only use this if the regmap instance is not
+ * yet ready when the component is registered. The function must also be called
+ * before the first IO attempt of the component.
+ */
+void snd_soc_component_init_regmap(struct snd_soc_component *component,
+ struct regmap *regmap)
+{
+ component->regmap = regmap;
+ snd_soc_component_setup_regmap(component);
+}
+EXPORT_SYMBOL_GPL(snd_soc_component_init_regmap);
+
+/**
+ * snd_soc_component_exit_regmap() - De-initialize regmap instance for the
+ * component
+ * @component: The component for which to de-initialize the regmap instance
+ *
+ * Calls regmap_exit() on the regmap instance associated to the component and
+ * removes the regmap instance from the component.
+ *
+ * This function should only be used if snd_soc_component_init_regmap() was used
+ * to initialize the regmap instance.
+ */
+void snd_soc_component_exit_regmap(struct snd_soc_component *component)
+{
+ regmap_exit(component->regmap);
+ component->regmap = NULL;
+}
+EXPORT_SYMBOL_GPL(snd_soc_component_exit_regmap);
+
+#endif
+
+/**
+ * snd_soc_component_read() - Read register value
+ * @component: Component to read from
+ * @reg: Register to read
+ * @val: Pointer to where the read value is stored
+ *
+ * Return: 0 on success, a negative error code otherwise.
+ */
+int snd_soc_component_read(struct snd_soc_component *component,
+ unsigned int reg, unsigned int *val)
+{
+ int ret;
+
+ if (component->regmap)
+ ret = regmap_read(component->regmap, reg, val);
+ else if (component->driver->read) {
+ *val = component->driver->read(component, reg);
+ ret = 0;
+ }
+ else
+ ret = -EIO;
+
+ return soc_component_ret(component, ret);
+}
+EXPORT_SYMBOL_GPL(snd_soc_component_read);
+
+unsigned int snd_soc_component_read32(struct snd_soc_component *component,
+ unsigned int reg)
+{
+ unsigned int val;
+ int ret;
+
+ ret = snd_soc_component_read(component, reg, &val);
+ if (ret < 0)
+ return soc_component_ret(component, -1);
+
+ return val;
+}
+EXPORT_SYMBOL_GPL(snd_soc_component_read32);
+
+/**
+ * snd_soc_component_write() - Write register value
+ * @component: Component to write to
+ * @reg: Register to write
+ * @val: Value to write to the register
+ *
+ * Return: 0 on success, a negative error code otherwise.
+ */
+int snd_soc_component_write(struct snd_soc_component *component,
+ unsigned int reg, unsigned int val)
+{
+ int ret = -EIO;
+
+ if (component->regmap)
+ ret = regmap_write(component->regmap, reg, val);
+ else if (component->driver->write)
+ ret = component->driver->write(component, reg, val);
+
+ return soc_component_ret(component, ret);
+}
+EXPORT_SYMBOL_GPL(snd_soc_component_write);
+
+static int snd_soc_component_update_bits_legacy(
+ struct snd_soc_component *component, unsigned int reg,
+ unsigned int mask, unsigned int val, bool *change)
+{
+ unsigned int old, new;
+ int ret;
+
+ mutex_lock(&component->io_mutex);
+
+ ret = snd_soc_component_read(component, reg, &old);
+ if (ret < 0)
+ goto out_unlock;
+
+ new = (old & ~mask) | (val & mask);
+ *change = old != new;
+ if (*change)
+ ret = snd_soc_component_write(component, reg, new);
+out_unlock:
+ mutex_unlock(&component->io_mutex);
+
+ return soc_component_ret(component, ret);
+}
+
+/**
+ * snd_soc_component_update_bits() - Perform read/modify/write cycle
+ * @component: Component to update
+ * @reg: Register to update
+ * @mask: Mask that specifies which bits to update
+ * @val: New value for the bits specified by mask
+ *
+ * Return: 1 if the operation was successful and the value of the register
+ * changed, 0 if the operation was successful, but the value did not change.
+ * Returns a negative error code otherwise.
+ */
+int snd_soc_component_update_bits(struct snd_soc_component *component,
+ unsigned int reg, unsigned int mask, unsigned int val)
+{
+ bool change;
+ int ret;
+
+ if (component->regmap)
+ ret = regmap_update_bits_check(component->regmap, reg, mask,
+ val, &change);
+ else
+ ret = snd_soc_component_update_bits_legacy(component, reg,
+ mask, val, &change);
+
+ if (ret < 0)
+ return soc_component_ret(component, ret);
+ return change;
}
+EXPORT_SYMBOL_GPL(snd_soc_component_update_bits);
+
+/**
+ * snd_soc_component_update_bits_async() - Perform asynchronous
+ * read/modify/write cycle
+ * @component: Component to update
+ * @reg: Register to update
+ * @mask: Mask that specifies which bits to update
+ * @val: New value for the bits specified by mask
+ *
+ * This function is similar to snd_soc_component_update_bits(), but the update
+ * operation is scheduled asynchronously. This means it may not be completed
+ * when the function returns. To make sure that all scheduled updates have been
+ * completed snd_soc_component_async_complete() must be called.
+ *
+ * Return: 1 if the operation was successful and the value of the register
+ * changed, 0 if the operation was successful, but the value did not change.
+ * Returns a negative error code otherwise.
+ */
+int snd_soc_component_update_bits_async(struct snd_soc_component *component,
+ unsigned int reg, unsigned int mask, unsigned int val)
+{
+ bool change;
+ int ret;
+
+ if (component->regmap)
+ ret = regmap_update_bits_check_async(component->regmap, reg,
+ mask, val, &change);
+ else
+ ret = snd_soc_component_update_bits_legacy(component, reg,
+ mask, val, &change);
+
+ if (ret < 0)
+ return soc_component_ret(component, ret);
+ return change;
+}
+EXPORT_SYMBOL_GPL(snd_soc_component_update_bits_async);
+
+/**
+ * snd_soc_component_async_complete() - Ensure asynchronous I/O has completed
+ * @component: Component for which to wait
+ *
+ * This function blocks until all asynchronous I/O which has previously been
+ * scheduled using snd_soc_component_update_bits_async() has completed.
+ */
+void snd_soc_component_async_complete(struct snd_soc_component *component)
+{
+ if (component->regmap)
+ regmap_async_complete(component->regmap);
+}
+EXPORT_SYMBOL_GPL(snd_soc_component_async_complete);
+
+/**
+ * snd_soc_component_test_bits - Test register for change
+ * @component: component
+ * @reg: Register to test
+ * @mask: Mask that specifies which bits to test
+ * @value: Value to test against
+ *
+ * Tests a register with a new value and checks if the new value is
+ * different from the old value.
+ *
+ * Return: 1 for change, otherwise 0.
+ */
+int snd_soc_component_test_bits(struct snd_soc_component *component,
+ unsigned int reg, unsigned int mask, unsigned int value)
+{
+ unsigned int old, new;
+ int ret;
+
+ ret = snd_soc_component_read(component, reg, &old);
+ if (ret < 0)
+ return soc_component_ret(component, ret);
+ new = (old & ~mask) | value;
+ return old != new;
+}
+EXPORT_SYMBOL_GPL(snd_soc_component_test_bits);
int snd_soc_pcm_component_pointer(struct snd_pcm_substream *substream)
{
/* FIXME: use 1st ioctl */
for_each_rtd_components(rtd, i, component)
if (component->driver->ioctl)
- return component->driver->ioctl(component, substream,
- cmd, arg);
+ return soc_component_ret(
+ component,
+ component->driver->ioctl(component,
+ substream, cmd, arg));
return snd_pcm_lib_ioctl(substream, cmd, arg);
}
ret = component->driver->sync_stop(component,
substream);
if (ret < 0)
- return ret;
+ soc_component_ret(component, ret);
}
}
/* FIXME. it returns 1st copy now */
for_each_rtd_components(rtd, i, component)
if (component->driver->copy_user)
- return component->driver->copy_user(
- component, substream, channel, pos, buf, bytes);
+ return soc_component_ret(
+ component,
+ component->driver->copy_user(
+ component, substream, channel,
+ pos, buf, bytes));
return -EINVAL;
}
/* FIXME. it returns 1st mmap now */
for_each_rtd_components(rtd, i, component)
if (component->driver->mmap)
- return component->driver->mmap(component,
- substream, vma);
+ soc_component_ret(
+ component,
+ component->driver->mmap(component,
+ substream, vma));
return -EINVAL;
}
if (component->driver->pcm_construct) {
ret = component->driver->pcm_construct(component, rtd);
if (ret < 0)
- return ret;
+ soc_component_ret(component, ret);
}
}
if (component->driver->pcm_destruct)
component->driver->pcm_destruct(component, rtd->pcm);
}
+
+int snd_soc_pcm_component_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_component *component;
+ int i, ret;
+
+ for_each_rtd_components(rtd, i, component) {
+ if (component->driver->prepare) {
+ ret = component->driver->prepare(component, substream);
+ if (ret < 0)
+ return soc_component_ret(component, ret);
+ }
+ }
+
+ return 0;
+}
+
+int snd_soc_pcm_component_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_component **last)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_component *component;
+ int i, ret;
+
+ for_each_rtd_components(rtd, i, component) {
+ if (component->driver->hw_params) {
+ ret = component->driver->hw_params(component,
+ substream, params);
+ if (ret < 0) {
+ *last = component;
+ return soc_component_ret(component, ret);
+ }
+ }
+ }
+
+ *last = NULL;
+ return 0;
+}
+
+void snd_soc_pcm_component_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_component *last)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_component *component;
+ int i, ret;
+
+ for_each_rtd_components(rtd, i, component) {
+ if (component == last)
+ break;
+
+ if (component->driver->hw_free) {
+ ret = component->driver->hw_free(component, substream);
+ if (ret < 0)
+ soc_component_ret(component, ret);
+ }
+ }
+}
+
+int snd_soc_pcm_component_trigger(struct snd_pcm_substream *substream,
+ int cmd)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_component *component;
+ int i, ret;
+
+ for_each_rtd_components(rtd, i, component) {
+ if (component->driver->trigger) {
+ ret = component->driver->trigger(component, substream, cmd);
+ if (ret < 0)
+ return soc_component_ret(component, ret);
+ }
+ }
+
+ return 0;
+}
component->name);
probed = 1;
- /* machine specific init */
- if (component->init) {
- ret = component->init(component);
- if (ret < 0) {
- dev_err(component->dev,
- "Failed to do machine specific init %d\n", ret);
- goto err_probe;
- }
- }
+ /*
+ * machine specific init
+ * see
+ * snd_soc_component_set_aux()
+ */
+ ret = snd_soc_component_init(component);
+ if (ret < 0)
+ goto err_probe;
ret = snd_soc_add_component_controls(component,
component->driver->controls,
struct snd_soc_component *component, *_component;
for_each_card_auxs_safe(card, component, _component) {
- component->init = NULL;
+ /* for snd_soc_component_init() */
+ snd_soc_component_set_aux(component, NULL);
list_del(&component->card_aux_list);
}
}
if (!component)
return -EPROBE_DEFER;
- component->init = aux->init;
+ /* for snd_soc_component_init() */
+ snd_soc_component_set_aux(component, aux);
/* see for_each_card_auxs */
list_add(&component->card_aux_list, &card->aux_comp_list);
}
return ret;
}
-static int snd_soc_component_initialize(struct snd_soc_component *component,
- const struct snd_soc_component_driver *driver, struct device *dev)
-{
- INIT_LIST_HEAD(&component->dai_list);
- INIT_LIST_HEAD(&component->dobj_list);
- INIT_LIST_HEAD(&component->card_list);
- mutex_init(&component->io_mutex);
-
- component->name = fmt_single_name(dev, &component->id);
- if (!component->name) {
- dev_err(dev, "ASoC: Failed to allocate name\n");
- return -ENOMEM;
- }
-
- component->dev = dev;
- component->driver = driver;
-
- return 0;
-}
-
-static void snd_soc_component_setup_regmap(struct snd_soc_component *component)
-{
- int val_bytes = regmap_get_val_bytes(component->regmap);
-
- /* Errors are legitimate for non-integer byte multiples */
- if (val_bytes > 0)
- component->val_bytes = val_bytes;
-}
-
-#ifdef CONFIG_REGMAP
-
-/**
- * snd_soc_component_init_regmap() - Initialize regmap instance for the
- * component
- * @component: The component for which to initialize the regmap instance
- * @regmap: The regmap instance that should be used by the component
- *
- * This function allows deferred assignment of the regmap instance that is
- * associated with the component. Only use this if the regmap instance is not
- * yet ready when the component is registered. The function must also be called
- * before the first IO attempt of the component.
- */
-void snd_soc_component_init_regmap(struct snd_soc_component *component,
- struct regmap *regmap)
-{
- component->regmap = regmap;
- snd_soc_component_setup_regmap(component);
-}
-EXPORT_SYMBOL_GPL(snd_soc_component_init_regmap);
-
-/**
- * snd_soc_component_exit_regmap() - De-initialize regmap instance for the
- * component
- * @component: The component for which to de-initialize the regmap instance
- *
- * Calls regmap_exit() on the regmap instance associated to the component and
- * removes the regmap instance from the component.
- *
- * This function should only be used if snd_soc_component_init_regmap() was used
- * to initialize the regmap instance.
- */
-void snd_soc_component_exit_regmap(struct snd_soc_component *component)
-{
- regmap_exit(component->regmap);
- component->regmap = NULL;
-}
-EXPORT_SYMBOL_GPL(snd_soc_component_exit_regmap);
-
-#endif
-
#define ENDIANNESS_MAP(name) \
(SNDRV_PCM_FMTBIT_##name##LE | SNDRV_PCM_FMTBIT_##name##BE)
static u64 endianness_format_map[] = {
struct snd_soc_dai_driver *dai_drv,
int num_dai)
{
+ const char *name = fmt_single_name(dev, &component->id);
int ret;
int i;
+ if (!name) {
+ dev_err(dev, "ASoC: Failed to allocate name\n");
+ return -ENOMEM;
+ }
+
mutex_lock(&client_mutex);
- ret = snd_soc_component_initialize(component, component_driver, dev);
+ ret = snd_soc_component_initialize(component, component_driver,
+ dev, name);
if (ret)
goto err_free;
+++ /dev/null
-// SPDX-License-Identifier: GPL-2.0+
-//
-// soc-io.c -- ASoC register I/O helpers
-//
-// Copyright 2009-2011 Wolfson Microelectronics PLC.
-//
-// Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
-
-#include <linux/i2c.h>
-#include <linux/spi/spi.h>
-#include <linux/regmap.h>
-#include <linux/export.h>
-#include <sound/soc.h>
-
-/**
- * snd_soc_component_read() - Read register value
- * @component: Component to read from
- * @reg: Register to read
- * @val: Pointer to where the read value is stored
- *
- * Return: 0 on success, a negative error code otherwise.
- */
-int snd_soc_component_read(struct snd_soc_component *component,
- unsigned int reg, unsigned int *val)
-{
- int ret;
-
- if (component->regmap)
- ret = regmap_read(component->regmap, reg, val);
- else if (component->driver->read) {
- *val = component->driver->read(component, reg);
- ret = 0;
- }
- else
- ret = -EIO;
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(snd_soc_component_read);
-
-unsigned int snd_soc_component_read32(struct snd_soc_component *component,
- unsigned int reg)
-{
- unsigned int val;
- int ret;
-
- ret = snd_soc_component_read(component, reg, &val);
- if (ret < 0)
- return -1;
-
- return val;
-}
-EXPORT_SYMBOL_GPL(snd_soc_component_read32);
-
-/**
- * snd_soc_component_write() - Write register value
- * @component: Component to write to
- * @reg: Register to write
- * @val: Value to write to the register
- *
- * Return: 0 on success, a negative error code otherwise.
- */
-int snd_soc_component_write(struct snd_soc_component *component,
- unsigned int reg, unsigned int val)
-{
- if (component->regmap)
- return regmap_write(component->regmap, reg, val);
- else if (component->driver->write)
- return component->driver->write(component, reg, val);
- else
- return -EIO;
-}
-EXPORT_SYMBOL_GPL(snd_soc_component_write);
-
-static int snd_soc_component_update_bits_legacy(
- struct snd_soc_component *component, unsigned int reg,
- unsigned int mask, unsigned int val, bool *change)
-{
- unsigned int old, new;
- int ret;
-
- mutex_lock(&component->io_mutex);
-
- ret = snd_soc_component_read(component, reg, &old);
- if (ret < 0)
- goto out_unlock;
-
- new = (old & ~mask) | (val & mask);
- *change = old != new;
- if (*change)
- ret = snd_soc_component_write(component, reg, new);
-out_unlock:
- mutex_unlock(&component->io_mutex);
-
- return ret;
-}
-
-/**
- * snd_soc_component_update_bits() - Perform read/modify/write cycle
- * @component: Component to update
- * @reg: Register to update
- * @mask: Mask that specifies which bits to update
- * @val: New value for the bits specified by mask
- *
- * Return: 1 if the operation was successful and the value of the register
- * changed, 0 if the operation was successful, but the value did not change.
- * Returns a negative error code otherwise.
- */
-int snd_soc_component_update_bits(struct snd_soc_component *component,
- unsigned int reg, unsigned int mask, unsigned int val)
-{
- bool change;
- int ret;
-
- if (component->regmap)
- ret = regmap_update_bits_check(component->regmap, reg, mask,
- val, &change);
- else
- ret = snd_soc_component_update_bits_legacy(component, reg,
- mask, val, &change);
-
- if (ret < 0)
- return ret;
- return change;
-}
-EXPORT_SYMBOL_GPL(snd_soc_component_update_bits);
-
-/**
- * snd_soc_component_update_bits_async() - Perform asynchronous
- * read/modify/write cycle
- * @component: Component to update
- * @reg: Register to update
- * @mask: Mask that specifies which bits to update
- * @val: New value for the bits specified by mask
- *
- * This function is similar to snd_soc_component_update_bits(), but the update
- * operation is scheduled asynchronously. This means it may not be completed
- * when the function returns. To make sure that all scheduled updates have been
- * completed snd_soc_component_async_complete() must be called.
- *
- * Return: 1 if the operation was successful and the value of the register
- * changed, 0 if the operation was successful, but the value did not change.
- * Returns a negative error code otherwise.
- */
-int snd_soc_component_update_bits_async(struct snd_soc_component *component,
- unsigned int reg, unsigned int mask, unsigned int val)
-{
- bool change;
- int ret;
-
- if (component->regmap)
- ret = regmap_update_bits_check_async(component->regmap, reg,
- mask, val, &change);
- else
- ret = snd_soc_component_update_bits_legacy(component, reg,
- mask, val, &change);
-
- if (ret < 0)
- return ret;
- return change;
-}
-EXPORT_SYMBOL_GPL(snd_soc_component_update_bits_async);
-
-/**
- * snd_soc_component_async_complete() - Ensure asynchronous I/O has completed
- * @component: Component for which to wait
- *
- * This function blocks until all asynchronous I/O which has previously been
- * scheduled using snd_soc_component_update_bits_async() has completed.
- */
-void snd_soc_component_async_complete(struct snd_soc_component *component)
-{
- if (component->regmap)
- regmap_async_complete(component->regmap);
-}
-EXPORT_SYMBOL_GPL(snd_soc_component_async_complete);
-
-/**
- * snd_soc_component_test_bits - Test register for change
- * @component: component
- * @reg: Register to test
- * @mask: Mask that specifies which bits to test
- * @value: Value to test against
- *
- * Tests a register with a new value and checks if the new value is
- * different from the old value.
- *
- * Return: 1 for change, otherwise 0.
- */
-int snd_soc_component_test_bits(struct snd_soc_component *component,
- unsigned int reg, unsigned int mask, unsigned int value)
-{
- unsigned int old, new;
- int ret;
-
- ret = snd_soc_component_read(component, reg, &old);
- if (ret < 0)
- return ret;
- new = (old & ~mask) | value;
- return old != new;
-}
-EXPORT_SYMBOL_GPL(snd_soc_component_test_bits);
static int soc_pcm_prepare(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_component *component;
struct snd_soc_dai *dai;
int i, ret = 0;
if (ret < 0)
goto out;
- for_each_rtd_components(rtd, i, component) {
- ret = snd_soc_component_prepare(component, substream);
- if (ret < 0) {
- dev_err(component->dev,
- "ASoC: platform prepare error: %d\n", ret);
- goto out;
- }
- }
+ ret = snd_soc_pcm_component_prepare(substream);
+ if (ret < 0)
+ goto out;
ret = snd_soc_pcm_dai_prepare(substream);
if (ret < 0) {
interval->max = channels;
}
-static int soc_pcm_components_hw_free(struct snd_pcm_substream *substream,
- struct snd_soc_component *last)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_component *component;
- int i, r, ret = 0;
-
- for_each_rtd_components(rtd, i, component) {
- if (component == last)
- break;
-
- r = snd_soc_component_hw_free(component, substream);
- if (r < 0)
- ret = r; /* use last ret */
- }
-
- return ret;
-}
-
/*
* Called by ALSA when the hardware params are set by application. This
* function can also be called multiple times and can allocate buffers
snd_soc_dapm_update_dai(substream, params, cpu_dai);
}
- for_each_rtd_components(rtd, i, component) {
- ret = snd_soc_component_hw_params(component, substream, params);
- if (ret < 0) {
- dev_err(component->dev,
- "ASoC: %s hw params failed: %d\n",
- component->name, ret);
- goto component_err;
- }
- }
- component = NULL;
+ ret = snd_soc_pcm_component_hw_params(substream, params, &component);
+ if (ret < 0)
+ goto component_err;
out:
mutex_unlock(&rtd->card->pcm_mutex);
return ret;
component_err:
- soc_pcm_components_hw_free(substream, component);
+ snd_soc_pcm_component_hw_free(substream, component);
i = rtd->num_cpus;
snd_soc_link_hw_free(substream);
/* free any component resources */
- soc_pcm_components_hw_free(substream, NULL);
+ snd_soc_pcm_component_hw_free(substream, NULL);
/* now free hw params for the DAIs */
for_each_rtd_dais(rtd, i, dai) {
return 0;
}
-static int soc_pcm_trigger_start(struct snd_pcm_substream *substream, int cmd)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_component *component;
- int i, ret;
-
- ret = snd_soc_link_trigger(substream, cmd);
- if (ret < 0)
- return ret;
-
- for_each_rtd_components(rtd, i, component) {
- ret = snd_soc_component_trigger(component, substream, cmd);
- if (ret < 0)
- return ret;
- }
-
- return snd_soc_pcm_dai_trigger(substream, cmd);
-}
-
-static int soc_pcm_trigger_stop(struct snd_pcm_substream *substream, int cmd)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_component *component;
- int i, ret;
-
- ret = snd_soc_pcm_dai_trigger(substream, cmd);
- if (ret < 0)
- return ret;
-
- for_each_rtd_components(rtd, i, component) {
- ret = snd_soc_component_trigger(component, substream, cmd);
- if (ret < 0)
- return ret;
- }
-
- ret = snd_soc_link_trigger(substream, cmd);
- if (ret < 0)
- return ret;
-
- return 0;
-}
-
static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
{
- int ret;
+ int ret = -EINVAL;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- ret = soc_pcm_trigger_start(substream, cmd);
+ ret = snd_soc_link_trigger(substream, cmd);
+ if (ret < 0)
+ break;
+
+ ret = snd_soc_pcm_component_trigger(substream, cmd);
+ if (ret < 0)
+ break;
+
+ ret = snd_soc_pcm_dai_trigger(substream, cmd);
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- ret = soc_pcm_trigger_stop(substream, cmd);
+ ret = snd_soc_pcm_dai_trigger(substream, cmd);
+ if (ret < 0)
+ break;
+
+ ret = snd_soc_pcm_component_trigger(substream, cmd);
+ if (ret < 0)
+ break;
+
+ ret = snd_soc_link_trigger(substream, cmd);
break;
- default:
- return -EINVAL;
}
return ret;