Ver código fonte

Merge remote-tracking branches 'asoc/topic/msm8916', 'asoc/topic/mtk', 'asoc/topic/nau8824', 'asoc/topic/nau8825' and 'asoc/topic/of-graph' into asoc-next

Mark Brown 8 anos atrás

+ 5 - 0
Documentation/devicetree/bindings/sound/audio-graph-card.txt

@@ -10,6 +10,8 @@ see ${LINUX}/Documentation/devicetree/bindings/sound/simple-card.txt
 Below are same as Simple-Card.
 
 - label
+- widgets
+- routing
 - dai-format
 - frame-master
 - bitclock-master
@@ -24,6 +26,9 @@ Required properties:
 - compatible				: "audio-graph-card";
 - dais					: list of CPU DAI port{s}
 
+Optional properties:
+- pa-gpios: GPIO used to control external amplifier.
+
 Example: Single DAI case
 
 	sound_card {

+ 7 - 2
Documentation/devicetree/bindings/sound/audio-graph-scu-card.txt

@@ -90,9 +90,12 @@ Example 2. 2 CPU 1 Codec (Mixing)
 		...
 
 		port {
-			codec_endpoint: endpoint {
+			codec_endpoint0: endpoint {
 				remote-endpoint = <&cpu_endpoint0>;
 			};
+			codec_endpoint1: endpoint {
+				remote-endpoint = <&cpu_endpoint1>;
+			};
 		};
 	};
 
@@ -101,7 +104,7 @@ Example 2. 2 CPU 1 Codec (Mixing)
 		ports {
 			cpu_port0: port {
 				cpu_endpoint0: endpoint {
-					remote-endpoint = <&codec_endpoint>;
+					remote-endpoint = <&codec_endpoint0>;
 
 					dai-format = "left_j";
 					...
@@ -109,6 +112,8 @@ Example 2. 2 CPU 1 Codec (Mixing)
 			};
 			cpu_port1: port {
 				cpu_endpoint1: endpoint {
+					remote-endpoint = <&codec_endpoint1>;
+
 					dai-format = "left_j";
 					...
 				};

+ 3 - 0
Documentation/devicetree/bindings/sound/nau8825.txt

@@ -69,6 +69,8 @@ Optional properties:
   - nuvoton,jack-insert-debounce: number from 0 to 7 that sets debounce time to 2^(n+2) ms
   - nuvoton,jack-eject-debounce: number from 0 to 7 that sets debounce time to 2^(n+2) ms
 
+  - nuvoton,crosstalk-bypass: make crosstalk function bypass if set.
+
   - clocks: list of phandle and clock specifier pairs according to common clock bindings for the
       clocks described in clock-names
   - clock-names: should include "mclk" for the MCLK master clock
@@ -96,6 +98,7 @@ Example:
       nuvoton,short-key-debounce = <2>;
       nuvoton,jack-insert-debounce = <7>;
       nuvoton,jack-eject-debounce = <7>;
+      nuvoton,crosstalk-bypass;
 
       clock-names = "mclk";
       clocks = <&tegra_car TEGRA210_CLK_CLK_OUT_2>;

+ 24 - 0
include/sound/simple_card_utils.h

@@ -22,6 +22,11 @@ struct asoc_simple_dai {
 	struct clk *clk;
 };
 
+struct asoc_simple_card_data {
+	u32 convert_rate;
+	u32 convert_channels;
+};
+
 int asoc_simple_card_parse_daifmt(struct device *dev,
 				  struct device_node *node,
 				  struct device_node *codec,
@@ -45,6 +50,8 @@ int asoc_simple_card_parse_clk(struct device *dev,
 			       struct device_node *dai_of_node,
 			       struct asoc_simple_dai *simple_dai,
 			       const char *name);
+int asoc_simple_card_clk_enable(struct asoc_simple_dai *dai);
+void asoc_simple_card_clk_disable(struct asoc_simple_dai *dai);
 
 #define asoc_simple_card_parse_cpu(node, dai_link,				\
 				   list_name, cells_name, is_single_link)	\
@@ -73,6 +80,12 @@ int asoc_simple_card_parse_graph_dai(struct device_node *ep,
 				     struct device_node **endpoint_np,
 				     const char **dai_name);
 
+#define asoc_simple_card_of_parse_tdm(np, dai)			\
+	snd_soc_of_parse_tdm_slot(np,	&(dai)->tx_slot_mask,	\
+					&(dai)->rx_slot_mask,	\
+					&(dai)->slots,		\
+					&(dai)->slot_width);
+
 int asoc_simple_card_init_dai(struct snd_soc_dai *dai,
 			      struct asoc_simple_dai *simple_dai);
 
@@ -82,4 +95,15 @@ void asoc_simple_card_canonicalize_cpu(struct snd_soc_dai_link *dai_link,
 
 int asoc_simple_card_clean_reference(struct snd_soc_card *card);
 
+void asoc_simple_card_convert_fixup(struct asoc_simple_card_data *data,
+				      struct snd_pcm_hw_params *params);
+void asoc_simple_card_parse_convert(struct device *dev, char *prefix,
+				    struct asoc_simple_card_data *data);
+
+int asoc_simple_card_of_parse_routing(struct snd_soc_card *card,
+				      char *prefix,
+				      int optional);
+int asoc_simple_card_of_parse_widgets(struct snd_soc_card *card,
+				      char *prefix);
+
 #endif /* __SIMPLE_CARD_UTILS_H */

+ 3 - 3
sound/soc/codecs/msm8916-wcd-analog.c

@@ -223,8 +223,8 @@ struct pm8916_wcd_analog_priv {
 	u16 codec_version;
 	struct clk *mclk;
 	struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)];
-	bool micbias1_cap_mode;
-	bool micbias2_cap_mode;
+	unsigned int micbias1_cap_mode;
+	unsigned int micbias2_cap_mode;
 };
 
 static const char *const adc2_mux_text[] = { "ZERO", "INP2", "INP3" };
@@ -285,7 +285,7 @@ static void pm8916_wcd_analog_micbias_enable(struct snd_soc_codec *codec)
 
 static int pm8916_wcd_analog_enable_micbias_ext(struct snd_soc_codec
 						 *codec, int event,
-						 int reg, u32 cap_mode)
+						 int reg, unsigned int cap_mode)
 {
 	switch (event) {
 	case SND_SOC_DAPM_POST_PMU:

+ 52 - 0
sound/soc/codecs/nau8824.c

@@ -1124,6 +1124,57 @@ static int nau8824_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
 	return 0;
 }
 
+/**
+ * nau8824_set_tdm_slot - configure DAI TDM.
+ * @dai: DAI
+ * @tx_mask: Bitmask representing active TX slots. Ex.
+ *                 0xf for normal 4 channel TDM.
+ *                 0xf0 for shifted 4 channel TDM
+ * @rx_mask: Bitmask [0:1] representing active DACR RX slots.
+ *                 Bitmask [2:3] representing active DACL RX slots.
+ *                 00=CH0,01=CH1,10=CH2,11=CH3. Ex.
+ *                 0xf for DACL/R selecting TDM CH3.
+ *                 0xf0 for DACL/R selecting shifted TDM CH3.
+ * @slots: Number of slots in use.
+ * @slot_width: Width in bits for each slot.
+ *
+ * Configures a DAI for TDM operation. Only support 4 slots TDM.
+ */
+static int nau8824_set_tdm_slot(struct snd_soc_dai *dai,
+	unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct nau8824 *nau8824 = snd_soc_codec_get_drvdata(codec);
+	unsigned int tslot_l = 0, ctrl_val = 0;
+
+	if (slots > 4 || ((tx_mask & 0xf0) && (tx_mask & 0xf)) ||
+		((rx_mask & 0xf0) && (rx_mask & 0xf)) ||
+		((rx_mask & 0xf0) && (tx_mask & 0xf)) ||
+		((rx_mask & 0xf) && (tx_mask & 0xf0)))
+		return -EINVAL;
+
+	ctrl_val |= (NAU8824_TDM_MODE | NAU8824_TDM_OFFSET_EN);
+	if (tx_mask & 0xf0) {
+		tslot_l = 4 * slot_width;
+		ctrl_val |= (tx_mask >> 4);
+	} else {
+		ctrl_val |= tx_mask;
+	}
+	if (rx_mask & 0xf0)
+		ctrl_val |= ((rx_mask >> 4) << NAU8824_TDM_DACR_RX_SFT);
+	else
+		ctrl_val |= (rx_mask << NAU8824_TDM_DACR_RX_SFT);
+
+	regmap_update_bits(nau8824->regmap, NAU8824_REG_TDM_CTRL,
+		NAU8824_TDM_MODE | NAU8824_TDM_OFFSET_EN |
+		NAU8824_TDM_DACL_RX_MASK | NAU8824_TDM_DACR_RX_MASK |
+		NAU8824_TDM_TX_MASK, ctrl_val);
+	regmap_update_bits(nau8824->regmap, NAU8824_REG_PORT0_LEFT_TIME_SLOT,
+		NAU8824_TSLOT_L_MASK, tslot_l);
+
+	return 0;
+}
+
 /**
  * nau8824_calc_fll_param - Calculate FLL parameters.
  * @fll_in: external clock provided to codec.
@@ -1440,6 +1491,7 @@ static struct snd_soc_codec_driver nau8824_codec_driver = {
 static const struct snd_soc_dai_ops nau8824_dai_ops = {
 	.hw_params = nau8824_hw_params,
 	.set_fmt = nau8824_set_fmt,
+	.set_tdm_slot = nau8824_set_tdm_slot,
 };
 
 #define NAU8824_RATES SNDRV_PCM_RATE_8000_192000

+ 12 - 0
sound/soc/codecs/nau8824.h

@@ -258,6 +258,18 @@
 #define NAU8824_I2S_MS_SLAVE		(0 << NAU8824_I2S_MS_SFT)
 #define NAU8824_I2S_BLK_DIV_MASK	0x7
 
+/* PORT0_LEFT_TIME_SLOT (0x1E) */
+#define NAU8824_TSLOT_L_MASK	0x3ff
+
+/* TDM_CTRL (0x20) */
+#define NAU8824_TDM_MODE		(0x1 << 15)
+#define NAU8824_TDM_OFFSET_EN		(0x1 << 14)
+#define NAU8824_TDM_DACL_RX_SFT	6
+#define NAU8824_TDM_DACL_RX_MASK	(0x3 << NAU8824_TDM_DACL_RX_SFT)
+#define NAU8824_TDM_DACR_RX_SFT	4
+#define NAU8824_TDM_DACR_RX_MASK	(0x3 << NAU8824_TDM_DACR_RX_SFT)
+#define NAU8824_TDM_TX_MASK		0xf
+
 /* ADC_FILTER_CTRL (0x24) */
 #define NAU8824_ADC_SYNC_DOWN_MASK	0x3
 #define NAU8824_ADC_SYNC_DOWN_32	0

+ 62 - 14
sound/soc/codecs/nau8825.c

@@ -1612,7 +1612,6 @@ static int nau8825_jack_insert(struct nau8825 *nau8825)
 		snd_soc_dapm_sync(dapm);
 		break;
 	case 2:
-	case 3:
 		dev_dbg(nau8825->dev, "CTIA (micgnd2) mic connected\n");
 		type = SND_JACK_HEADSET;
 
@@ -1632,6 +1631,11 @@ static int nau8825_jack_insert(struct nau8825 *nau8825)
 		snd_soc_dapm_force_enable_pin(dapm, "SAR");
 		snd_soc_dapm_sync(dapm);
 		break;
+	case 3:
+		/* detect error case */
+		dev_err(nau8825->dev, "detection error; disable mic function\n");
+		type = SND_JACK_HEADPHONE;
+		break;
 	}
 
 	/* Leaving HPOL/R grounded after jack insert by default. They will be
@@ -1682,7 +1686,7 @@ static irqreturn_t nau8825_interrupt(int irq, void *data)
 	} else if (active_irq & NAU8825_HEADSET_COMPLETION_IRQ) {
 		if (nau8825_is_jack_inserted(regmap)) {
 			event |= nau8825_jack_insert(nau8825);
-			if (!nau8825->high_imped) {
+			if (!nau8825->xtalk_bypass && !nau8825->high_imped) {
 				/* Apply the cross talk suppression in the
 				 * headset without high impedance.
 				 */
@@ -2328,6 +2332,13 @@ static int nau8825_set_bias_level(struct snd_soc_codec *codec,
 		break;
 
 	case SND_SOC_BIAS_OFF:
+		/* Reset the configuration of jack type for detection */
+		/* Detach 2kOhm Resistors from MICBIAS to MICGND1/2 */
+		regmap_update_bits(nau8825->regmap, NAU8825_REG_MIC_BIAS,
+			NAU8825_MICBIAS_JKSLV | NAU8825_MICBIAS_JKR2, 0);
+		/* ground HPL/HPR, MICGRND1/2 */
+		regmap_update_bits(nau8825->regmap,
+			NAU8825_REG_HSD_CTRL, 0xf, 0xf);
 		/* Cancel and reset cross talk detection funciton */
 		nau8825_xtalk_cancel(nau8825);
 		/* Turn off all interruptions before system shutdown. Keep the
@@ -2351,6 +2362,10 @@ static int __maybe_unused nau8825_suspend(struct snd_soc_codec *codec)
 
 	disable_irq(nau8825->irq);
 	snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF);
+	/* Power down codec power; don't suppoet button wakeup */
+	snd_soc_dapm_disable_pin(nau8825->dapm, "SAR");
+	snd_soc_dapm_disable_pin(nau8825->dapm, "MICBIAS");
+	snd_soc_dapm_sync(nau8825->dapm);
 	regcache_cache_only(nau8825->regmap, true);
 	regcache_mark_dirty(nau8825->regmap);
 
@@ -2425,10 +2440,13 @@ static void nau8825_print_device_properties(struct nau8825 *nau8825)
 			nau8825->jack_insert_debounce);
 	dev_dbg(dev, "jack-eject-debounce:  %d\n",
 			nau8825->jack_eject_debounce);
+	dev_dbg(dev, "crosstalk-bypass:     %d\n",
+			nau8825->xtalk_bypass);
 }
 
 static int nau8825_read_device_properties(struct device *dev,
 	struct nau8825 *nau8825) {
+	int ret;
 
 	nau8825->jkdet_enable = device_property_read_bool(dev,
 		"nuvoton,jkdet-enable");
@@ -2436,30 +2454,60 @@ static int nau8825_read_device_properties(struct device *dev,
 		"nuvoton,jkdet-pull-enable");
 	nau8825->jkdet_pull_up = device_property_read_bool(dev,
 		"nuvoton,jkdet-pull-up");
-	device_property_read_u32(dev, "nuvoton,jkdet-polarity",
+	ret = device_property_read_u32(dev, "nuvoton,jkdet-polarity",
 		&nau8825->jkdet_polarity);
-	device_property_read_u32(dev, "nuvoton,micbias-voltage",
+	if (ret)
+		nau8825->jkdet_polarity = 1;
+	ret = device_property_read_u32(dev, "nuvoton,micbias-voltage",
 		&nau8825->micbias_voltage);
-	device_property_read_u32(dev, "nuvoton,vref-impedance",
+	if (ret)
+		nau8825->micbias_voltage = 6;
+	ret = device_property_read_u32(dev, "nuvoton,vref-impedance",
 		&nau8825->vref_impedance);
-	device_property_read_u32(dev, "nuvoton,sar-threshold-num",
+	if (ret)
+		nau8825->vref_impedance = 2;
+	ret = device_property_read_u32(dev, "nuvoton,sar-threshold-num",
 		&nau8825->sar_threshold_num);
-	device_property_read_u32_array(dev, "nuvoton,sar-threshold",
+	if (ret)
+		nau8825->sar_threshold_num = 4;
+	ret = device_property_read_u32_array(dev, "nuvoton,sar-threshold",
 		nau8825->sar_threshold, nau8825->sar_threshold_num);
-	device_property_read_u32(dev, "nuvoton,sar-hysteresis",
+	if (ret) {
+		nau8825->sar_threshold[0] = 0x08;
+		nau8825->sar_threshold[1] = 0x12;
+		nau8825->sar_threshold[2] = 0x26;
+		nau8825->sar_threshold[3] = 0x73;
+	}
+	ret = device_property_read_u32(dev, "nuvoton,sar-hysteresis",
 		&nau8825->sar_hysteresis);
-	device_property_read_u32(dev, "nuvoton,sar-voltage",
+	if (ret)
+		nau8825->sar_hysteresis = 0;
+	ret = device_property_read_u32(dev, "nuvoton,sar-voltage",
 		&nau8825->sar_voltage);
-	device_property_read_u32(dev, "nuvoton,sar-compare-time",
+	if (ret)
+		nau8825->sar_voltage = 6;
+	ret = device_property_read_u32(dev, "nuvoton,sar-compare-time",
 		&nau8825->sar_compare_time);
-	device_property_read_u32(dev, "nuvoton,sar-sampling-time",
+	if (ret)
+		nau8825->sar_compare_time = 1;
+	ret = device_property_read_u32(dev, "nuvoton,sar-sampling-time",
 		&nau8825->sar_sampling_time);
-	device_property_read_u32(dev, "nuvoton,short-key-debounce",
+	if (ret)
+		nau8825->sar_sampling_time = 1;
+	ret = device_property_read_u32(dev, "nuvoton,short-key-debounce",
 		&nau8825->key_debounce);
-	device_property_read_u32(dev, "nuvoton,jack-insert-debounce",
+	if (ret)
+		nau8825->key_debounce = 3;
+	ret = device_property_read_u32(dev, "nuvoton,jack-insert-debounce",
 		&nau8825->jack_insert_debounce);
-	device_property_read_u32(dev, "nuvoton,jack-eject-debounce",
+	if (ret)
+		nau8825->jack_insert_debounce = 7;
+	ret = device_property_read_u32(dev, "nuvoton,jack-eject-debounce",
 		&nau8825->jack_eject_debounce);
+	if (ret)
+		nau8825->jack_eject_debounce = 0;
+	nau8825->xtalk_bypass = device_property_read_bool(dev,
+		"nuvoton,crosstalk-bypass");
 
 	nau8825->mclk = devm_clk_get(dev, "mclk");
 	if (PTR_ERR(nau8825->mclk) == -EPROBE_DEFER) {

+ 1 - 0
sound/soc/codecs/nau8825.h

@@ -476,6 +476,7 @@ struct nau8825 {
 	int xtalk_event_mask;
 	bool xtalk_protect;
 	int imp_rms[NAU8825_XTALK_IMM];
+	int xtalk_bypass;
 };
 
 int nau8825_enable_jack_detect(struct snd_soc_codec *codec,

+ 55 - 18
sound/soc/generic/audio-graph-card.c

@@ -13,6 +13,7 @@
 #include <linux/clk.h>
 #include <linux/device.h>
 #include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/of_device.h>
@@ -30,6 +31,34 @@ struct graph_card_data {
 		struct asoc_simple_dai codec_dai;
 	} *dai_props;
 	struct snd_soc_dai_link *dai_link;
+	struct gpio_desc *pa_gpio;
+};
+
+static int asoc_graph_card_outdrv_event(struct snd_soc_dapm_widget *w,
+					struct snd_kcontrol *kcontrol,
+					int event)
+{
+	struct snd_soc_dapm_context *dapm = w->dapm;
+	struct graph_card_data *priv = snd_soc_card_get_drvdata(dapm->card);
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		gpiod_set_value_cansleep(priv->pa_gpio, 1);
+		break;
+	case SND_SOC_DAPM_PRE_PMD:
+		gpiod_set_value_cansleep(priv->pa_gpio, 0);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static const struct snd_soc_dapm_widget asoc_graph_card_dapm_widgets[] = {
+	SND_SOC_DAPM_OUT_DRV_E("Amplifier", SND_SOC_NOPM,
+			       0, 0, NULL, 0, asoc_graph_card_outdrv_event,
+			       SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
 };
 
 #define graph_priv_to_card(priv) (&(priv)->snd_card)
@@ -44,13 +73,13 @@ static int asoc_graph_card_startup(struct snd_pcm_substream *substream)
 	struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num);
 	int ret;
 
-	ret = clk_prepare_enable(dai_props->cpu_dai.clk);
+	ret = asoc_simple_card_clk_enable(&dai_props->cpu_dai);
 	if (ret)
 		return ret;
 
-	ret = clk_prepare_enable(dai_props->codec_dai.clk);
+	ret = asoc_simple_card_clk_enable(&dai_props->codec_dai);
 	if (ret)
-		clk_disable_unprepare(dai_props->cpu_dai.clk);
+		asoc_simple_card_clk_disable(&dai_props->cpu_dai);
 
 	return ret;
 }
@@ -61,9 +90,9 @@ static void asoc_graph_card_shutdown(struct snd_pcm_substream *substream)
 	struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
 	struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num);
 
-	clk_disable_unprepare(dai_props->cpu_dai.clk);
+	asoc_simple_card_clk_disable(&dai_props->cpu_dai);
 
-	clk_disable_unprepare(dai_props->codec_dai.clk);
+	asoc_simple_card_clk_disable(&dai_props->codec_dai);
 }
 
 static struct snd_soc_ops asoc_graph_card_ops = {
@@ -100,7 +129,6 @@ static int asoc_graph_card_dai_link_of(struct device_node *cpu_port,
 	struct graph_dai_props *dai_props = graph_priv_to_props(priv, idx);
 	struct asoc_simple_dai *cpu_dai = &dai_props->cpu_dai;
 	struct asoc_simple_dai *codec_dai = &dai_props->codec_dai;
-	struct snd_soc_card *card = graph_priv_to_card(priv);
 	struct device_node *cpu_ep    = of_get_next_child(cpu_port, NULL);
 	struct device_node *codec_ep = of_graph_get_remote_endpoint(cpu_ep);
 	struct device_node *rcpu_ep = of_graph_get_remote_endpoint(codec_ep);
@@ -131,19 +159,11 @@ static int asoc_graph_card_dai_link_of(struct device_node *cpu_port,
 	if (ret < 0)
 		goto dai_link_of_err;
 
-	ret = snd_soc_of_parse_tdm_slot(cpu_ep,
-					&cpu_dai->tx_slot_mask,
-					&cpu_dai->rx_slot_mask,
-					&cpu_dai->slots,
-					&cpu_dai->slot_width);
+	ret = asoc_simple_card_of_parse_tdm(cpu_ep, cpu_dai);
 	if (ret < 0)
 		goto dai_link_of_err;
 
-	ret = snd_soc_of_parse_tdm_slot(codec_ep,
-					&codec_dai->tx_slot_mask,
-					&codec_dai->rx_slot_mask,
-					&codec_dai->slots,
-					&codec_dai->slot_width);
+	ret = asoc_simple_card_of_parse_tdm(codec_ep, codec_dai);
 	if (ret < 0)
 		goto dai_link_of_err;
 
@@ -170,7 +190,7 @@ static int asoc_graph_card_dai_link_of(struct device_node *cpu_port,
 	dai_link->init = asoc_graph_card_dai_init;
 
 	asoc_simple_card_canonicalize_cpu(dai_link,
-					  card->num_links == 1);
+		of_graph_get_endpoint_count(dai_link->cpu_of_node) == 1);
 
 dai_link_of_err:
 	of_node_put(cpu_ep);
@@ -189,8 +209,16 @@ static int asoc_graph_card_parse_of(struct graph_card_data *priv)
 	int rc, idx = 0;
 	int ret;
 
+	ret = asoc_simple_card_of_parse_widgets(card, NULL);
+	if (ret < 0)
+		return ret;
+
+	ret = asoc_simple_card_of_parse_routing(card, NULL, 1);
+	if (ret < 0)
+		return ret;
+
 	/*
-	 * we need to consider "widgets", "routing", "mclk-fs" around here
+	 * we need to consider "mclk-fs" around here
 	 * see simple-card
 	 */
 
@@ -242,6 +270,13 @@ static int asoc_graph_card_probe(struct platform_device *pdev)
 	if (!dai_props || !dai_link)
 		return -ENOMEM;
 
+	priv->pa_gpio = devm_gpiod_get_optional(dev, "pa", GPIOD_OUT_LOW);
+	if (IS_ERR(priv->pa_gpio)) {
+		ret = PTR_ERR(priv->pa_gpio);
+		dev_err(dev, "failed to get amplifier gpio: %d\n", ret);
+		return ret;
+	}
+
 	priv->dai_props			= dai_props;
 	priv->dai_link			= dai_link;
 
@@ -251,6 +286,8 @@ static int asoc_graph_card_probe(struct platform_device *pdev)
 	card->dev	= dev;
 	card->dai_link	= dai_link;
 	card->num_links	= num;
+	card->dapm_widgets = asoc_graph_card_dapm_widgets;
+	card->num_dapm_widgets = ARRAY_SIZE(asoc_graph_card_dapm_widgets);
 
 	ret = asoc_graph_card_parse_of(priv);
 	if (ret < 0) {

+ 34 - 34
sound/soc/generic/audio-graph-scu-card.c

@@ -30,8 +30,7 @@ struct graph_card_data {
 	struct snd_soc_codec_conf codec_conf;
 	struct asoc_simple_dai *dai_props;
 	struct snd_soc_dai_link *dai_link;
-	u32 convert_rate;
-	u32 convert_channels;
+	struct asoc_simple_card_data adata;
 };
 
 #define graph_priv_to_card(priv) (&(priv)->snd_card)
@@ -45,7 +44,7 @@ static int asoc_graph_card_startup(struct snd_pcm_substream *substream)
 	struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
 	struct asoc_simple_dai *dai_props = graph_priv_to_props(priv, rtd->num);
 
-	return clk_prepare_enable(dai_props->clk);
+	return asoc_simple_card_clk_enable(dai_props);
 }
 
 static void asoc_graph_card_shutdown(struct snd_pcm_substream *substream)
@@ -54,7 +53,7 @@ static void asoc_graph_card_shutdown(struct snd_pcm_substream *substream)
 	struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
 	struct asoc_simple_dai *dai_props = graph_priv_to_props(priv, rtd->num);
 
-	clk_disable_unprepare(dai_props->clk);
+	asoc_simple_card_clk_disable(dai_props);
 }
 
 static struct snd_soc_ops asoc_graph_card_ops = {
@@ -83,18 +82,8 @@ static int asoc_graph_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
 					       struct snd_pcm_hw_params *params)
 {
 	struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
-	struct snd_interval *rate = hw_param_interval(params,
-						      SNDRV_PCM_HW_PARAM_RATE);
-	struct snd_interval *channels = hw_param_interval(params,
-							  SNDRV_PCM_HW_PARAM_CHANNELS);
 
-	if (priv->convert_rate)
-		rate->min =
-		rate->max = priv->convert_rate;
-
-	if (priv->convert_channels)
-		channels->min =
-		channels->max = priv->convert_channels;
+	asoc_simple_card_convert_fixup(&priv->adata, params);
 
 	return 0;
 }
@@ -136,7 +125,7 @@ static int asoc_graph_card_dai_link_of(struct device_node *ep,
 
 		/* card->num_links includes Codec */
 		asoc_simple_card_canonicalize_cpu(dai_link,
-					(card->num_links - 1) == 1);
+			of_graph_get_endpoint_count(dai_link->cpu_of_node) == 1);
 	} else {
 		/* FE is dummy */
 		dai_link->cpu_of_node		= NULL;
@@ -167,11 +156,7 @@ static int asoc_graph_card_dai_link_of(struct device_node *ep,
 					      "prefix");
 	}
 
-	ret = snd_soc_of_parse_tdm_slot(ep,
-					&dai_props->tx_slot_mask,
-					&dai_props->rx_slot_mask,
-					&dai_props->slots,
-					&dai_props->slot_width);
+	ret = asoc_simple_card_of_parse_tdm(ep, dai_props);
 	if (ret)
 		return ret;
 
@@ -198,6 +183,8 @@ static int asoc_graph_card_parse_of(struct graph_card_data *priv)
 	struct device_node *cpu_ep;
 	struct device_node *codec_ep;
 	struct device_node *rcpu_ep;
+	struct device_node *codec_port;
+	struct device_node *codec_port_old;
 	unsigned int daifmt = 0;
 	int dai_idx, ret;
 	int rc, codec;
@@ -210,15 +197,11 @@ static int asoc_graph_card_parse_of(struct graph_card_data *priv)
 	 * see simple-card
 	 */
 
-	ret = snd_soc_of_parse_audio_routing(card, "routing");
-	if (ret)
+	ret = asoc_simple_card_of_parse_routing(card, NULL, 0);
+	if (ret < 0)
 		return ret;
 
-	/* sampling rate convert */
-	of_property_read_u32(node, "convert-rate", &priv->convert_rate);
-
-	/* channels transfer */
-	of_property_read_u32(node, "convert-channels", &priv->convert_channels);
+	asoc_simple_card_parse_convert(dev, NULL, &priv->adata);
 
 	/*
 	 * it supports multi CPU, single CODEC only here
@@ -254,6 +237,7 @@ static int asoc_graph_card_parse_of(struct graph_card_data *priv)
 	}
 
 	dai_idx = 0;
+	codec_port_old = NULL;
 	for (codec = 0; codec < 2; codec++) {
 		/*
 		 * To listup valid sounds continuously,
@@ -264,15 +248,22 @@ static int asoc_graph_card_parse_of(struct graph_card_data *priv)
 			cpu_port = it.node;
 			cpu_ep   = of_get_next_child(cpu_port, NULL);
 			codec_ep = of_graph_get_remote_endpoint(cpu_ep);
+			codec_port = of_graph_get_port_parent(codec_ep);
 
 			of_node_put(cpu_port);
 			of_node_put(cpu_ep);
 			of_node_put(codec_ep);
+			of_node_put(codec_port);
 
 			if (codec) {
-				if (!codec_ep)
+				if (!codec_port)
 					continue;
 
+				if (codec_port_old == codec_port)
+					continue;
+
+				codec_port_old = codec_port;
+
 				/* Back-End (= Codec) */
 				ret = asoc_graph_card_dai_link_of(codec_ep, priv, daifmt, dai_idx++, 0);
 				if (ret < 0)
@@ -290,9 +281,6 @@ static int asoc_graph_card_parse_of(struct graph_card_data *priv)
 	if (ret)
 		goto parse_of_err;
 
-	dev_dbg(dev, "convert_rate     %d\n", priv->convert_rate);
-	dev_dbg(dev, "convert_channels %d\n", priv->convert_channels);
-
 	ret = 0;
 
 parse_of_err:
@@ -306,22 +294,34 @@ static int asoc_graph_get_dais_count(struct device *dev)
 	struct device_node *cpu_port;
 	struct device_node *cpu_ep;
 	struct device_node *codec_ep;
+	struct device_node *codec_port;
+	struct device_node *codec_port_old;
 	int count = 0;
 	int rc;
 
+	codec_port_old = NULL;
 	of_for_each_phandle(&it, rc, node, "dais", NULL, 0) {
 		cpu_port = it.node;
 		cpu_ep   = of_get_next_child(cpu_port, NULL);
 		codec_ep = of_graph_get_remote_endpoint(cpu_ep);
+		codec_port = of_graph_get_port_parent(codec_ep);
 
 		of_node_put(cpu_port);
 		of_node_put(cpu_ep);
 		of_node_put(codec_ep);
+		of_node_put(codec_port);
 
 		if (cpu_ep)
 			count++;
-		if (codec_ep)
-			count++;
+
+		if (!codec_port)
+			continue;
+
+		if (codec_port_old == codec_port)
+			continue;
+
+		count++;
+		codec_port_old = codec_port;
 	}
 
 	return count;

+ 101 - 1
sound/soc/generic/simple-card-utils.c

@@ -13,6 +13,46 @@
 #include <linux/of_graph.h>
 #include <sound/simple_card_utils.h>
 
+void asoc_simple_card_convert_fixup(struct asoc_simple_card_data *data,
+				    struct snd_pcm_hw_params *params)
+{
+	struct snd_interval *rate = hw_param_interval(params,
+						SNDRV_PCM_HW_PARAM_RATE);
+	struct snd_interval *channels = hw_param_interval(params,
+						SNDRV_PCM_HW_PARAM_CHANNELS);
+
+	if (data->convert_rate)
+		rate->min =
+		rate->max = data->convert_rate;
+
+	if (data->convert_channels)
+		channels->min =
+		channels->max = data->convert_channels;
+}
+EXPORT_SYMBOL_GPL(asoc_simple_card_convert_fixup);
+
+void asoc_simple_card_parse_convert(struct device *dev, char *prefix,
+				    struct asoc_simple_card_data *data)
+{
+	struct device_node *np = dev->of_node;
+	char prop[128];
+
+	if (!prefix)
+		prefix = "";
+
+	/* sampling rate convert */
+	snprintf(prop, sizeof(prop), "%s%s", prefix, "convert-rate");
+	of_property_read_u32(np, prop, &data->convert_rate);
+
+	/* channels transfer */
+	snprintf(prop, sizeof(prop), "%s%s", prefix, "convert-channels");
+	of_property_read_u32(np, prop, &data->convert_channels);
+
+	dev_dbg(dev, "convert_rate     %d\n", data->convert_rate);
+	dev_dbg(dev, "convert_channels %d\n", data->convert_channels);
+}
+EXPORT_SYMBOL_GPL(asoc_simple_card_parse_convert);
+
 int asoc_simple_card_parse_daifmt(struct device *dev,
 				  struct device_node *node,
 				  struct device_node *codec,
@@ -110,6 +150,24 @@ int asoc_simple_card_parse_card_name(struct snd_soc_card *card,
 }
 EXPORT_SYMBOL_GPL(asoc_simple_card_parse_card_name);
 
+static void asoc_simple_card_clk_register(struct asoc_simple_dai *dai,
+					  struct clk *clk)
+{
+	dai->clk = clk;
+}
+
+int asoc_simple_card_clk_enable(struct asoc_simple_dai *dai)
+{
+	return clk_prepare_enable(dai->clk);
+}
+EXPORT_SYMBOL_GPL(asoc_simple_card_clk_enable);
+
+void asoc_simple_card_clk_disable(struct asoc_simple_dai *dai)
+{
+	clk_disable_unprepare(dai->clk);
+}
+EXPORT_SYMBOL_GPL(asoc_simple_card_clk_disable);
+
 int asoc_simple_card_parse_clk(struct device *dev,
 			       struct device_node *node,
 			       struct device_node *dai_of_node,
@@ -128,7 +186,8 @@ int asoc_simple_card_parse_clk(struct device *dev,
 	clk = devm_get_clk_from_child(dev, node, NULL);
 	if (!IS_ERR(clk)) {
 		simple_dai->sysclk = clk_get_rate(clk);
-		simple_dai->clk = clk;
+
+		asoc_simple_card_clk_register(simple_dai, clk);
 	} else if (!of_property_read_u32(node, "system-clock-frequency", &val)) {
 		simple_dai->sysclk = val;
 	} else {
@@ -316,6 +375,47 @@ int asoc_simple_card_clean_reference(struct snd_soc_card *card)
 }
 EXPORT_SYMBOL_GPL(asoc_simple_card_clean_reference);
 
+int asoc_simple_card_of_parse_routing(struct snd_soc_card *card,
+				      char *prefix,
+				      int optional)
+{
+	struct device_node *node = card->dev->of_node;
+	char prop[128];
+
+	if (!prefix)
+		prefix = "";
+
+	snprintf(prop, sizeof(prop), "%s%s", prefix, "routing");
+
+	if (!of_property_read_bool(node, prop)) {
+		if (optional)
+			return 0;
+		return -EINVAL;
+	}
+
+	return snd_soc_of_parse_audio_routing(card, prop);
+}
+EXPORT_SYMBOL_GPL(asoc_simple_card_of_parse_routing);
+
+int asoc_simple_card_of_parse_widgets(struct snd_soc_card *card,
+				      char *prefix)
+{
+	struct device_node *node = card->dev->of_node;
+	char prop[128];
+
+	if (!prefix)
+		prefix = "";
+
+	snprintf(prop, sizeof(prop), "%s%s", prefix, "widgets");
+
+	if (of_property_read_bool(node, prop))
+		return snd_soc_of_parse_audio_simple_widgets(card, prop);
+
+	/* no widgets is not error */
+	return 0;
+}
+EXPORT_SYMBOL_GPL(asoc_simple_card_of_parse_widgets);
+
 /* Module information */
 MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
 MODULE_DESCRIPTION("ALSA SoC Simple Card Utils");

+ 23 - 31
sound/soc/generic/simple-card.c

@@ -118,13 +118,13 @@ static int asoc_simple_card_startup(struct snd_pcm_substream *substream)
 		simple_priv_to_props(priv, rtd->num);
 	int ret;
 
-	ret = clk_prepare_enable(dai_props->cpu_dai.clk);
+	ret = asoc_simple_card_clk_enable(&dai_props->cpu_dai);
 	if (ret)
 		return ret;
 
-	ret = clk_prepare_enable(dai_props->codec_dai.clk);
+	ret = asoc_simple_card_clk_enable(&dai_props->codec_dai);
 	if (ret)
-		clk_disable_unprepare(dai_props->cpu_dai.clk);
+		asoc_simple_card_clk_disable(&dai_props->cpu_dai);
 
 	return ret;
 }
@@ -136,9 +136,9 @@ static void asoc_simple_card_shutdown(struct snd_pcm_substream *substream)
 	struct simple_dai_props *dai_props =
 		simple_priv_to_props(priv, rtd->num);
 
-	clk_disable_unprepare(dai_props->cpu_dai.clk);
+	asoc_simple_card_clk_disable(&dai_props->cpu_dai);
 
-	clk_disable_unprepare(dai_props->codec_dai.clk);
+	asoc_simple_card_clk_disable(&dai_props->codec_dai);
 }
 
 static int asoc_simple_card_hw_params(struct snd_pcm_substream *substream,
@@ -233,13 +233,19 @@ static int asoc_simple_card_dai_link_of(struct device_node *node,
 	snprintf(prop, sizeof(prop), "%scpu", prefix);
 	cpu = of_get_child_by_name(node, prop);
 
+	if (!cpu) {
+		ret = -EINVAL;
+		dev_err(dev, "%s: Can't find %s DT node\n", __func__, prop);
+		goto dai_link_of_err;
+	}
+
 	snprintf(prop, sizeof(prop), "%splat", prefix);
 	plat = of_get_child_by_name(node, prop);
 
 	snprintf(prop, sizeof(prop), "%scodec", prefix);
 	codec = of_get_child_by_name(node, prop);
 
-	if (!cpu || !codec) {
+	if (!codec) {
 		ret = -EINVAL;
 		dev_err(dev, "%s: Can't find %s DT node\n", __func__, prop);
 		goto dai_link_of_err;
@@ -265,17 +271,11 @@ static int asoc_simple_card_dai_link_of(struct device_node *node,
 	if (ret < 0)
 		goto dai_link_of_err;
 
-	ret = snd_soc_of_parse_tdm_slot(cpu,	&cpu_dai->tx_slot_mask,
-						&cpu_dai->rx_slot_mask,
-						&cpu_dai->slots,
-						&cpu_dai->slot_width);
+	ret = asoc_simple_card_of_parse_tdm(cpu, cpu_dai);
 	if (ret < 0)
 		goto dai_link_of_err;
 
-	ret = snd_soc_of_parse_tdm_slot(codec,	&codec_dai->tx_slot_mask,
-						&codec_dai->rx_slot_mask,
-						&codec_dai->slots,
-						&codec_dai->slot_width);
+	ret = asoc_simple_card_of_parse_tdm(codec, codec_dai);
 	if (ret < 0)
 		goto dai_link_of_err;
 
@@ -341,12 +341,12 @@ static int asoc_simple_card_parse_aux_devs(struct device_node *node,
 	return 0;
 }
 
-static int asoc_simple_card_parse_of(struct device_node *node,
-				     struct simple_card_data *priv)
+static int asoc_simple_card_parse_of(struct simple_card_data *priv)
 {
 	struct device *dev = simple_priv_to_dev(priv);
 	struct snd_soc_card *card = simple_priv_to_card(priv);
 	struct device_node *dai_link;
+	struct device_node *node = dev->of_node;
 	int ret;
 
 	if (!node)
@@ -354,21 +354,13 @@ static int asoc_simple_card_parse_of(struct device_node *node,
 
 	dai_link = of_get_child_by_name(node, PREFIX "dai-link");
 
-	/* The off-codec widgets */
-	if (of_property_read_bool(node, PREFIX "widgets")) {
-		ret = snd_soc_of_parse_audio_simple_widgets(card,
-					PREFIX "widgets");
-		if (ret)
-			goto card_parse_end;
-	}
+	ret = asoc_simple_card_of_parse_widgets(card, PREFIX);
+	if (ret < 0)
+		goto card_parse_end;
 
-	/* DAPM routes */
-	if (of_property_read_bool(node, PREFIX "routing")) {
-		ret = snd_soc_of_parse_audio_routing(card,
-					PREFIX "routing");
-		if (ret)
-			goto card_parse_end;
-	}
+	ret = asoc_simple_card_of_parse_routing(card, PREFIX, 1);
+	if (ret < 0)
+		goto card_parse_end;
 
 	/* Factor to mclk, used in hw_params() */
 	of_property_read_u32(node, PREFIX "mclk-fs", &priv->mclk_fs);
@@ -445,7 +437,7 @@ static int asoc_simple_card_probe(struct platform_device *pdev)
 
 	if (np && of_device_is_available(np)) {
 
-		ret = asoc_simple_card_parse_of(np, priv);
+		ret = asoc_simple_card_parse_of(priv);
 		if (ret < 0) {
 			if (ret != -EPROBE_DEFER)
 				dev_err(dev, "parse error %d\n", ret);

+ 7 - 29
sound/soc/generic/simple-scu-card.c

@@ -27,8 +27,7 @@ struct simple_card_data {
 	struct snd_soc_codec_conf codec_conf;
 	struct asoc_simple_dai *dai_props;
 	struct snd_soc_dai_link *dai_link;
-	u32 convert_rate;
-	u32 convert_channels;
+	struct asoc_simple_card_data adata;
 };
 
 #define simple_priv_to_card(priv) (&(priv)->snd_card)
@@ -47,7 +46,7 @@ static int asoc_simple_card_startup(struct snd_pcm_substream *substream)
 	struct asoc_simple_dai *dai_props =
 		simple_priv_to_props(priv, rtd->num);
 
-	return clk_prepare_enable(dai_props->clk);
+	return asoc_simple_card_clk_enable(dai_props);
 }
 
 static void asoc_simple_card_shutdown(struct snd_pcm_substream *substream)
@@ -57,7 +56,7 @@ static void asoc_simple_card_shutdown(struct snd_pcm_substream *substream)
 	struct asoc_simple_dai *dai_props =
 		simple_priv_to_props(priv, rtd->num);
 
-	clk_disable_unprepare(dai_props->clk);
+	asoc_simple_card_clk_disable(dai_props);
 }
 
 static const struct snd_soc_ops asoc_simple_card_ops = {
@@ -86,18 +85,8 @@ static int asoc_simple_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
 					struct snd_pcm_hw_params *params)
 {
 	struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
-	struct snd_interval *rate = hw_param_interval(params,
-						      SNDRV_PCM_HW_PARAM_RATE);
-	struct snd_interval *channels = hw_param_interval(params,
-						SNDRV_PCM_HW_PARAM_CHANNELS);
 
-	if (priv->convert_rate)
-		rate->min =
-		rate->max = priv->convert_rate;
-
-	if (priv->convert_channels)
-		channels->min =
-		channels->max = priv->convert_channels;
+	asoc_simple_card_convert_fixup(&priv->adata, params);
 
 	return 0;
 }
@@ -171,11 +160,7 @@ static int asoc_simple_card_dai_link_of(struct device_node *np,
 					      PREFIX "prefix");
 	}
 
-	ret = snd_soc_of_parse_tdm_slot(np,
-					&dai_props->tx_slot_mask,
-					&dai_props->rx_slot_mask,
-					&dai_props->slots,
-					&dai_props->slot_width);
+	ret = asoc_simple_card_of_parse_tdm(np, dai_props);
 	if (ret)
 		return ret;
 
@@ -206,15 +191,11 @@ static int asoc_simple_card_parse_of(struct simple_card_data *priv)
 	if (!node)
 		return -EINVAL;
 
-	ret = snd_soc_of_parse_audio_routing(card, PREFIX "routing");
+	ret = asoc_simple_card_of_parse_routing(card, PREFIX, 0);
 	if (ret < 0)
 		return ret;
 
-	/* sampling rate convert */
-	of_property_read_u32(node, PREFIX "convert-rate", &priv->convert_rate);
-
-	/* channels transfer */
-	of_property_read_u32(node, PREFIX "convert-channels", &priv->convert_channels);
+	asoc_simple_card_parse_convert(dev, PREFIX, &priv->adata);
 
 	/* find 1st codec */
 	np = of_get_child_by_name(node, PREFIX "codec");
@@ -241,9 +222,6 @@ static int asoc_simple_card_parse_of(struct simple_card_data *priv)
 	if (ret < 0)
 		return ret;
 
-	dev_dbg(dev, "convert_rate     %d\n", priv->convert_rate);
-	dev_dbg(dev, "convert_channels %d\n", priv->convert_channels);
-
 	return 0;
 }
 

+ 1 - 1
sound/soc/mediatek/mt2701/mt2701-cs42448.c

@@ -107,7 +107,7 @@ static const struct snd_kcontrol_new mt2701_cs42448_controls[] = {
 
 static const unsigned int mt2701_cs42448_sampling_rates[] = {48000};
 
-static struct snd_pcm_hw_constraint_list mt2701_cs42448_constraints_rates = {
+static const struct snd_pcm_hw_constraint_list mt2701_cs42448_constraints_rates = {
 		.count = ARRAY_SIZE(mt2701_cs42448_sampling_rates),
 		.list = mt2701_cs42448_sampling_rates,
 		.mask = 0,