18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci//
38c2ecf20Sopenharmony_ci// TSE-850 audio - ASoC driver for the Axentia TSE-850 with a PCM5142 codec
48c2ecf20Sopenharmony_ci//
58c2ecf20Sopenharmony_ci// Copyright (C) 2016 Axentia Technologies AB
68c2ecf20Sopenharmony_ci//
78c2ecf20Sopenharmony_ci// Author: Peter Rosin <peda@axentia.se>
88c2ecf20Sopenharmony_ci//
98c2ecf20Sopenharmony_ci//               loop1 relays
108c2ecf20Sopenharmony_ci//   IN1 +---o  +------------+  o---+ OUT1
118c2ecf20Sopenharmony_ci//            \                /
128c2ecf20Sopenharmony_ci//             +              +
138c2ecf20Sopenharmony_ci//             |   /          |
148c2ecf20Sopenharmony_ci//             +--o  +--.     |
158c2ecf20Sopenharmony_ci//             |  add   |     |
168c2ecf20Sopenharmony_ci//             |        V     |
178c2ecf20Sopenharmony_ci//             |      .---.   |
188c2ecf20Sopenharmony_ci//   DAC +----------->|Sum|---+
198c2ecf20Sopenharmony_ci//             |      '---'   |
208c2ecf20Sopenharmony_ci//             |              |
218c2ecf20Sopenharmony_ci//             +              +
228c2ecf20Sopenharmony_ci//
238c2ecf20Sopenharmony_ci//   IN2 +---o--+------------+--o---+ OUT2
248c2ecf20Sopenharmony_ci//               loop2 relays
258c2ecf20Sopenharmony_ci//
268c2ecf20Sopenharmony_ci// The 'loop1' gpio pin controls two relays, which are either in loop
278c2ecf20Sopenharmony_ci// position, meaning that input and output are directly connected, or
288c2ecf20Sopenharmony_ci// they are in mixer position, meaning that the signal is passed through
298c2ecf20Sopenharmony_ci// the 'Sum' mixer. Similarly for 'loop2'.
308c2ecf20Sopenharmony_ci//
318c2ecf20Sopenharmony_ci// In the above, the 'loop1' relays are inactive, thus feeding IN1 to the
328c2ecf20Sopenharmony_ci// mixer (if 'add' is active) and feeding the mixer output to OUT1. The
338c2ecf20Sopenharmony_ci// 'loop2' relays are active, short-cutting the TSE-850 from channel 2.
348c2ecf20Sopenharmony_ci// IN1, IN2, OUT1 and OUT2 are TSE-850 connectors and DAC is the PCB name
358c2ecf20Sopenharmony_ci// of the (filtered) output from the PCM5142 codec.
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci#include <linux/clk.h>
388c2ecf20Sopenharmony_ci#include <linux/gpio.h>
398c2ecf20Sopenharmony_ci#include <linux/module.h>
408c2ecf20Sopenharmony_ci#include <linux/of.h>
418c2ecf20Sopenharmony_ci#include <linux/of_device.h>
428c2ecf20Sopenharmony_ci#include <linux/of_gpio.h>
438c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h>
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci#include <sound/soc.h>
468c2ecf20Sopenharmony_ci#include <sound/pcm_params.h>
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cistruct tse850_priv {
498c2ecf20Sopenharmony_ci	struct gpio_desc *add;
508c2ecf20Sopenharmony_ci	struct gpio_desc *loop1;
518c2ecf20Sopenharmony_ci	struct gpio_desc *loop2;
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	struct regulator *ana;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	int add_cache;
568c2ecf20Sopenharmony_ci	int loop1_cache;
578c2ecf20Sopenharmony_ci	int loop2_cache;
588c2ecf20Sopenharmony_ci};
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cistatic int tse850_get_mux1(struct snd_kcontrol *kctrl,
618c2ecf20Sopenharmony_ci			   struct snd_ctl_elem_value *ucontrol)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kctrl);
648c2ecf20Sopenharmony_ci	struct snd_soc_card *card = dapm->card;
658c2ecf20Sopenharmony_ci	struct tse850_priv *tse850 = snd_soc_card_get_drvdata(card);
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	ucontrol->value.enumerated.item[0] = tse850->loop1_cache;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	return 0;
708c2ecf20Sopenharmony_ci}
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cistatic int tse850_put_mux1(struct snd_kcontrol *kctrl,
738c2ecf20Sopenharmony_ci			   struct snd_ctl_elem_value *ucontrol)
748c2ecf20Sopenharmony_ci{
758c2ecf20Sopenharmony_ci	struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kctrl);
768c2ecf20Sopenharmony_ci	struct snd_soc_card *card = dapm->card;
778c2ecf20Sopenharmony_ci	struct tse850_priv *tse850 = snd_soc_card_get_drvdata(card);
788c2ecf20Sopenharmony_ci	struct soc_enum *e = (struct soc_enum *)kctrl->private_value;
798c2ecf20Sopenharmony_ci	unsigned int val = ucontrol->value.enumerated.item[0];
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	if (val >= e->items)
828c2ecf20Sopenharmony_ci		return -EINVAL;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	gpiod_set_value_cansleep(tse850->loop1, val);
858c2ecf20Sopenharmony_ci	tse850->loop1_cache = val;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	return snd_soc_dapm_put_enum_double(kctrl, ucontrol);
888c2ecf20Sopenharmony_ci}
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_cistatic int tse850_get_mux2(struct snd_kcontrol *kctrl,
918c2ecf20Sopenharmony_ci			   struct snd_ctl_elem_value *ucontrol)
928c2ecf20Sopenharmony_ci{
938c2ecf20Sopenharmony_ci	struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kctrl);
948c2ecf20Sopenharmony_ci	struct snd_soc_card *card = dapm->card;
958c2ecf20Sopenharmony_ci	struct tse850_priv *tse850 = snd_soc_card_get_drvdata(card);
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	ucontrol->value.enumerated.item[0] = tse850->loop2_cache;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	return 0;
1008c2ecf20Sopenharmony_ci}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_cistatic int tse850_put_mux2(struct snd_kcontrol *kctrl,
1038c2ecf20Sopenharmony_ci			   struct snd_ctl_elem_value *ucontrol)
1048c2ecf20Sopenharmony_ci{
1058c2ecf20Sopenharmony_ci	struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kctrl);
1068c2ecf20Sopenharmony_ci	struct snd_soc_card *card = dapm->card;
1078c2ecf20Sopenharmony_ci	struct tse850_priv *tse850 = snd_soc_card_get_drvdata(card);
1088c2ecf20Sopenharmony_ci	struct soc_enum *e = (struct soc_enum *)kctrl->private_value;
1098c2ecf20Sopenharmony_ci	unsigned int val = ucontrol->value.enumerated.item[0];
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	if (val >= e->items)
1128c2ecf20Sopenharmony_ci		return -EINVAL;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	gpiod_set_value_cansleep(tse850->loop2, val);
1158c2ecf20Sopenharmony_ci	tse850->loop2_cache = val;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	return snd_soc_dapm_put_enum_double(kctrl, ucontrol);
1188c2ecf20Sopenharmony_ci}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_cistatic int tse850_get_mix(struct snd_kcontrol *kctrl,
1218c2ecf20Sopenharmony_ci			  struct snd_ctl_elem_value *ucontrol)
1228c2ecf20Sopenharmony_ci{
1238c2ecf20Sopenharmony_ci	struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kctrl);
1248c2ecf20Sopenharmony_ci	struct snd_soc_card *card = dapm->card;
1258c2ecf20Sopenharmony_ci	struct tse850_priv *tse850 = snd_soc_card_get_drvdata(card);
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	ucontrol->value.enumerated.item[0] = tse850->add_cache;
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	return 0;
1308c2ecf20Sopenharmony_ci}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_cistatic int tse850_put_mix(struct snd_kcontrol *kctrl,
1338c2ecf20Sopenharmony_ci			  struct snd_ctl_elem_value *ucontrol)
1348c2ecf20Sopenharmony_ci{
1358c2ecf20Sopenharmony_ci	struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kctrl);
1368c2ecf20Sopenharmony_ci	struct snd_soc_card *card = dapm->card;
1378c2ecf20Sopenharmony_ci	struct tse850_priv *tse850 = snd_soc_card_get_drvdata(card);
1388c2ecf20Sopenharmony_ci	int connect = !!ucontrol->value.integer.value[0];
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	if (tse850->add_cache == connect)
1418c2ecf20Sopenharmony_ci		return 0;
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	/*
1448c2ecf20Sopenharmony_ci	 * Hmmm, this gpiod_set_value_cansleep call should probably happen
1458c2ecf20Sopenharmony_ci	 * inside snd_soc_dapm_mixer_update_power in the loop.
1468c2ecf20Sopenharmony_ci	 */
1478c2ecf20Sopenharmony_ci	gpiod_set_value_cansleep(tse850->add, connect);
1488c2ecf20Sopenharmony_ci	tse850->add_cache = connect;
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	snd_soc_dapm_mixer_update_power(dapm, kctrl, connect, NULL);
1518c2ecf20Sopenharmony_ci	return 1;
1528c2ecf20Sopenharmony_ci}
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_cistatic int tse850_get_ana(struct snd_kcontrol *kctrl,
1558c2ecf20Sopenharmony_ci			  struct snd_ctl_elem_value *ucontrol)
1568c2ecf20Sopenharmony_ci{
1578c2ecf20Sopenharmony_ci	struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kctrl);
1588c2ecf20Sopenharmony_ci	struct snd_soc_card *card = dapm->card;
1598c2ecf20Sopenharmony_ci	struct tse850_priv *tse850 = snd_soc_card_get_drvdata(card);
1608c2ecf20Sopenharmony_ci	int ret;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	ret = regulator_get_voltage(tse850->ana);
1638c2ecf20Sopenharmony_ci	if (ret < 0)
1648c2ecf20Sopenharmony_ci		return ret;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	/*
1678c2ecf20Sopenharmony_ci	 * Map regulator output values like so:
1688c2ecf20Sopenharmony_ci	 *      -11.5V to "Low" (enum 0)
1698c2ecf20Sopenharmony_ci	 * 11.5V-12.5V to "12V" (enum 1)
1708c2ecf20Sopenharmony_ci	 * 12.5V-13.5V to "13V" (enum 2)
1718c2ecf20Sopenharmony_ci	 *     ...
1728c2ecf20Sopenharmony_ci	 * 18.5V-19.5V to "19V" (enum 8)
1738c2ecf20Sopenharmony_ci	 * 19.5V-      to "20V" (enum 9)
1748c2ecf20Sopenharmony_ci	 */
1758c2ecf20Sopenharmony_ci	if (ret < 11000000)
1768c2ecf20Sopenharmony_ci		ret = 11000000;
1778c2ecf20Sopenharmony_ci	else if (ret > 20000000)
1788c2ecf20Sopenharmony_ci		ret = 20000000;
1798c2ecf20Sopenharmony_ci	ret -= 11000000;
1808c2ecf20Sopenharmony_ci	ret = (ret + 500000) / 1000000;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	ucontrol->value.enumerated.item[0] = ret;
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	return 0;
1858c2ecf20Sopenharmony_ci}
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_cistatic int tse850_put_ana(struct snd_kcontrol *kctrl,
1888c2ecf20Sopenharmony_ci			  struct snd_ctl_elem_value *ucontrol)
1898c2ecf20Sopenharmony_ci{
1908c2ecf20Sopenharmony_ci	struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kctrl);
1918c2ecf20Sopenharmony_ci	struct snd_soc_card *card = dapm->card;
1928c2ecf20Sopenharmony_ci	struct tse850_priv *tse850 = snd_soc_card_get_drvdata(card);
1938c2ecf20Sopenharmony_ci	struct soc_enum *e = (struct soc_enum *)kctrl->private_value;
1948c2ecf20Sopenharmony_ci	unsigned int uV = ucontrol->value.enumerated.item[0];
1958c2ecf20Sopenharmony_ci	int ret;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	if (uV >= e->items)
1988c2ecf20Sopenharmony_ci		return -EINVAL;
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	/*
2018c2ecf20Sopenharmony_ci	 * Map enum zero (Low) to 2 volts on the regulator, do this since
2028c2ecf20Sopenharmony_ci	 * the ana regulator is supplied by the system 12V voltage and
2038c2ecf20Sopenharmony_ci	 * requesting anything below the system voltage causes the system
2048c2ecf20Sopenharmony_ci	 * voltage to be passed through the regulator. Also, the ana
2058c2ecf20Sopenharmony_ci	 * regulator induces noise when requesting voltages near the
2068c2ecf20Sopenharmony_ci	 * system voltage. So, by mapping Low to 2V, that noise is
2078c2ecf20Sopenharmony_ci	 * eliminated when all that is needed is 12V (the system voltage).
2088c2ecf20Sopenharmony_ci	 */
2098c2ecf20Sopenharmony_ci	if (uV)
2108c2ecf20Sopenharmony_ci		uV = 11000000 + (1000000 * uV);
2118c2ecf20Sopenharmony_ci	else
2128c2ecf20Sopenharmony_ci		uV = 2000000;
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	ret = regulator_set_voltage(tse850->ana, uV, uV);
2158c2ecf20Sopenharmony_ci	if (ret < 0)
2168c2ecf20Sopenharmony_ci		return ret;
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	return snd_soc_dapm_put_enum_double(kctrl, ucontrol);
2198c2ecf20Sopenharmony_ci}
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_cistatic const char * const mux_text[] = { "Mixer", "Loop" };
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_cistatic const struct soc_enum mux_enum =
2248c2ecf20Sopenharmony_ci	SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(mux_text), mux_text);
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new mux1 =
2278c2ecf20Sopenharmony_ci	SOC_DAPM_ENUM_EXT("MUX1", mux_enum, tse850_get_mux1, tse850_put_mux1);
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new mux2 =
2308c2ecf20Sopenharmony_ci	SOC_DAPM_ENUM_EXT("MUX2", mux_enum, tse850_get_mux2, tse850_put_mux2);
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci#define TSE850_DAPM_SINGLE_EXT(xname, reg, shift, max, invert, xget, xput) \
2338c2ecf20Sopenharmony_ci{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
2348c2ecf20Sopenharmony_ci	.info = snd_soc_info_volsw, \
2358c2ecf20Sopenharmony_ci	.get = xget, \
2368c2ecf20Sopenharmony_ci	.put = xput, \
2378c2ecf20Sopenharmony_ci	.private_value = SOC_SINGLE_VALUE(reg, shift, max, invert, 0) }
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new mix[] = {
2408c2ecf20Sopenharmony_ci	TSE850_DAPM_SINGLE_EXT("IN Switch", SND_SOC_NOPM, 0, 1, 0,
2418c2ecf20Sopenharmony_ci			       tse850_get_mix, tse850_put_mix),
2428c2ecf20Sopenharmony_ci};
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_cistatic const char * const ana_text[] = {
2458c2ecf20Sopenharmony_ci	"Low", "12V", "13V", "14V", "15V", "16V", "17V", "18V", "19V", "20V"
2468c2ecf20Sopenharmony_ci};
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_cistatic const struct soc_enum ana_enum =
2498c2ecf20Sopenharmony_ci	SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(ana_text), ana_text);
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new out =
2528c2ecf20Sopenharmony_ci	SOC_DAPM_ENUM_EXT("ANA", ana_enum, tse850_get_ana, tse850_put_ana);
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_cistatic const struct snd_soc_dapm_widget tse850_dapm_widgets[] = {
2558c2ecf20Sopenharmony_ci	SND_SOC_DAPM_LINE("OUT1", NULL),
2568c2ecf20Sopenharmony_ci	SND_SOC_DAPM_LINE("OUT2", NULL),
2578c2ecf20Sopenharmony_ci	SND_SOC_DAPM_LINE("IN1", NULL),
2588c2ecf20Sopenharmony_ci	SND_SOC_DAPM_LINE("IN2", NULL),
2598c2ecf20Sopenharmony_ci	SND_SOC_DAPM_INPUT("DAC"),
2608c2ecf20Sopenharmony_ci	SND_SOC_DAPM_AIF_IN("AIFINL", "Playback", 0, SND_SOC_NOPM, 0, 0),
2618c2ecf20Sopenharmony_ci	SND_SOC_DAPM_AIF_IN("AIFINR", "Playback", 1, SND_SOC_NOPM, 0, 0),
2628c2ecf20Sopenharmony_ci	SOC_MIXER_ARRAY("MIX", SND_SOC_NOPM, 0, 0, mix),
2638c2ecf20Sopenharmony_ci	SND_SOC_DAPM_MUX("MUX1", SND_SOC_NOPM, 0, 0, &mux1),
2648c2ecf20Sopenharmony_ci	SND_SOC_DAPM_MUX("MUX2", SND_SOC_NOPM, 0, 0, &mux2),
2658c2ecf20Sopenharmony_ci	SND_SOC_DAPM_OUT_DRV("OUT", SND_SOC_NOPM, 0, 0, &out, 1),
2668c2ecf20Sopenharmony_ci};
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci/*
2698c2ecf20Sopenharmony_ci * These connections are not entirely correct, since both IN1 and IN2
2708c2ecf20Sopenharmony_ci * are always fed to MIX (if the "IN switch" is set so), i.e. without
2718c2ecf20Sopenharmony_ci * regard to the loop1 and loop2 relays that according to this only
2728c2ecf20Sopenharmony_ci * control MUX1 and MUX2 but in fact also control how the input signals
2738c2ecf20Sopenharmony_ci * are routed.
2748c2ecf20Sopenharmony_ci * But, 1) I don't know how to do it right, and 2) it doesn't seem to
2758c2ecf20Sopenharmony_ci * matter in practice since nothing is powered in those sections anyway.
2768c2ecf20Sopenharmony_ci */
2778c2ecf20Sopenharmony_cistatic const struct snd_soc_dapm_route tse850_intercon[] = {
2788c2ecf20Sopenharmony_ci	{ "OUT1", NULL, "MUX1" },
2798c2ecf20Sopenharmony_ci	{ "OUT2", NULL, "MUX2" },
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	{ "MUX1", "Loop",  "IN1" },
2828c2ecf20Sopenharmony_ci	{ "MUX1", "Mixer", "OUT" },
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	{ "MUX2", "Loop",  "IN2" },
2858c2ecf20Sopenharmony_ci	{ "MUX2", "Mixer", "OUT" },
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	{ "OUT", NULL, "MIX" },
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	{ "MIX", NULL, "DAC" },
2908c2ecf20Sopenharmony_ci	{ "MIX", "IN Switch", "IN1" },
2918c2ecf20Sopenharmony_ci	{ "MIX", "IN Switch", "IN2" },
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	/* connect board input to the codec left channel output pin */
2948c2ecf20Sopenharmony_ci	{ "DAC", NULL, "OUTL" },
2958c2ecf20Sopenharmony_ci};
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ciSND_SOC_DAILINK_DEFS(pcm,
2988c2ecf20Sopenharmony_ci	DAILINK_COMP_ARRAY(COMP_EMPTY()),
2998c2ecf20Sopenharmony_ci	DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "pcm512x-hifi")),
3008c2ecf20Sopenharmony_ci	DAILINK_COMP_ARRAY(COMP_EMPTY()));
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_cistatic struct snd_soc_dai_link tse850_dailink = {
3038c2ecf20Sopenharmony_ci	.name = "TSE-850",
3048c2ecf20Sopenharmony_ci	.stream_name = "TSE-850-PCM",
3058c2ecf20Sopenharmony_ci	.dai_fmt = SND_SOC_DAIFMT_I2S
3068c2ecf20Sopenharmony_ci		 | SND_SOC_DAIFMT_NB_NF
3078c2ecf20Sopenharmony_ci		 | SND_SOC_DAIFMT_CBM_CFS,
3088c2ecf20Sopenharmony_ci	SND_SOC_DAILINK_REG(pcm),
3098c2ecf20Sopenharmony_ci};
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_cistatic struct snd_soc_card tse850_card = {
3128c2ecf20Sopenharmony_ci	.name = "TSE-850-ASoC",
3138c2ecf20Sopenharmony_ci	.owner = THIS_MODULE,
3148c2ecf20Sopenharmony_ci	.dai_link = &tse850_dailink,
3158c2ecf20Sopenharmony_ci	.num_links = 1,
3168c2ecf20Sopenharmony_ci	.dapm_widgets = tse850_dapm_widgets,
3178c2ecf20Sopenharmony_ci	.num_dapm_widgets = ARRAY_SIZE(tse850_dapm_widgets),
3188c2ecf20Sopenharmony_ci	.dapm_routes = tse850_intercon,
3198c2ecf20Sopenharmony_ci	.num_dapm_routes = ARRAY_SIZE(tse850_intercon),
3208c2ecf20Sopenharmony_ci	.fully_routed = true,
3218c2ecf20Sopenharmony_ci};
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_cistatic int tse850_dt_init(struct platform_device *pdev)
3248c2ecf20Sopenharmony_ci{
3258c2ecf20Sopenharmony_ci	struct device_node *np = pdev->dev.of_node;
3268c2ecf20Sopenharmony_ci	struct device_node *codec_np, *cpu_np;
3278c2ecf20Sopenharmony_ci	struct snd_soc_dai_link *dailink = &tse850_dailink;
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	if (!np) {
3308c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "only device tree supported\n");
3318c2ecf20Sopenharmony_ci		return -EINVAL;
3328c2ecf20Sopenharmony_ci	}
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	cpu_np = of_parse_phandle(np, "axentia,cpu-dai", 0);
3358c2ecf20Sopenharmony_ci	if (!cpu_np) {
3368c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to get cpu dai\n");
3378c2ecf20Sopenharmony_ci		return -EINVAL;
3388c2ecf20Sopenharmony_ci	}
3398c2ecf20Sopenharmony_ci	dailink->cpus->of_node = cpu_np;
3408c2ecf20Sopenharmony_ci	dailink->platforms->of_node = cpu_np;
3418c2ecf20Sopenharmony_ci	of_node_put(cpu_np);
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	codec_np = of_parse_phandle(np, "axentia,audio-codec", 0);
3448c2ecf20Sopenharmony_ci	if (!codec_np) {
3458c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to get codec info\n");
3468c2ecf20Sopenharmony_ci		return -EINVAL;
3478c2ecf20Sopenharmony_ci	}
3488c2ecf20Sopenharmony_ci	dailink->codecs->of_node = codec_np;
3498c2ecf20Sopenharmony_ci	of_node_put(codec_np);
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	return 0;
3528c2ecf20Sopenharmony_ci}
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_cistatic int tse850_probe(struct platform_device *pdev)
3558c2ecf20Sopenharmony_ci{
3568c2ecf20Sopenharmony_ci	struct snd_soc_card *card = &tse850_card;
3578c2ecf20Sopenharmony_ci	struct device *dev = card->dev = &pdev->dev;
3588c2ecf20Sopenharmony_ci	struct tse850_priv *tse850;
3598c2ecf20Sopenharmony_ci	int ret;
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	tse850 = devm_kzalloc(dev, sizeof(*tse850), GFP_KERNEL);
3628c2ecf20Sopenharmony_ci	if (!tse850)
3638c2ecf20Sopenharmony_ci		return -ENOMEM;
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	snd_soc_card_set_drvdata(card, tse850);
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	ret = tse850_dt_init(pdev);
3688c2ecf20Sopenharmony_ci	if (ret) {
3698c2ecf20Sopenharmony_ci		dev_err(dev, "failed to init dt info\n");
3708c2ecf20Sopenharmony_ci		return ret;
3718c2ecf20Sopenharmony_ci	}
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	tse850->add = devm_gpiod_get(dev, "axentia,add", GPIOD_OUT_HIGH);
3748c2ecf20Sopenharmony_ci	if (IS_ERR(tse850->add)) {
3758c2ecf20Sopenharmony_ci		if (PTR_ERR(tse850->add) != -EPROBE_DEFER)
3768c2ecf20Sopenharmony_ci			dev_err(dev, "failed to get 'add' gpio\n");
3778c2ecf20Sopenharmony_ci		return PTR_ERR(tse850->add);
3788c2ecf20Sopenharmony_ci	}
3798c2ecf20Sopenharmony_ci	tse850->add_cache = 1;
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	tse850->loop1 = devm_gpiod_get(dev, "axentia,loop1", GPIOD_OUT_HIGH);
3828c2ecf20Sopenharmony_ci	if (IS_ERR(tse850->loop1)) {
3838c2ecf20Sopenharmony_ci		if (PTR_ERR(tse850->loop1) != -EPROBE_DEFER)
3848c2ecf20Sopenharmony_ci			dev_err(dev, "failed to get 'loop1' gpio\n");
3858c2ecf20Sopenharmony_ci		return PTR_ERR(tse850->loop1);
3868c2ecf20Sopenharmony_ci	}
3878c2ecf20Sopenharmony_ci	tse850->loop1_cache = 1;
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	tse850->loop2 = devm_gpiod_get(dev, "axentia,loop2", GPIOD_OUT_HIGH);
3908c2ecf20Sopenharmony_ci	if (IS_ERR(tse850->loop2)) {
3918c2ecf20Sopenharmony_ci		if (PTR_ERR(tse850->loop2) != -EPROBE_DEFER)
3928c2ecf20Sopenharmony_ci			dev_err(dev, "failed to get 'loop2' gpio\n");
3938c2ecf20Sopenharmony_ci		return PTR_ERR(tse850->loop2);
3948c2ecf20Sopenharmony_ci	}
3958c2ecf20Sopenharmony_ci	tse850->loop2_cache = 1;
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	tse850->ana = devm_regulator_get(dev, "axentia,ana");
3988c2ecf20Sopenharmony_ci	if (IS_ERR(tse850->ana)) {
3998c2ecf20Sopenharmony_ci		if (PTR_ERR(tse850->ana) != -EPROBE_DEFER)
4008c2ecf20Sopenharmony_ci			dev_err(dev, "failed to get 'ana' regulator\n");
4018c2ecf20Sopenharmony_ci		return PTR_ERR(tse850->ana);
4028c2ecf20Sopenharmony_ci	}
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	ret = regulator_enable(tse850->ana);
4058c2ecf20Sopenharmony_ci	if (ret < 0) {
4068c2ecf20Sopenharmony_ci		dev_err(dev, "failed to enable the 'ana' regulator\n");
4078c2ecf20Sopenharmony_ci		return ret;
4088c2ecf20Sopenharmony_ci	}
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	ret = snd_soc_register_card(card);
4118c2ecf20Sopenharmony_ci	if (ret) {
4128c2ecf20Sopenharmony_ci		dev_err(dev, "snd_soc_register_card failed\n");
4138c2ecf20Sopenharmony_ci		goto err_disable_ana;
4148c2ecf20Sopenharmony_ci	}
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	return 0;
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_cierr_disable_ana:
4198c2ecf20Sopenharmony_ci	regulator_disable(tse850->ana);
4208c2ecf20Sopenharmony_ci	return ret;
4218c2ecf20Sopenharmony_ci}
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_cistatic int tse850_remove(struct platform_device *pdev)
4248c2ecf20Sopenharmony_ci{
4258c2ecf20Sopenharmony_ci	struct snd_soc_card *card = platform_get_drvdata(pdev);
4268c2ecf20Sopenharmony_ci	struct tse850_priv *tse850 = snd_soc_card_get_drvdata(card);
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	snd_soc_unregister_card(card);
4298c2ecf20Sopenharmony_ci	regulator_disable(tse850->ana);
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	return 0;
4328c2ecf20Sopenharmony_ci}
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_cistatic const struct of_device_id tse850_dt_ids[] = {
4358c2ecf20Sopenharmony_ci	{ .compatible = "axentia,tse850-pcm5142", },
4368c2ecf20Sopenharmony_ci	{ /* sentinel */ }
4378c2ecf20Sopenharmony_ci};
4388c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, tse850_dt_ids);
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_cistatic struct platform_driver tse850_driver = {
4418c2ecf20Sopenharmony_ci	.driver = {
4428c2ecf20Sopenharmony_ci		.name = "axentia-tse850-pcm5142",
4438c2ecf20Sopenharmony_ci		.of_match_table = of_match_ptr(tse850_dt_ids),
4448c2ecf20Sopenharmony_ci	},
4458c2ecf20Sopenharmony_ci	.probe = tse850_probe,
4468c2ecf20Sopenharmony_ci	.remove = tse850_remove,
4478c2ecf20Sopenharmony_ci};
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_cimodule_platform_driver(tse850_driver);
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci/* Module information */
4528c2ecf20Sopenharmony_ciMODULE_AUTHOR("Peter Rosin <peda@axentia.se>");
4538c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ALSA SoC driver for TSE-850 with PCM5142 codec");
4548c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
455