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