Some sc7180 devices use audio adsp to play sound. The setup for this adsp is similar to the dirrect lpass usage but requires the use of different link ids and clocks. This commit adds support for the qdsp based audio, reusing the common parts like audio codec setup and jack creation. Since the setup is mostly generic and codec specific setup is guarded behind a check, a generic compatible is added, similar to other platforms. Even though those changes target Acer Aspire 1 as the only user of the adsp audio on this platform present upstream at the moment of the commit, those changes should be either dirrectly compatible or trivially expandable to the other devices that will be added in the future. Signed-off-by: Nikita Travkin <nikita@trvn.ru> Link: https://lore.kernel.org/r/20231020-sc7180-qdsp-sndcard-v1-2-157706b7d06f@trvn.ru Signed-off-by: Mark Brown <broonie@kernel.org>
582 lines
15 KiB
C
582 lines
15 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
//
|
|
// Copyright (c) 2020, The Linux Foundation. All rights reserved.
|
|
//
|
|
// sc7180.c -- ALSA SoC Machine driver for SC7180
|
|
|
|
#include <dt-bindings/sound/sc7180-lpass.h>
|
|
#include <dt-bindings/sound/qcom,q6afe.h>
|
|
#include <linux/gpio.h>
|
|
#include <linux/gpio/consumer.h>
|
|
#include <linux/module.h>
|
|
#include <linux/of.h>
|
|
#include <linux/platform_device.h>
|
|
#include <sound/core.h>
|
|
#include <sound/jack.h>
|
|
#include <sound/pcm.h>
|
|
#include <sound/soc.h>
|
|
#include <uapi/linux/input-event-codes.h>
|
|
|
|
#include "../codecs/rt5682.h"
|
|
#include "../codecs/rt5682s.h"
|
|
#include "common.h"
|
|
#include "qdsp6/q6afe.h"
|
|
|
|
#define DEFAULT_MCLK_RATE 19200000
|
|
#define MI2S_BCLK_RATE 1536000
|
|
#define RT5682_PLL1_FREQ (48000 * 512)
|
|
|
|
#define DRIVER_NAME "SC7180"
|
|
|
|
struct sc7180_snd_data {
|
|
struct snd_soc_card card;
|
|
u32 pri_mi2s_clk_count;
|
|
struct snd_soc_jack hs_jack;
|
|
struct snd_soc_jack hdmi_jack;
|
|
struct gpio_desc *dmic_sel;
|
|
int dmic_switch;
|
|
};
|
|
|
|
static void sc7180_jack_free(struct snd_jack *jack)
|
|
{
|
|
struct snd_soc_component *component = jack->private_data;
|
|
|
|
snd_soc_component_set_jack(component, NULL, NULL);
|
|
}
|
|
|
|
static struct snd_soc_jack_pin sc7180_jack_pins[] = {
|
|
{
|
|
.pin = "Headphone Jack",
|
|
.mask = SND_JACK_HEADPHONE,
|
|
},
|
|
{
|
|
.pin = "Headset Mic",
|
|
.mask = SND_JACK_MICROPHONE,
|
|
},
|
|
};
|
|
|
|
static int sc7180_headset_init(struct snd_soc_pcm_runtime *rtd)
|
|
{
|
|
struct snd_soc_card *card = rtd->card;
|
|
struct sc7180_snd_data *pdata = snd_soc_card_get_drvdata(card);
|
|
struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
|
|
struct snd_soc_component *component = codec_dai->component;
|
|
struct snd_jack *jack;
|
|
int rval;
|
|
|
|
rval = snd_soc_card_jack_new_pins(card, "Headset Jack",
|
|
SND_JACK_HEADSET |
|
|
SND_JACK_HEADPHONE |
|
|
SND_JACK_BTN_0 | SND_JACK_BTN_1 |
|
|
SND_JACK_BTN_2 | SND_JACK_BTN_3,
|
|
&pdata->hs_jack,
|
|
sc7180_jack_pins,
|
|
ARRAY_SIZE(sc7180_jack_pins));
|
|
|
|
if (rval < 0) {
|
|
dev_err(card->dev, "Unable to add Headset Jack\n");
|
|
return rval;
|
|
}
|
|
|
|
jack = pdata->hs_jack.jack;
|
|
|
|
snd_jack_set_key(jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
|
|
snd_jack_set_key(jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);
|
|
snd_jack_set_key(jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
|
|
snd_jack_set_key(jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);
|
|
|
|
jack->private_data = component;
|
|
jack->private_free = sc7180_jack_free;
|
|
|
|
return snd_soc_component_set_jack(component, &pdata->hs_jack, NULL);
|
|
}
|
|
|
|
static int sc7180_hdmi_init(struct snd_soc_pcm_runtime *rtd)
|
|
{
|
|
struct snd_soc_card *card = rtd->card;
|
|
struct sc7180_snd_data *pdata = snd_soc_card_get_drvdata(card);
|
|
struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
|
|
struct snd_soc_component *component = codec_dai->component;
|
|
struct snd_jack *jack;
|
|
int rval;
|
|
|
|
rval = snd_soc_card_jack_new(
|
|
card, "HDMI Jack",
|
|
SND_JACK_LINEOUT,
|
|
&pdata->hdmi_jack);
|
|
|
|
if (rval < 0) {
|
|
dev_err(card->dev, "Unable to add HDMI Jack\n");
|
|
return rval;
|
|
}
|
|
|
|
jack = pdata->hdmi_jack.jack;
|
|
jack->private_data = component;
|
|
jack->private_free = sc7180_jack_free;
|
|
|
|
return snd_soc_component_set_jack(component, &pdata->hdmi_jack, NULL);
|
|
}
|
|
|
|
static int sc7180_init(struct snd_soc_pcm_runtime *rtd)
|
|
{
|
|
struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
|
|
|
|
switch (cpu_dai->id) {
|
|
case MI2S_PRIMARY:
|
|
return sc7180_headset_init(rtd);
|
|
case MI2S_SECONDARY:
|
|
return 0;
|
|
case LPASS_DP_RX:
|
|
return sc7180_hdmi_init(rtd);
|
|
default:
|
|
dev_err(rtd->dev, "%s: invalid dai id 0x%x\n", __func__,
|
|
cpu_dai->id);
|
|
return -EINVAL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int sc7180_qdsp_init(struct snd_soc_pcm_runtime *rtd)
|
|
{
|
|
struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
|
|
|
|
switch (cpu_dai->id) {
|
|
case PRIMARY_MI2S_RX:
|
|
return sc7180_headset_init(rtd);
|
|
case PRIMARY_MI2S_TX:
|
|
case TERTIARY_MI2S_RX:
|
|
return 0;
|
|
case DISPLAY_PORT_RX:
|
|
return sc7180_hdmi_init(rtd);
|
|
default:
|
|
dev_err(rtd->dev, "%s: invalid dai id 0x%x\n", __func__,
|
|
cpu_dai->id);
|
|
return -EINVAL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int sc7180_startup_realtek_codec(struct snd_soc_pcm_runtime *rtd)
|
|
{
|
|
struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
|
|
int pll_id, pll_source, pll_in, pll_out, clk_id, ret;
|
|
|
|
if (!strcmp(codec_dai->name, "rt5682-aif1")) {
|
|
pll_source = RT5682_PLL1_S_MCLK;
|
|
pll_id = 0;
|
|
clk_id = RT5682_SCLK_S_PLL1;
|
|
pll_out = RT5682_PLL1_FREQ;
|
|
pll_in = DEFAULT_MCLK_RATE;
|
|
} else if (!strcmp(codec_dai->name, "rt5682s-aif1")) {
|
|
pll_source = RT5682S_PLL_S_MCLK;
|
|
pll_id = RT5682S_PLL2;
|
|
clk_id = RT5682S_SCLK_S_PLL2;
|
|
pll_out = RT5682_PLL1_FREQ;
|
|
pll_in = DEFAULT_MCLK_RATE;
|
|
} else {
|
|
return 0;
|
|
}
|
|
snd_soc_dai_set_fmt(codec_dai,
|
|
SND_SOC_DAIFMT_BC_FC |
|
|
SND_SOC_DAIFMT_NB_NF |
|
|
SND_SOC_DAIFMT_I2S);
|
|
|
|
/* Configure PLL1 for codec */
|
|
ret = snd_soc_dai_set_pll(codec_dai, pll_id, pll_source,
|
|
pll_in, pll_out);
|
|
if (ret) {
|
|
dev_err(rtd->dev, "can't set codec pll: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
/* Configure sysclk for codec */
|
|
ret = snd_soc_dai_set_sysclk(codec_dai, clk_id, pll_out,
|
|
SND_SOC_CLOCK_IN);
|
|
if (ret)
|
|
dev_err(rtd->dev, "snd_soc_dai_set_sysclk err = %d\n",
|
|
ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int sc7180_snd_startup(struct snd_pcm_substream *substream)
|
|
{
|
|
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
|
struct snd_soc_card *card = rtd->card;
|
|
struct sc7180_snd_data *data = snd_soc_card_get_drvdata(card);
|
|
struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
|
|
int ret;
|
|
|
|
switch (cpu_dai->id) {
|
|
case MI2S_PRIMARY:
|
|
if (++data->pri_mi2s_clk_count == 1) {
|
|
snd_soc_dai_set_sysclk(cpu_dai,
|
|
LPASS_MCLK0,
|
|
DEFAULT_MCLK_RATE,
|
|
SNDRV_PCM_STREAM_PLAYBACK);
|
|
}
|
|
|
|
ret = sc7180_startup_realtek_codec(rtd);
|
|
if (ret)
|
|
return ret;
|
|
|
|
break;
|
|
case MI2S_SECONDARY:
|
|
break;
|
|
case LPASS_DP_RX:
|
|
break;
|
|
default:
|
|
dev_err(rtd->dev, "%s: invalid dai id 0x%x\n", __func__,
|
|
cpu_dai->id);
|
|
return -EINVAL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int sc7180_qdsp_snd_startup(struct snd_pcm_substream *substream)
|
|
{
|
|
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
|
struct snd_soc_card *card = rtd->card;
|
|
struct sc7180_snd_data *data = snd_soc_card_get_drvdata(card);
|
|
struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
|
|
struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
|
|
int ret;
|
|
|
|
switch (cpu_dai->id) {
|
|
case PRIMARY_MI2S_RX:
|
|
case PRIMARY_MI2S_TX:
|
|
if (++data->pri_mi2s_clk_count == 1) {
|
|
snd_soc_dai_set_sysclk(cpu_dai,
|
|
Q6AFE_LPASS_CLK_ID_MCLK_1,
|
|
DEFAULT_MCLK_RATE,
|
|
SNDRV_PCM_STREAM_PLAYBACK);
|
|
snd_soc_dai_set_sysclk(cpu_dai,
|
|
Q6AFE_LPASS_CLK_ID_PRI_MI2S_IBIT,
|
|
MI2S_BCLK_RATE,
|
|
SNDRV_PCM_STREAM_PLAYBACK);
|
|
}
|
|
|
|
snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_BP_FP);
|
|
|
|
ret = sc7180_startup_realtek_codec(rtd);
|
|
if (ret)
|
|
return ret;
|
|
|
|
break;
|
|
case TERTIARY_MI2S_RX:
|
|
snd_soc_dai_set_sysclk(cpu_dai,
|
|
Q6AFE_LPASS_CLK_ID_TER_MI2S_IBIT,
|
|
MI2S_BCLK_RATE,
|
|
SNDRV_PCM_STREAM_PLAYBACK);
|
|
|
|
snd_soc_dai_set_fmt(codec_dai,
|
|
SND_SOC_DAIFMT_BC_FC |
|
|
SND_SOC_DAIFMT_NB_NF |
|
|
SND_SOC_DAIFMT_I2S);
|
|
snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_BP_FP);
|
|
break;
|
|
case DISPLAY_PORT_RX:
|
|
break;
|
|
default:
|
|
dev_err(rtd->dev, "%s: invalid dai id 0x%x\n", __func__,
|
|
cpu_dai->id);
|
|
return -EINVAL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int dmic_get(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol);
|
|
struct sc7180_snd_data *data = snd_soc_card_get_drvdata(dapm->card);
|
|
|
|
ucontrol->value.integer.value[0] = data->dmic_switch;
|
|
return 0;
|
|
}
|
|
|
|
static int dmic_set(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol);
|
|
struct sc7180_snd_data *data = snd_soc_card_get_drvdata(dapm->card);
|
|
|
|
data->dmic_switch = ucontrol->value.integer.value[0];
|
|
gpiod_set_value(data->dmic_sel, data->dmic_switch);
|
|
return 0;
|
|
}
|
|
|
|
static void sc7180_snd_shutdown(struct snd_pcm_substream *substream)
|
|
{
|
|
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
|
struct snd_soc_card *card = rtd->card;
|
|
struct sc7180_snd_data *data = snd_soc_card_get_drvdata(card);
|
|
struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
|
|
|
|
switch (cpu_dai->id) {
|
|
case MI2S_PRIMARY:
|
|
if (--data->pri_mi2s_clk_count == 0) {
|
|
snd_soc_dai_set_sysclk(cpu_dai,
|
|
LPASS_MCLK0,
|
|
0,
|
|
SNDRV_PCM_STREAM_PLAYBACK);
|
|
}
|
|
break;
|
|
case MI2S_SECONDARY:
|
|
break;
|
|
case LPASS_DP_RX:
|
|
break;
|
|
default:
|
|
dev_err(rtd->dev, "%s: invalid dai id 0x%x\n", __func__,
|
|
cpu_dai->id);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void sc7180_qdsp_snd_shutdown(struct snd_pcm_substream *substream)
|
|
{
|
|
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
|
struct snd_soc_card *card = rtd->card;
|
|
struct sc7180_snd_data *data = snd_soc_card_get_drvdata(card);
|
|
struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
|
|
|
|
switch (cpu_dai->id) {
|
|
case PRIMARY_MI2S_RX:
|
|
case PRIMARY_MI2S_TX:
|
|
if (--data->pri_mi2s_clk_count == 0) {
|
|
snd_soc_dai_set_sysclk(cpu_dai,
|
|
Q6AFE_LPASS_CLK_ID_MCLK_1,
|
|
0,
|
|
SNDRV_PCM_STREAM_PLAYBACK);
|
|
snd_soc_dai_set_sysclk(cpu_dai,
|
|
Q6AFE_LPASS_CLK_ID_PRI_MI2S_IBIT,
|
|
0,
|
|
SNDRV_PCM_STREAM_PLAYBACK);
|
|
}
|
|
break;
|
|
case TERTIARY_MI2S_RX:
|
|
snd_soc_dai_set_sysclk(cpu_dai,
|
|
Q6AFE_LPASS_CLK_ID_TER_MI2S_IBIT,
|
|
0,
|
|
SNDRV_PCM_STREAM_PLAYBACK);
|
|
break;
|
|
case DISPLAY_PORT_RX:
|
|
break;
|
|
default:
|
|
dev_err(rtd->dev, "%s: invalid dai id 0x%x\n", __func__,
|
|
cpu_dai->id);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static int sc7180_adau7002_init(struct snd_soc_pcm_runtime *rtd)
|
|
{
|
|
struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
|
|
|
|
switch (cpu_dai->id) {
|
|
case MI2S_PRIMARY:
|
|
return 0;
|
|
case MI2S_SECONDARY:
|
|
return 0;
|
|
case LPASS_DP_RX:
|
|
return sc7180_hdmi_init(rtd);
|
|
default:
|
|
dev_err(rtd->dev, "%s: invalid dai id 0x%x\n", __func__,
|
|
cpu_dai->id);
|
|
return -EINVAL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int sc7180_adau7002_snd_startup(struct snd_pcm_substream *substream)
|
|
{
|
|
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
|
struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
|
|
struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
|
|
struct snd_pcm_runtime *runtime = substream->runtime;
|
|
|
|
switch (cpu_dai->id) {
|
|
case MI2S_PRIMARY:
|
|
snd_soc_dai_set_fmt(codec_dai,
|
|
SND_SOC_DAIFMT_CBS_CFS |
|
|
SND_SOC_DAIFMT_NB_NF |
|
|
SND_SOC_DAIFMT_I2S);
|
|
runtime->hw.formats = SNDRV_PCM_FMTBIT_S32_LE;
|
|
snd_pcm_hw_constraint_msbits(runtime, 0, 32, 32);
|
|
|
|
break;
|
|
case MI2S_SECONDARY:
|
|
break;
|
|
case LPASS_DP_RX:
|
|
break;
|
|
default:
|
|
dev_err(rtd->dev, "%s: invalid dai id 0x%x\n", __func__,
|
|
cpu_dai->id);
|
|
return -EINVAL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int sc7180_qdsp_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
|
|
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);
|
|
|
|
rate->min = rate->max = 48000;
|
|
channels->min = channels->max = 2;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct snd_soc_ops sc7180_ops = {
|
|
.startup = sc7180_snd_startup,
|
|
.shutdown = sc7180_snd_shutdown,
|
|
};
|
|
|
|
static const struct snd_soc_ops sc7180_qdsp_ops = {
|
|
.startup = sc7180_qdsp_snd_startup,
|
|
.shutdown = sc7180_qdsp_snd_shutdown,
|
|
};
|
|
|
|
static const struct snd_soc_ops sc7180_adau7002_ops = {
|
|
.startup = sc7180_adau7002_snd_startup,
|
|
};
|
|
|
|
static const struct snd_soc_dapm_widget sc7180_snd_widgets[] = {
|
|
SND_SOC_DAPM_HP("Headphone Jack", NULL),
|
|
SND_SOC_DAPM_MIC("Headset Mic", NULL),
|
|
};
|
|
|
|
static const struct snd_kcontrol_new sc7180_snd_controls[] = {
|
|
SOC_DAPM_PIN_SWITCH("Headphone Jack"),
|
|
SOC_DAPM_PIN_SWITCH("Headset Mic"),
|
|
};
|
|
|
|
static const struct snd_soc_dapm_widget sc7180_adau7002_snd_widgets[] = {
|
|
SND_SOC_DAPM_MIC("DMIC", NULL),
|
|
};
|
|
|
|
static const char * const dmic_mux_text[] = {
|
|
"Front Mic",
|
|
"Rear Mic",
|
|
};
|
|
|
|
static SOC_ENUM_SINGLE_DECL(sc7180_dmic_enum,
|
|
SND_SOC_NOPM, 0, dmic_mux_text);
|
|
|
|
static const struct snd_kcontrol_new sc7180_dmic_mux_control =
|
|
SOC_DAPM_ENUM_EXT("DMIC Select Mux", sc7180_dmic_enum,
|
|
dmic_get, dmic_set);
|
|
|
|
static const struct snd_soc_dapm_widget sc7180_snd_dual_mic_widgets[] = {
|
|
SND_SOC_DAPM_HP("Headphone Jack", NULL),
|
|
SND_SOC_DAPM_MIC("Headset Mic", NULL),
|
|
SND_SOC_DAPM_MIC("DMIC", NULL),
|
|
SND_SOC_DAPM_MUX("Dmic Mux", SND_SOC_NOPM, 0, 0, &sc7180_dmic_mux_control),
|
|
};
|
|
|
|
static const struct snd_kcontrol_new sc7180_snd_dual_mic_controls[] = {
|
|
SOC_DAPM_PIN_SWITCH("Headphone Jack"),
|
|
SOC_DAPM_PIN_SWITCH("Headset Mic"),
|
|
};
|
|
|
|
static const struct snd_soc_dapm_route sc7180_snd_dual_mic_audio_route[] = {
|
|
{"Dmic Mux", "Front Mic", "DMIC"},
|
|
{"Dmic Mux", "Rear Mic", "DMIC"},
|
|
};
|
|
|
|
static int sc7180_snd_platform_probe(struct platform_device *pdev)
|
|
{
|
|
struct snd_soc_card *card;
|
|
struct sc7180_snd_data *data;
|
|
struct device *dev = &pdev->dev;
|
|
struct snd_soc_dai_link *link;
|
|
int ret;
|
|
int i;
|
|
bool qdsp = false, no_headphone = false;
|
|
|
|
/* Allocate the private data */
|
|
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
|
|
if (!data)
|
|
return -ENOMEM;
|
|
|
|
card = &data->card;
|
|
snd_soc_card_set_drvdata(card, data);
|
|
|
|
card->owner = THIS_MODULE;
|
|
card->driver_name = DRIVER_NAME;
|
|
card->dev = dev;
|
|
card->dapm_widgets = sc7180_snd_widgets;
|
|
card->num_dapm_widgets = ARRAY_SIZE(sc7180_snd_widgets);
|
|
card->controls = sc7180_snd_controls;
|
|
card->num_controls = ARRAY_SIZE(sc7180_snd_controls);
|
|
|
|
if (of_property_read_bool(dev->of_node, "dmic-gpios")) {
|
|
card->dapm_widgets = sc7180_snd_dual_mic_widgets,
|
|
card->num_dapm_widgets = ARRAY_SIZE(sc7180_snd_dual_mic_widgets),
|
|
card->controls = sc7180_snd_dual_mic_controls,
|
|
card->num_controls = ARRAY_SIZE(sc7180_snd_dual_mic_controls),
|
|
card->dapm_routes = sc7180_snd_dual_mic_audio_route,
|
|
card->num_dapm_routes = ARRAY_SIZE(sc7180_snd_dual_mic_audio_route),
|
|
data->dmic_sel = devm_gpiod_get(&pdev->dev, "dmic", GPIOD_OUT_LOW);
|
|
if (IS_ERR(data->dmic_sel)) {
|
|
dev_err(&pdev->dev, "DMIC gpio failed err=%ld\n", PTR_ERR(data->dmic_sel));
|
|
return PTR_ERR(data->dmic_sel);
|
|
}
|
|
}
|
|
|
|
if (of_device_is_compatible(dev->of_node, "google,sc7180-coachz")) {
|
|
no_headphone = true;
|
|
card->dapm_widgets = sc7180_adau7002_snd_widgets;
|
|
card->num_dapm_widgets = ARRAY_SIZE(sc7180_adau7002_snd_widgets);
|
|
} else if (of_device_is_compatible(dev->of_node, "qcom,sc7180-qdsp6-sndcard")) {
|
|
qdsp = true;
|
|
}
|
|
|
|
ret = qcom_snd_parse_of(card);
|
|
if (ret)
|
|
return ret;
|
|
|
|
for_each_card_prelinks(card, i, link) {
|
|
if (no_headphone) {
|
|
link->ops = &sc7180_adau7002_ops;
|
|
link->init = sc7180_adau7002_init;
|
|
} else if (qdsp) {
|
|
if (link->no_pcm == 1) {
|
|
link->ops = &sc7180_qdsp_ops;
|
|
link->be_hw_params_fixup = sc7180_qdsp_be_hw_params_fixup;
|
|
link->init = sc7180_qdsp_init;
|
|
}
|
|
} else {
|
|
link->ops = &sc7180_ops;
|
|
link->init = sc7180_init;
|
|
}
|
|
}
|
|
|
|
return devm_snd_soc_register_card(dev, card);
|
|
}
|
|
|
|
static const struct of_device_id sc7180_snd_device_id[] = {
|
|
{.compatible = "google,sc7180-trogdor"},
|
|
{.compatible = "google,sc7180-coachz"},
|
|
{.compatible = "qcom,sc7180-qdsp6-sndcard"},
|
|
{},
|
|
};
|
|
MODULE_DEVICE_TABLE(of, sc7180_snd_device_id);
|
|
|
|
static struct platform_driver sc7180_snd_driver = {
|
|
.probe = sc7180_snd_platform_probe,
|
|
.driver = {
|
|
.name = "msm-snd-sc7180",
|
|
.of_match_table = sc7180_snd_device_id,
|
|
.pm = &snd_soc_pm_ops,
|
|
},
|
|
};
|
|
module_platform_driver(sc7180_snd_driver);
|
|
|
|
MODULE_DESCRIPTION("sc7180 ASoC Machine Driver");
|
|
MODULE_LICENSE("GPL v2");
|