Merge series "ASoC: max98357a: support MAX98360A in OF" from Tzung-Bi Shih <tzungbi...
authorMark Brown <broonie@kernel.org>
Mon, 15 Jun 2020 19:58:44 +0000 (20:58 +0100)
committerMark Brown <broonie@kernel.org>
Mon, 15 Jun 2020 19:58:44 +0000 (20:58 +0100)
Commit 1a0f2433d738 ("ASoC: max98357a: Add ACPI HID MAX98360A") supports
MAX98360A in ACPI world.  This series supports MAX98360A in OF world.

Tzung-Bi Shih (2):
  ASoC: max98357a: add compatible string for MAX98360A
  ASoC: dt-bindings: add compatible string for MAX98360A

 .../devicetree/bindings/sound/max98357a.txt          | 12 +++++++++---
 sound/soc/codecs/max98357a.c                         |  1 +
 2 files changed, 10 insertions(+), 3 deletions(-)

--
2.27.0.278.ge193c7cf3a9-goog

25 files changed:
Documentation/devicetree/bindings/sound/maxim,max98390.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/sound/mt6358.txt
Documentation/devicetree/bindings/sound/samsung,aries-wm8994.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/sound/wm8960.txt
Documentation/devicetree/bindings/sound/wm8994.txt
include/sound/soc-component.h
include/sound/wm8960.h
sound/soc/Makefile
sound/soc/amd/acp3x-rt5682-max9836.c
sound/soc/codecs/max98390.c
sound/soc/codecs/mt6358.c
sound/soc/codecs/rl6231.c
sound/soc/codecs/rt5682.c
sound/soc/codecs/rt5682.h
sound/soc/codecs/wm0010.c
sound/soc/codecs/wm8960.c
sound/soc/img/img-i2s-in.c
sound/soc/img/img-parallel-out.c
sound/soc/samsung/Kconfig
sound/soc/samsung/Makefile
sound/soc/samsung/aries_wm8994.c [new file with mode: 0644]
sound/soc/soc-component.c
sound/soc/soc-core.c
sound/soc/soc-io.c [deleted file]
sound/soc/soc-pcm.c

diff --git a/Documentation/devicetree/bindings/sound/maxim,max98390.yaml b/Documentation/devicetree/bindings/sound/maxim,max98390.yaml
new file mode 100644 (file)
index 0000000..e5ac352
--- /dev/null
@@ -0,0 +1,51 @@
+# 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>;
+      };
+    };
index 5465730..59a73ff 100644 (file)
@@ -10,9 +10,15 @@ Required properties:
 - 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>;
 };
diff --git a/Documentation/devicetree/bindings/sound/samsung,aries-wm8994.yaml b/Documentation/devicetree/bindings/sound/samsung,aries-wm8994.yaml
new file mode 100644 (file)
index 0000000..902a0b6
--- /dev/null
@@ -0,0 +1,147 @@
+# 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>;
+        };
+    };
+
index 6d29ac3..85d3b28 100644 (file)
@@ -21,6 +21,17 @@ Optional properties:
        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 {
index 367b58c..8fa9475 100644 (file)
@@ -68,6 +68,29 @@ Optional properties:
   - 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 {
index 5663891..4a4bb72 100644 (file)
@@ -2,7 +2,8 @@
  *
  * 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
@@ -324,6 +325,13 @@ static inline int snd_soc_component_cache_sync(
        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);
@@ -359,6 +367,7 @@ int snd_soc_component_stream_event(struct snd_soc_component *component,
 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);
@@ -421,16 +430,6 @@ int snd_soc_component_open(struct snd_soc_component *component,
                           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);
@@ -455,5 +454,13 @@ int snd_soc_pcm_component_mmap(struct snd_pcm_substream *substream,
                               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 */
index d22e848..275fd5b 100644 (file)
@@ -16,6 +16,23 @@ struct wm8960_data {
        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
index 7f17475..ddbac3a 100644 (file)
@@ -1,6 +1,6 @@
 # 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),)
index e499c00..f745b42 100644 (file)
@@ -188,25 +188,27 @@ static int acp3x_ec_dmic0_startup(struct snd_pcm_substream *substream)
 
        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)
@@ -229,11 +231,6 @@ static const struct snd_soc_ops acp3x_ec_cap0_ops = {
        .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,
@@ -279,21 +276,26 @@ static struct snd_soc_dai_link acp3x_dai_5682_98357[] = {
                .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[] = {
@@ -301,6 +303,8 @@ 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[] = {
index e6613b5..b345e62 100644 (file)
@@ -842,6 +842,20 @@ static int max98390_dsm_calibrate(struct snd_soc_component *component)
        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 =
@@ -853,18 +867,10 @@ static int max98390_probe(struct snd_soc_component *component)
        /* 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);
index 1b830ea..1f39d59 100644 (file)
@@ -95,6 +95,8 @@ struct mt6358_priv {
        struct regulator *avdd_reg;
 
        int wov_enabled;
+
+       unsigned int dmic_one_wire_mode;
 };
 
 int mt6358_set_mtkaif_protocol(struct snd_soc_component *cmpnt,
@@ -1831,7 +1833,10 @@ static int mt6358_dmic_enable(struct mt6358_priv *priv)
        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);
@@ -2426,6 +2431,20 @@ static const struct snd_soc_component_driver mt6358_soc_component_driver = {
        .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;
@@ -2445,6 +2464,8 @@ static int mt6358_platform_driver_probe(struct platform_device *pdev)
        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));
 
index 8c9daf3..d1fc170 100644 (file)
@@ -103,7 +103,9 @@ struct pll_calc_map {
 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,
index 3e9d2c6..36cfd10 100644 (file)
@@ -2248,7 +2248,7 @@ static int rt5682_set_component_pll(struct snd_soc_component *component,
 {
        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] &&
@@ -2317,8 +2317,15 @@ static int rt5682_set_component_pll(struct snd_soc_component *component,
                        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);
@@ -2456,8 +2463,8 @@ static int rt5682_set_bias_level(struct snd_soc_component *component,
 
 #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)
 {
@@ -2527,13 +2534,22 @@ static unsigned long rt5682_wclk_recalc_rate(struct clk_hw *hw,
        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,
@@ -2542,13 +2558,22 @@ 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,
@@ -2561,6 +2586,7 @@ 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;
@@ -2583,23 +2609,17 @@ static int rt5682_wclk_set_rate(struct clk_hw *hw, unsigned long rate,
                        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);
 
index f172c9e..6d94327 100644 (file)
 #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)
index fbcee21..2f2b2f5 100644 (file)
@@ -515,7 +515,7 @@ static int wm0010_stage2_load(struct snd_soc_component *component)
        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;
@@ -527,8 +527,6 @@ static int wm0010_stage2_load(struct snd_soc_component *component)
                goto abort1;
        }
 
-       memcpy(img, &fw->data[0], fw->size);
-
        spi_message_init(&m);
        memset(&t, 0, sizeof(t));
        t.rx_buf = out;
index 6cf0f66..2f7f049 100644 (file)
@@ -1389,6 +1389,12 @@ static void wm8960_set_pdata_from_of(struct i2c_client *i2c,
 
        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,
@@ -1446,6 +1452,20 @@ 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,
index e30b66b..0843235 100644 (file)
@@ -343,8 +343,10 @@ static int img_i2s_in_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
        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);
index 5ddbe3a..4da49a4 100644 (file)
@@ -163,8 +163,10 @@ static int img_prl_out_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
        }
 
        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;
index 99a4924..8bc6627 100644 (file)
@@ -212,4 +212,17 @@ config SND_SOC_SAMSUNG_TM2_WM5110
        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
index 8f5dfe2..22259f7 100644 (file)
@@ -41,6 +41,7 @@ snd-soc-bells-objs := bells.o
 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
@@ -64,3 +65,4 @@ obj-$(CONFIG_SND_SOC_BELLS) += snd-soc-bells.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
diff --git a/sound/soc/samsung/aries_wm8994.c b/sound/soc/samsung/aries_wm8994.c
new file mode 100644 (file)
index 0000000..8579c87
--- /dev/null
@@ -0,0 +1,695 @@
+// 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");
index 785a038..d121f5f 100644 (file)
@@ -2,12 +2,69 @@
 //
 // 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
@@ -22,11 +79,13 @@ int snd_soc_component_set_sysclk(struct snd_soc_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);
 
@@ -44,11 +103,13 @@ int snd_soc_component_set_pll(struct snd_soc_component *component, int pll_id,
                              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);
 
@@ -62,194 +123,105 @@ void snd_soc_component_seq_notifier(struct snd_soc_component *component,
 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);
 
@@ -257,22 +229,7 @@ int snd_soc_component_force_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_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);
 
@@ -287,21 +244,25 @@ 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,
@@ -314,52 +275,23 @@ 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)
@@ -383,10 +315,12 @@ int snd_soc_component_is_suspended(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)
@@ -398,21 +332,267 @@ 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)
 {
@@ -438,8 +618,10 @@ int snd_soc_pcm_component_ioctl(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);
 }
@@ -455,7 +637,7 @@ int snd_soc_pcm_component_sync_stop(struct snd_pcm_substream *substream)
                        ret = component->driver->sync_stop(component,
                                                           substream);
                        if (ret < 0)
-                               return ret;
+                               soc_component_ret(component, ret);
                }
        }
 
@@ -473,8 +655,11 @@ int snd_soc_pcm_component_copy_user(struct snd_pcm_substream *substream,
        /* 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;
 }
@@ -510,8 +695,10 @@ int snd_soc_pcm_component_mmap(struct snd_pcm_substream *substream,
        /* 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;
 }
@@ -526,7 +713,7 @@ int snd_soc_pcm_component_new(struct snd_soc_pcm_runtime *rtd)
                if (component->driver->pcm_construct) {
                        ret = component->driver->pcm_construct(component, rtd);
                        if (ret < 0)
-                               return ret;
+                               soc_component_ret(component, ret);
                }
        }
 
@@ -545,3 +732,80 @@ void snd_soc_pcm_component_free(struct snd_soc_pcm_runtime *rtd)
                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;
+}
index 0f30f5a..c38bb42 100644 (file)
@@ -1208,15 +1208,14 @@ static int soc_probe_component(struct snd_soc_card *card,
             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,
@@ -1330,7 +1329,8 @@ static void soc_unbind_aux_dev(struct snd_soc_card *card)
        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);
        }
 }
@@ -1347,7 +1347,8 @@ static int soc_bind_aux_dev(struct snd_soc_card *card)
                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);
        }
@@ -2378,76 +2379,6 @@ err:
        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[] = {
@@ -2510,12 +2441,19 @@ int snd_soc_add_component(struct device *dev,
                        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;
 
diff --git a/sound/soc/soc-io.c b/sound/soc/soc-io.c
deleted file mode 100644 (file)
index 1ff9175..0000000
+++ /dev/null
@@ -1,202 +0,0 @@
-// 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);
index c517064..9be6c28 100644 (file)
@@ -850,7 +850,6 @@ static void codec2codec_close_delayed_work(struct snd_soc_pcm_runtime *rtd)
 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;
 
@@ -860,14 +859,9 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream)
        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) {
@@ -904,25 +898,6 @@ static void soc_pcm_codec_params_fixup(struct snd_pcm_hw_params *params,
        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
@@ -1015,23 +990,16 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
                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;
 
@@ -1090,7 +1058,7 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream)
        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) {
@@ -1104,65 +1072,37 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream)
        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;