Commit 246f388e authored by Mark Brown's avatar Mark Brown

sc7180: Add qdsp based soundcard

Merge series from Nikita Travkin <nikita@trvn.ru>:

Some devices, such as Acer Aspire 1, can't use lpass dirrectly, but
instead must use adsp core to play sound. Since otherwise the hardware
is, usually, very similar across the devices on the same platform, it
makes sense to reuse the same boardfile.

This series refactors the sc7180.c slightly and adds the functions to
control clocks via adsp instead of controlling the hardware directly.
parents 11817547 4531f512
......@@ -25,6 +25,7 @@ properties:
- qcom,apq8016-sbc-sndcard
- qcom,msm8916-qdsp6-sndcard
- qcom,qrb5165-rb5-sndcard
- qcom,sc7180-qdsp6-sndcard
- qcom,sc8280xp-sndcard
- qcom,sdm845-sndcard
- qcom,sm8250-sndcard
......
......@@ -5,6 +5,7 @@
// 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>
......@@ -19,8 +20,10 @@
#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"
......@@ -133,12 +136,28 @@ static int sc7180_init(struct snd_soc_pcm_runtime *rtd)
return 0;
}
static int sc7180_snd_startup(struct snd_pcm_substream *substream)
static int sc7180_qdsp_init(struct snd_soc_pcm_runtime *rtd)
{
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:
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;
......@@ -154,17 +173,9 @@ static int sc7180_snd_startup(struct snd_pcm_substream *substream)
clk_id = RT5682S_SCLK_S_PLL2;
pll_out = RT5682_PLL1_FREQ;
pll_in = DEFAULT_MCLK_RATE;
} else {
return 0;
}
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);
}
snd_soc_dai_set_fmt(codec_dai,
SND_SOC_DAIFMT_BC_FC |
SND_SOC_DAIFMT_NB_NF |
......@@ -185,6 +196,30 @@ static int sc7180_snd_startup(struct snd_pcm_substream *substream)
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;
......@@ -198,6 +233,58 @@ static int sc7180_snd_startup(struct snd_pcm_substream *substream)
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)
{
......@@ -246,6 +333,42 @@ static void sc7180_snd_shutdown(struct snd_pcm_substream *substream)
}
}
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);
......@@ -294,11 +417,30 @@ static int sc7180_adau7002_snd_startup(struct snd_pcm_substream *substream)
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,
};
......@@ -354,7 +496,7 @@ static int sc7180_snd_platform_probe(struct platform_device *pdev)
struct snd_soc_dai_link *link;
int ret;
int i;
bool no_headphone = false;
bool qdsp = false, no_headphone = false;
/* Allocate the private data */
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
......@@ -390,6 +532,8 @@ static int sc7180_snd_platform_probe(struct platform_device *pdev)
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);
......@@ -400,6 +544,12 @@ static int sc7180_snd_platform_probe(struct platform_device *pdev)
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;
......@@ -412,6 +562,7 @@ static int sc7180_snd_platform_probe(struct platform_device *pdev)
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);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment