diff --git a/audioreach-driver/audioreach_common.c b/audioreach-driver/audioreach_common.c index 523b8bd..aa37289 100644 --- a/audioreach-driver/audioreach_common.c +++ b/audioreach-driver/audioreach_common.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -17,26 +18,133 @@ #define AFE_PORT_MAX 137 #define NAME_SIZE 32 +#define I2S_MCLKFS 256 + +#define I2S_MCLK_RATE(rate) \ + ((rate) * (I2S_MCLKFS)) +#define I2S_BIT_RATE(rate, channels, format) \ + ((rate) * (channels) * (format)) + +struct snd_soc_common { + const char *driver_name; + const struct snd_soc_dapm_widget *dapm_widgets; + int num_dapm_widgets; + const struct snd_soc_dapm_route *dapm_routes; + int num_dapm_routes; + const struct snd_kcontrol_new *controls; + int num_controls; + unsigned int codec_dai_fmt; + int num_wsa_spkr; + bool codec_sysclk_set; + bool mi2s_mclk_enable; + bool mi2s_bclk_enable; + bool wcd_jack; +}; + struct qcs6490_snd_data { bool stream_prepared[AFE_PORT_MAX]; struct snd_soc_card *card; struct sdw_stream_runtime *sruntime[AFE_PORT_MAX]; struct snd_soc_jack jack; struct snd_soc_jack dp_jack[8]; + struct snd_soc_common *snd_soc_common_priv; bool jack_setup; }; -struct qcom_snd_dailink_data { - u32 mclk_fs; - u32 mclk_id; - u32 clk_direction; -}; - struct qcom_snd_common_data { struct qcom_snd_dailink_data *link_data; }; -static const struct snd_soc_dapm_widget qcom_jack_snd_widgets[] = { +static struct snd_soc_jack_pin qcs6490_headset_jack_pins[] = { + /* Headset */ + { + .pin = "Mic Jack", + .mask = SND_JACK_MICROPHONE, + }, + { + .pin = "Headphone Jack", + .mask = SND_JACK_HEADPHONE, + }, +}; + +static char *qcs6490_wsa_spk_to_prefix(const char *spk_name) +{ + if (!strcmp(spk_name, "WooferLeft Speaker")) + return "WooferLeft"; + if (!strcmp(spk_name, "TweeterLeft Speaker")) + return "TweeterLeft"; + if (!strcmp(spk_name, "WooferRight Speaker")) + return "WooferRight"; + if (!strcmp(spk_name, "TweeterRight Speaker")) + return "TweeterRight"; + if (!strcmp(spk_name, "SpkrLeft Speaker")) + return "SpkrLeft"; + if (!strcmp(spk_name, "SpkrRight Speaker")) + return "SpkrRight"; + + return NULL; +} + +static void qcs6490_wsa_mute_stream(struct snd_soc_card *card, + const char *prefix, int mute) +{ + struct snd_soc_pcm_runtime *rtd; + struct snd_soc_dai *cpu_dai, *codec_dai; + int i, ret; + + for_each_card_rtds(card, rtd) { + cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); + if (cpu_dai->id != WSA_CODEC_DMA_RX_0 && + cpu_dai->id != WSA_CODEC_DMA_RX_1) + continue; + + for_each_rtd_codec_dais(rtd, i, codec_dai) { + const char *name_prefix; + + if (!codec_dai->component || !codec_dai->driver || + !codec_dai->driver->ops || + !codec_dai->driver->ops->mute_stream) + continue; + + name_prefix = codec_dai->component->name_prefix; + if (!name_prefix || strcmp(name_prefix, prefix)) + continue; + + ret = codec_dai->driver->ops->mute_stream(codec_dai, mute, + SNDRV_PCM_STREAM_PLAYBACK); + if (ret && ret != -ENOTSUPP) + dev_warn(card->dev, + "mute_stream failed for %s on %s: %d\n", + prefix, codec_dai->name, ret); + return; + } + } +} + +static int qcs6490_wsa_spk_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + char *prefix = qcs6490_wsa_spk_to_prefix(w->name); + + if (!prefix) + return 0; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + qcs6490_wsa_mute_stream(snd_soc_dapm_to_card(w->dapm), prefix, 0); + break; + case SND_SOC_DAPM_PRE_PMD: + qcs6490_wsa_mute_stream(snd_soc_dapm_to_card(w->dapm), prefix, 1); + break; + default: + break; + } + + return 0; +} + +static struct snd_soc_dapm_widget qcs6490_dapm_widgets[] = { SND_SOC_DAPM_HP("Headphone Jack", NULL), SND_SOC_DAPM_MIC("Mic Jack", NULL), SND_SOC_DAPM_SPK("DP0 Jack", NULL), @@ -47,21 +155,76 @@ static const struct snd_soc_dapm_widget qcom_jack_snd_widgets[] = { SND_SOC_DAPM_SPK("DP5 Jack", NULL), SND_SOC_DAPM_SPK("DP6 Jack", NULL), SND_SOC_DAPM_SPK("DP7 Jack", NULL), + SND_SOC_DAPM_SPK("SpkrLeft Speaker", qcs6490_wsa_spk_event), + SND_SOC_DAPM_SPK("SpkrRight Speaker", qcs6490_wsa_spk_event), + }; +static const struct snd_soc_dapm_route qcs6490_dapm_routes[] = { + { "SpkrLeft Speaker", NULL, "SpkrLeft SPKR" }, + { "SpkrRight Speaker", NULL, "SpkrRight SPKR" }, +}; -static struct snd_soc_jack_pin qcs6490_headset_jack_pins[] = { - /* Headset */ - { - .pin = "Mic Jack", - .mask = SND_JACK_MICROPHONE, - }, - { - .pin = "Headphone Jack", - .mask = SND_JACK_HEADPHONE, - }, +static struct snd_soc_dapm_widget glymur_dapm_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_MIC("Mic Jack", NULL), + SND_SOC_DAPM_SPK("DP0 Jack", NULL), + SND_SOC_DAPM_SPK("DP1 Jack", NULL), + SND_SOC_DAPM_SPK("DP2 Jack", NULL), + SND_SOC_DAPM_SPK("DP3 Jack", NULL), + SND_SOC_DAPM_SPK("DP4 Jack", NULL), + SND_SOC_DAPM_SPK("DP5 Jack", NULL), + SND_SOC_DAPM_SPK("DP6 Jack", NULL), + SND_SOC_DAPM_SPK("DP7 Jack", NULL), + SND_SOC_DAPM_SPK("WooferLeft Speaker", qcs6490_wsa_spk_event), + SND_SOC_DAPM_SPK("TweeterLeft Speaker", qcs6490_wsa_spk_event), + SND_SOC_DAPM_SPK("WooferRight Speaker", qcs6490_wsa_spk_event), + SND_SOC_DAPM_SPK("TweeterRight Speaker", qcs6490_wsa_spk_event), }; +static const struct snd_soc_dapm_route glymur_dapm_routes[] = { + { "WooferLeft Speaker", NULL, "WooferLeft SPKR" }, + { "TweeterLeft Speaker", NULL, "TweeterLeft SPKR" }, + { "WooferRight Speaker", NULL, "WooferRight SPKR" }, + { "TweeterRight Speaker", NULL, "TweeterRight SPKR" }, +}; + +static struct snd_soc_dapm_widget sa8775p_dapm_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_MIC("Mic Jack", NULL), + SND_SOC_DAPM_SPK("DP0 Jack", NULL), + SND_SOC_DAPM_SPK("DP1 Jack", NULL), + SND_SOC_DAPM_SPK("DP2 Jack", NULL), + SND_SOC_DAPM_SPK("DP3 Jack", NULL), + SND_SOC_DAPM_SPK("DP4 Jack", NULL), + SND_SOC_DAPM_SPK("DP5 Jack", NULL), + SND_SOC_DAPM_SPK("DP6 Jack", NULL), + SND_SOC_DAPM_SPK("DP7 Jack", NULL), +}; + +static inline int qcs6490_get_mclk_freq(struct snd_pcm_hw_params *params) +{ + int rate = params_rate(params); + + switch (rate) { + case 11025: + case 44100: + case 88200: + return I2S_MCLK_RATE(44100); + default: + break; + } + + return I2S_MCLK_RATE(rate); +} + +static inline int qcs6490_get_bclk_freq(struct snd_pcm_hw_params *params) +{ + return I2S_BIT_RATE(params_rate(params), + params_channels(params), + snd_pcm_format_width(params_format(params))); +} + static int qcs6490_snd_sdw_startup(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); @@ -127,7 +290,7 @@ static int qcs6490_snd_sdw_prepare(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); struct snd_soc_dai *codec_dai; - int ret, i; + int ret; if (!sruntime) return 0; @@ -168,17 +331,6 @@ static int qcs6490_snd_sdw_prepare(struct snd_pcm_substream *substream, } *stream_prepared = true; - switch (cpu_dai->id) { - case WSA_CODEC_DMA_RX_0: - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - for_each_rtd_codec_dais(rtd, i, codec_dai) - snd_soc_dai_digital_mute(codec_dai, 0, substream->stream); - } - break; - default: - break; - } - return ret; } @@ -246,19 +398,6 @@ static void qcs6490_snd_shutdown(struct snd_pcm_substream *substream) struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); struct qcs6490_snd_data *pdata = snd_soc_card_get_drvdata(rtd->card); struct sdw_stream_runtime *sruntime = pdata->sruntime[cpu_dai->id]; - struct snd_soc_dai *codec_dai; - int i; - - switch (cpu_dai->id) { - case WSA_CODEC_DMA_RX_0: - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - for_each_rtd_codec_dais(rtd, i, codec_dai) - snd_soc_dai_digital_mute(codec_dai, 1, substream->stream); - } - break; - default: - break; - } pdata->sruntime[cpu_dai->id] = NULL; sdw_release_stream(sruntime); @@ -534,8 +673,8 @@ static int qcs6490_snd_parse_of(struct snd_soc_card *card) } if (!card->dapm_widgets) { - card->dapm_widgets = qcom_jack_snd_widgets; - card->num_dapm_widgets = ARRAY_SIZE(qcom_jack_snd_widgets); + card->dapm_widgets = qcs6490_dapm_widgets; + card->num_dapm_widgets = ARRAY_SIZE(qcs6490_dapm_widgets); } return 0; @@ -564,10 +703,24 @@ static int qcs6490_snd_init(struct snd_soc_pcm_runtime *rtd) * to reduce the risk of speaker damage until we have active * speaker protection in place. */ - snd_soc_limit_volume(card, "WSA_RX0 Digital Volume", 81); - snd_soc_limit_volume(card, "WSA_RX1 Digital Volume", 81); - snd_soc_limit_volume(card, "SpkrLeft PA Volume", 17); - snd_soc_limit_volume(card, "SpkrRight PA Volume", 17); + if (data->snd_soc_common_priv->num_wsa_spkr == 4) { + snd_soc_limit_volume(card, "WSA WSA_RX0 Digital Volume", 81); + snd_soc_limit_volume(card, "WSA WSA_RX1 Digital Volume", 81); + snd_soc_limit_volume(card, "WSA2 WSA_RX0 Digital Volume", 81); + snd_soc_limit_volume(card, "WSA2 WSA_RX1 Digital Volume", 81); + snd_soc_limit_volume(card, "SpkrLeft PA Volume", 6); + snd_soc_limit_volume(card, "SpkrRight PA Volume", 6); + snd_soc_limit_volume(card, "WooferLeft PA Volume", 6); + snd_soc_limit_volume(card, "TweeterLeft PA Volume", 6); + snd_soc_limit_volume(card, "WooferRight PA Volume", 6); + snd_soc_limit_volume(card, "TweeterRight PA Volume", 6); + } else { + snd_soc_limit_volume(card, "WSA_RX0 Digital Volume", 81); + snd_soc_limit_volume(card, "WSA_RX1 Digital Volume", 81); + snd_soc_limit_volume(card, "SpkrLeft PA Volume", 17); + snd_soc_limit_volume(card, "SpkrRight PA Volume", 17); + } + break; case DISPLAY_PORT_RX_0: /* DISPLAY_PORT dai ids are not contiguous */ @@ -585,7 +738,10 @@ static int qcs6490_snd_init(struct snd_soc_pcm_runtime *rtd) if (dp_jack) return qcs6490_snd_dp_jack_setup(rtd, dp_jack, dp_pcm_id); - return qcs6490_snd_wcd_jack_setup(rtd, &data->jack, &data->jack_setup); + if (data->snd_soc_common_priv->wcd_jack) + return qcs6490_snd_wcd_jack_setup(rtd, &data->jack, &data->jack_setup); + + return 0; } static int qcs6490_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, @@ -598,8 +754,6 @@ static int qcs6490_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, SNDRV_PCM_HW_PARAM_CHANNELS); rate->min = rate->max = 48000; - channels->min = 2; - channels->max = 2; switch (cpu_dai->id) { case TX_CODEC_DMA_TX_0: case TX_CODEC_DMA_TX_1: @@ -619,8 +773,54 @@ static int qcs6490_snd_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); struct qcs6490_snd_data *pdata = snd_soc_card_get_drvdata(rtd->card); + int mclk_freq = qcs6490_get_mclk_freq(params); + int bclk_freq = qcs6490_get_bclk_freq(params); + int ret = 0; + + switch (cpu_dai->id) { + case PRIMARY_MI2S_RX ... QUATERNARY_MI2S_TX: + case QUINARY_MI2S_RX ... QUINARY_MI2S_TX: + ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_BP_FP); + if (ret && ret != -ENOTSUPP) + return ret; + + if (pdata->snd_soc_common_priv->codec_dai_fmt) { + ret = snd_soc_dai_set_fmt(codec_dai, + pdata->snd_soc_common_priv->codec_dai_fmt); + if (ret && ret != -ENOTSUPP) + return ret; + } + + if (pdata->snd_soc_common_priv->mi2s_mclk_enable) { + ret = snd_soc_dai_set_sysclk(cpu_dai, + LPAIF_MI2S_MCLK, mclk_freq, + SND_SOC_CLOCK_OUT); + if (ret) + return ret; + } + + if (pdata->snd_soc_common_priv->mi2s_bclk_enable) { + ret = snd_soc_dai_set_sysclk(cpu_dai, + LPAIF_MI2S_BCLK, bclk_freq, + SND_SOC_CLOCK_OUT); + if (ret) + return ret; + } + + if (pdata->snd_soc_common_priv->codec_sysclk_set) { + ret = snd_soc_dai_set_sysclk(codec_dai, + 0, mclk_freq, + SND_SOC_CLOCK_IN); + if (ret) + return ret; + } + break; + default: + break; + } return qcs6490_snd_sdw_hw_params(substream, params, &pdata->sruntime[cpu_dai->id]); } @@ -677,35 +877,168 @@ static int qcs6490_platform_probe(struct platform_device *pdev) card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL); if (!card) return -ENOMEM; - card->owner = THIS_MODULE; /* Allocate the private data */ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; + data->snd_soc_common_priv = (struct snd_soc_common *)of_device_get_match_data(dev); + if (!data->snd_soc_common_priv) + return -ENODEV; + + card->owner = THIS_MODULE; card->dev = dev; dev_set_drvdata(dev, card); snd_soc_card_set_drvdata(card, data); + card->dapm_widgets = data->snd_soc_common_priv->dapm_widgets; + card->num_dapm_widgets = data->snd_soc_common_priv->num_dapm_widgets; + card->dapm_routes = data->snd_soc_common_priv->dapm_routes; + card->num_dapm_routes = data->snd_soc_common_priv->num_dapm_routes; + card->controls = data->snd_soc_common_priv->controls; + card->num_controls = data->snd_soc_common_priv->num_controls; + ret = qcs6490_snd_parse_of(card); if (ret) return ret; - card->driver_name = of_device_get_match_data(dev); + card->driver_name = data->snd_soc_common_priv->driver_name; qcs6490_add_be_ops(card); return devm_snd_soc_register_card(dev, card); } +static struct snd_soc_common glymur_priv_data = { + .driver_name = "glymur", + .dapm_widgets = glymur_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(glymur_dapm_widgets), + .dapm_routes = glymur_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(glymur_dapm_routes), + .num_wsa_spkr = 4, + .wcd_jack = true, +}; + +static struct snd_soc_common kaanapali_priv_data = { + .driver_name = "kaanapali", + .dapm_widgets = qcs6490_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(qcs6490_dapm_widgets), + .dapm_routes = qcs6490_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(qcs6490_dapm_routes), + .num_wsa_spkr = 2, + .wcd_jack = true, +}; + +static struct snd_soc_common qcs9100_priv_data = { + .driver_name = "sa8775p", + .dapm_widgets = sa8775p_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sa8775p_dapm_widgets), +}; + +static struct snd_soc_common qcs615_priv_data = { + .driver_name = "qcs615", + .dapm_widgets = sa8775p_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sa8775p_dapm_widgets), +}; + +static struct snd_soc_common qcm6490_priv_data = { + .driver_name = "qcm6490", + .dapm_widgets = qcs6490_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(qcs6490_dapm_widgets), + .dapm_routes = qcs6490_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(qcs6490_dapm_routes), + .num_wsa_spkr = 2, + .wcd_jack = true, +}; + +static struct snd_soc_common qcs6490_priv_data = { + .driver_name = "qcs6490", + .dapm_widgets = qcs6490_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(qcs6490_dapm_widgets), + .dapm_routes = qcs6490_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(qcs6490_dapm_routes), + .num_wsa_spkr = 2, + .wcd_jack = true, +}; + +static struct snd_soc_common qcs8275_priv_data = { + .driver_name = "qcs8300", + .dapm_widgets = sa8775p_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sa8775p_dapm_widgets), +}; + +static struct snd_soc_common sc8280xp_priv_data = { + .driver_name = "sc8280xp", + .dapm_widgets = qcs6490_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(qcs6490_dapm_widgets), + .dapm_routes = qcs6490_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(qcs6490_dapm_routes), + .num_wsa_spkr = 2, + .wcd_jack = true, +}; + +static struct snd_soc_common sm8450_priv_data = { + .driver_name = "sm8450", + .dapm_widgets = qcs6490_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(qcs6490_dapm_widgets), + .dapm_routes = qcs6490_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(qcs6490_dapm_routes), + .num_wsa_spkr = 2, + .wcd_jack = true, +}; + +static struct snd_soc_common sm8550_priv_data = { + .driver_name = "sm8550", + .dapm_widgets = qcs6490_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(qcs6490_dapm_widgets), + .dapm_routes = qcs6490_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(qcs6490_dapm_routes), + .num_wsa_spkr = 2, + .wcd_jack = true, +}; + +static struct snd_soc_common sm8650_priv_data = { + .driver_name = "sm8650", + .dapm_widgets = qcs6490_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(qcs6490_dapm_widgets), + .dapm_routes = qcs6490_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(qcs6490_dapm_routes), + .num_wsa_spkr = 2, + .wcd_jack = true, +}; + +static struct snd_soc_common sm8750_priv_data = { + .driver_name = "sm8750", + .dapm_widgets = qcs6490_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(qcs6490_dapm_widgets), + .dapm_routes = qcs6490_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(qcs6490_dapm_routes), + .num_wsa_spkr = 2, + .wcd_jack = true, +}; + +static struct snd_soc_common x1e80100_priv_data = { + .driver_name = "x1e80100", + .dapm_widgets = glymur_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(glymur_dapm_widgets), + .dapm_routes = glymur_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(glymur_dapm_routes), + .num_wsa_spkr = 4, + .wcd_jack = true, +}; + static const struct of_device_id snd_qcs6490_dt_match[] = { - {.compatible = "qcom,qcm6490-idp-sndcard", "qcm6490"}, - {.compatible = "qcom,qcs615-sndcard", "qcs615"}, - {.compatible = "qcom,qcs6490-rb3gen2-sndcard", "qcs6490"}, - {.compatible = "qcom,qcs8275-sndcard", "qcs8275"}, - {.compatible = "qcom,qcs8300-sndcard", "qcs8300"}, - {.compatible = "qcom,qcs9075-sndcard", "qcs9075"}, - {.compatible = "qcom,qcs9100-sndcard", "qcs9100"}, - {.compatible = "qcom,sm8750-sndcard", "sm8750"}, - {.compatible = "qcom,x1e80100-sndcard", "x1e80100"}, - {.compatible = "qcom,glymur-sndcard", "glymur"}, + {.compatible = "qcom,glymur-sndcard", .data = &glymur_priv_data}, + {.compatible = "qcom,kaanapali-sndcard", .data = &kaanapali_priv_data}, + {.compatible = "qcom,qcm6490-idp-sndcard", .data = &qcm6490_priv_data}, + {.compatible = "qcom,qcs615-sndcard", .data = &qcs615_priv_data}, + {.compatible = "qcom,qcs6490-rb3gen2-sndcard", .data = &qcs6490_priv_data}, + {.compatible = "qcom,qcs8275-sndcard", .data = &qcs8275_priv_data}, + {.compatible = "qcom,qcs9075-sndcard", .data = &qcs9100_priv_data}, + {.compatible = "qcom,qcs9100-sndcard", .data = &qcs9100_priv_data}, + {.compatible = "qcom,sc8280xp-sndcard", .data = &sc8280xp_priv_data}, + {.compatible = "qcom,sm8450-sndcard", .data = &sm8450_priv_data}, + {.compatible = "qcom,sm8550-sndcard", .data = &sm8550_priv_data}, + {.compatible = "qcom,sm8650-sndcard", .data = &sm8650_priv_data}, + {.compatible = "qcom,sm8750-sndcard", .data = &sm8750_priv_data}, + {.compatible = "qcom,x1e80100-sndcard", .data = &x1e80100_priv_data}, {} }; diff --git a/audioreach-driver/q6apm_audio.h b/audioreach-driver/q6apm_audio.h index 9503955..13199c0 100644 --- a/audioreach-driver/q6apm_audio.h +++ b/audioreach-driver/q6apm_audio.h @@ -53,4 +53,8 @@ struct apm_module_param_data { #define APM_MODULE_PARAM_DATA_SIZE sizeof(struct apm_module_param_data) +struct snd_soc_dai_driver * + q6dsp_audio_ports_set_config(struct device *dev, + struct q6dsp_audio_port_dai_driver_config *cfg, + int *num_dais); #endif diff --git a/audioreach-driver/q6apm_lpass_dummy_dais.c b/audioreach-driver/q6apm_lpass_dummy_dais.c index 0bd7292..44c34f9 100644 --- a/audioreach-driver/q6apm_lpass_dummy_dais.c +++ b/audioreach-driver/q6apm_lpass_dummy_dais.c @@ -609,10 +609,18 @@ static struct snd_soc_dai_driver q6dsp_audio_fe_dais[] = { Q6AFE_CDC_DMA_RX_DAI(RX_CODEC_DMA_RX_7), }; +struct q6apm_dai_priv_data { + struct clk *mclk; + struct clk *bclk; + struct clk *eclk; + bool mclk_enabled, bclk_enabled, eclk_enabled; +}; + struct q6apm_lpass_dai_data { struct q6apm_graph *graph[APM_PORT_MAX]; bool is_port_started[APM_PORT_MAX]; struct audioreach_module_config module_config[APM_PORT_MAX]; + struct q6apm_dai_priv_data priv[APM_PORT_MAX]; }; static const struct snd_pcm_hardware q6apm_dummy_dma_hardware = { @@ -685,23 +693,86 @@ q6dsp_audio_ports_set_config(struct device *dev, return q6dsp_audio_fe_dais; } -static int q6apm_lpass_dai_dummy_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) +static int q6apm_lpass_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) { snd_soc_set_runtime_hwparams(substream, &q6apm_dummy_dma_hardware); return 0; } +static void q6i2s_lpass_dai_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) +{ + struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev); + + if (dai_data->priv[dai->id].mclk_enabled) { + clk_disable_unprepare(dai_data->priv[dai->id].mclk); + dai_data->priv[dai->id].mclk_enabled = false; + } + + if (dai_data->priv[dai->id].bclk_enabled) { + clk_disable_unprepare(dai_data->priv[dai->id].bclk); + dai_data->priv[dai->id].bclk_enabled = false; + } + + if (dai_data->priv[dai->id].eclk_enabled) { + clk_disable_unprepare(dai_data->priv[dai->id].eclk); + dai_data->priv[dai->id].eclk_enabled = false; + } +} + +static int q6i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int freq, int dir) +{ + struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev); + struct clk *sysclk = NULL; + bool *enabled = NULL; + int ret = 0; + + switch (clk_id) { + case LPAIF_MI2S_MCLK: + sysclk = dai_data->priv[dai->id].mclk; + enabled = &dai_data->priv[dai->id].mclk_enabled; + break; + case LPAIF_MI2S_BCLK: + sysclk = dai_data->priv[dai->id].bclk; + enabled = &dai_data->priv[dai->id].bclk_enabled; + break; + case LPAIF_MI2S_ECLK: + sysclk = dai_data->priv[dai->id].eclk; + enabled = &dai_data->priv[dai->id].eclk_enabled; + break; + default: + return -EINVAL; + } + + if (sysclk) { + if (*enabled) + return 0; + + clk_set_rate(sysclk, freq); + ret = clk_prepare_enable(sysclk); + if (ret) { + dev_err(dai->dev, "Error, Unable to prepare (%d) sysclk\n", clk_id); + return ret; + } + + *enabled = true; + } + + return ret; +} + static const struct snd_soc_dai_ops q6dummy_ops = { - .startup = q6apm_lpass_dai_dummy_startup, + .startup = q6apm_lpass_dai_startup, }; static const struct snd_soc_dai_ops q6i2sdummy_ops = { - .startup = q6apm_lpass_dai_dummy_startup, + .startup = q6apm_lpass_dai_startup, + .set_sysclk = q6i2s_set_sysclk, + .shutdown = q6i2s_lpass_dai_shutdown, }; static const struct snd_soc_dai_ops q6tdmdummy_ops = { - .startup = q6apm_lpass_dai_dummy_startup, + .startup = q6apm_lpass_dai_startup, }; static const struct snd_soc_component_driver q6apm_lpass_dummy_dai_component = { @@ -711,6 +782,72 @@ static const struct snd_soc_component_driver q6apm_lpass_dummy_dai_component = { .use_dai_pcm_id = false, }; +static int of_q6apm_parse_dai_data(struct device *dev, + struct q6apm_lpass_dai_data *data) +{ + int ret; + + for_each_child_of_node_scoped(dev->of_node, node) { + struct q6apm_dai_priv_data *priv; + int id; + + ret = of_property_read_u32(node, "reg", &id); + if (ret || id < 0 || id >= APM_PORT_MAX) { + dev_err(dev, "valid dai id not found:%d\n", ret); + continue; + } + + switch (id) { + /* MI2S specific properties */ + case PRIMARY_MI2S_RX ... QUATERNARY_MI2S_TX: + case QUINARY_MI2S_RX ... QUINARY_MI2S_TX: + priv = &data->priv[id]; + priv->mclk = of_clk_get_by_name(node, "mclk"); + if (IS_ERR(priv->mclk)) { + if (PTR_ERR(priv->mclk) == -EPROBE_DEFER) + return dev_err_probe(dev, PTR_ERR(priv->mclk), + "unable to get mi2s mclk\n"); + priv->mclk = NULL; + } + + priv->bclk = of_clk_get_by_name(node, "bclk"); + if (IS_ERR(priv->bclk)) { + if (PTR_ERR(priv->bclk) == -EPROBE_DEFER) { + if (priv->mclk) { + clk_put(priv->mclk); + priv->mclk = NULL; + } + return dev_err_probe(dev, PTR_ERR(priv->bclk), + "unable to get mi2s bclk\n"); + } + priv->bclk = NULL; + } + + priv->eclk = of_clk_get_by_name(node, "eclk"); + if (IS_ERR(priv->eclk)) { + if (PTR_ERR(priv->eclk) == -EPROBE_DEFER) { + if (priv->mclk) { + clk_put(priv->mclk); + priv->mclk = NULL; + } + if (priv->bclk) { + clk_put(priv->bclk); + priv->bclk = NULL; + } + return dev_err_probe(dev, PTR_ERR(priv->eclk), + "unable to get mi2s eclk\n"); + } + priv->eclk = NULL; + } + break; + default: + break; + } + } + + return 0; +} + static int q6apm_lpass_dummy_dai_dev_probe(struct platform_device *pdev) { const struct snd_soc_component_driver *q6apm_lpass_component = NULL; @@ -719,12 +856,16 @@ static int q6apm_lpass_dummy_dai_dev_probe(struct platform_device *pdev) struct snd_soc_dai_driver *dais; struct device *dev = &pdev->dev; int num_dais; + int ret; dai_data = devm_kzalloc(dev, sizeof(*dai_data), GFP_KERNEL); if (!dai_data) return -ENOMEM; dev_set_drvdata(dev, dai_data); + ret = of_q6apm_parse_dai_data(dev, dai_data); + if (ret) + return ret; memset(&cfg, 0, sizeof(cfg)); diff --git a/audioreach-driver/q6prm_audioreach.h b/audioreach-driver/q6prm_audioreach.h index 51fb155..4917abb 100644 --- a/audioreach-driver/q6prm_audioreach.h +++ b/audioreach-driver/q6prm_audioreach.h @@ -6,6 +6,10 @@ #include +#define LPAIF_MI2S_MCLK 1 +#define LPAIF_MI2S_BCLK 2 +#define LPAIF_MI2S_ECLK 3 + /* Clock ID for Primary I2S IBIT */ #define Q6PRM_LPASS_CLK_ID_PRI_MI2S_IBIT 0x100 /* Clock ID for Primary I2S EBIT */