mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-12-17 15:26:48 +07:00
1a497983a5
Currently the number of DAI links is statically defined by the machine driver at build time using an array. This makes it difficult to shrink/ grow the number of DAI links at runtime in order to reflect any changes in topology. We can change the DAI link array in the core to a list so that PCMs and FE DAI links can be added and deleted at runtime to reflect changes in use case and DSP topology. The machine driver can still register DAI links as an array. As the 1st step, this patch change the PCM runtime array to a list. A new PCM runtime is added to the list when a DAI link is bound successfully. Later patches will further implement the DAI link list. More: - define snd_soc_new/free_pcm_runtime() to create/free a runtime. - define soc_add_pcm_runtime() to add a runtime to the rtd list. - define soc_remove_pcm_runtimes() to clean up the runtime list. - traverse the rtd list to probe the link components and dais. - Add a field "num" to PCM runtime struct, used to specify the device number when creating the pcm device, and for a soc card to access its dai_props array. - The following 3rd party machine/platform drivers iterate the rtd list to check the runtimes: sound/soc/intel/atom/sst-mfld-platform-pcm.c sound/soc/intel/boards/cht_bsw_rt5645.c sound/soc/intel/boards/cht_bsw_rt5672.c sound/soc/intel/boards/cht_bsw_max98090_ti.c Signed-off-by: Mengdong Lin <mengdong.lin@linux.intel.com> Signed-off-by: Mark Brown <broonie@kernel.org>
384 lines
11 KiB
C
384 lines
11 KiB
C
/*
|
|
* cht-bsw-rt5645.c - ASoc Machine driver for Intel Cherryview-based platforms
|
|
* Cherrytrail and Braswell, with RT5645 codec.
|
|
*
|
|
* Copyright (C) 2015 Intel Corp
|
|
* Author: Fang, Yang A <yang.a.fang@intel.com>
|
|
* N,Harshapriya <harshapriya.n@intel.com>
|
|
* This file is modified from cht_bsw_rt5672.c
|
|
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; version 2 of the License.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/acpi.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/slab.h>
|
|
#include <sound/pcm.h>
|
|
#include <sound/pcm_params.h>
|
|
#include <sound/soc.h>
|
|
#include <sound/jack.h>
|
|
#include "../../codecs/rt5645.h"
|
|
#include "../atom/sst-atom-controls.h"
|
|
|
|
#define CHT_PLAT_CLK_3_HZ 19200000
|
|
#define CHT_CODEC_DAI "rt5645-aif1"
|
|
|
|
struct cht_acpi_card {
|
|
char *codec_id;
|
|
int codec_type;
|
|
struct snd_soc_card *soc_card;
|
|
};
|
|
|
|
struct cht_mc_private {
|
|
struct snd_soc_jack jack;
|
|
struct cht_acpi_card *acpi_card;
|
|
};
|
|
|
|
static inline struct snd_soc_dai *cht_get_codec_dai(struct snd_soc_card *card)
|
|
{
|
|
struct snd_soc_pcm_runtime *rtd;
|
|
|
|
list_for_each_entry(rtd, &card->rtd_list, list) {
|
|
if (!strncmp(rtd->codec_dai->name, CHT_CODEC_DAI,
|
|
strlen(CHT_CODEC_DAI)))
|
|
return rtd->codec_dai;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static int platform_clock_control(struct snd_soc_dapm_widget *w,
|
|
struct snd_kcontrol *k, int event)
|
|
{
|
|
struct snd_soc_dapm_context *dapm = w->dapm;
|
|
struct snd_soc_card *card = dapm->card;
|
|
struct snd_soc_dai *codec_dai;
|
|
int ret;
|
|
|
|
codec_dai = cht_get_codec_dai(card);
|
|
if (!codec_dai) {
|
|
dev_err(card->dev, "Codec dai not found; Unable to set platform clock\n");
|
|
return -EIO;
|
|
}
|
|
|
|
if (!SND_SOC_DAPM_EVENT_OFF(event))
|
|
return 0;
|
|
|
|
/* Set codec sysclk source to its internal clock because codec PLL will
|
|
* be off when idle and MCLK will also be off by ACPI when codec is
|
|
* runtime suspended. Codec needs clock for jack detection and button
|
|
* press.
|
|
*/
|
|
ret = snd_soc_dai_set_sysclk(codec_dai, RT5645_SCLK_S_RCCLK,
|
|
0, SND_SOC_CLOCK_IN);
|
|
if (ret < 0) {
|
|
dev_err(card->dev, "can't set codec sysclk: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct snd_soc_dapm_widget cht_dapm_widgets[] = {
|
|
SND_SOC_DAPM_HP("Headphone", NULL),
|
|
SND_SOC_DAPM_MIC("Headset Mic", NULL),
|
|
SND_SOC_DAPM_MIC("Int Mic", NULL),
|
|
SND_SOC_DAPM_SPK("Ext Spk", NULL),
|
|
SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,
|
|
platform_clock_control, SND_SOC_DAPM_POST_PMD),
|
|
};
|
|
|
|
static const struct snd_soc_dapm_route cht_rt5645_audio_map[] = {
|
|
{"IN1P", NULL, "Headset Mic"},
|
|
{"IN1N", NULL, "Headset Mic"},
|
|
{"DMIC L1", NULL, "Int Mic"},
|
|
{"DMIC R1", NULL, "Int Mic"},
|
|
{"Headphone", NULL, "HPOL"},
|
|
{"Headphone", NULL, "HPOR"},
|
|
{"Ext Spk", NULL, "SPOL"},
|
|
{"Ext Spk", NULL, "SPOR"},
|
|
{"AIF1 Playback", NULL, "ssp2 Tx"},
|
|
{"ssp2 Tx", NULL, "codec_out0"},
|
|
{"ssp2 Tx", NULL, "codec_out1"},
|
|
{"codec_in0", NULL, "ssp2 Rx" },
|
|
{"codec_in1", NULL, "ssp2 Rx" },
|
|
{"ssp2 Rx", NULL, "AIF1 Capture"},
|
|
{"Headphone", NULL, "Platform Clock"},
|
|
{"Headset Mic", NULL, "Platform Clock"},
|
|
{"Int Mic", NULL, "Platform Clock"},
|
|
{"Ext Spk", NULL, "Platform Clock"},
|
|
};
|
|
|
|
static const struct snd_soc_dapm_route cht_rt5650_audio_map[] = {
|
|
{"IN1P", NULL, "Headset Mic"},
|
|
{"IN1N", NULL, "Headset Mic"},
|
|
{"DMIC L2", NULL, "Int Mic"},
|
|
{"DMIC R2", NULL, "Int Mic"},
|
|
{"Headphone", NULL, "HPOL"},
|
|
{"Headphone", NULL, "HPOR"},
|
|
{"Ext Spk", NULL, "SPOL"},
|
|
{"Ext Spk", NULL, "SPOR"},
|
|
{"AIF1 Playback", NULL, "ssp2 Tx"},
|
|
{"ssp2 Tx", NULL, "codec_out0"},
|
|
{"ssp2 Tx", NULL, "codec_out1"},
|
|
{"codec_in0", NULL, "ssp2 Rx" },
|
|
{"codec_in1", NULL, "ssp2 Rx" },
|
|
{"ssp2 Rx", NULL, "AIF1 Capture"},
|
|
{"Headphone", NULL, "Platform Clock"},
|
|
{"Headset Mic", NULL, "Platform Clock"},
|
|
{"Int Mic", NULL, "Platform Clock"},
|
|
{"Ext Spk", NULL, "Platform Clock"},
|
|
};
|
|
|
|
static const struct snd_kcontrol_new cht_mc_controls[] = {
|
|
SOC_DAPM_PIN_SWITCH("Headphone"),
|
|
SOC_DAPM_PIN_SWITCH("Headset Mic"),
|
|
SOC_DAPM_PIN_SWITCH("Int Mic"),
|
|
SOC_DAPM_PIN_SWITCH("Ext Spk"),
|
|
};
|
|
|
|
static int cht_aif1_hw_params(struct snd_pcm_substream *substream,
|
|
struct snd_pcm_hw_params *params)
|
|
{
|
|
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
|
struct snd_soc_dai *codec_dai = rtd->codec_dai;
|
|
int ret;
|
|
|
|
/* set codec PLL source to the 19.2MHz platform clock (MCLK) */
|
|
ret = snd_soc_dai_set_pll(codec_dai, 0, RT5645_PLL1_S_MCLK,
|
|
CHT_PLAT_CLK_3_HZ, params_rate(params) * 512);
|
|
if (ret < 0) {
|
|
dev_err(rtd->dev, "can't set codec pll: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
ret = snd_soc_dai_set_sysclk(codec_dai, RT5645_SCLK_S_PLL1,
|
|
params_rate(params) * 512, SND_SOC_CLOCK_IN);
|
|
if (ret < 0) {
|
|
dev_err(rtd->dev, "can't set codec sysclk: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cht_codec_init(struct snd_soc_pcm_runtime *runtime)
|
|
{
|
|
int ret;
|
|
int jack_type;
|
|
struct snd_soc_codec *codec = runtime->codec;
|
|
struct snd_soc_dai *codec_dai = runtime->codec_dai;
|
|
struct cht_mc_private *ctx = snd_soc_card_get_drvdata(runtime->card);
|
|
|
|
/* Select clk_i2s1_asrc as ASRC clock source */
|
|
rt5645_sel_asrc_clk_src(codec,
|
|
RT5645_DA_STEREO_FILTER |
|
|
RT5645_DA_MONO_L_FILTER |
|
|
RT5645_DA_MONO_R_FILTER |
|
|
RT5645_AD_STEREO_FILTER,
|
|
RT5645_CLK_SEL_I2S1_ASRC);
|
|
|
|
/* TDM 4 slots 24 bit, set Rx & Tx bitmask to 4 active slots */
|
|
ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xF, 0xF, 4, 24);
|
|
if (ret < 0) {
|
|
dev_err(runtime->dev, "can't set codec TDM slot %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
if (ctx->acpi_card->codec_type == CODEC_TYPE_RT5650)
|
|
jack_type = SND_JACK_HEADPHONE | SND_JACK_MICROPHONE |
|
|
SND_JACK_BTN_0 | SND_JACK_BTN_1 |
|
|
SND_JACK_BTN_2 | SND_JACK_BTN_3;
|
|
else
|
|
jack_type = SND_JACK_HEADPHONE | SND_JACK_MICROPHONE;
|
|
|
|
ret = snd_soc_card_jack_new(runtime->card, "Headset Jack",
|
|
jack_type, &ctx->jack,
|
|
NULL, 0);
|
|
if (ret) {
|
|
dev_err(runtime->dev, "Headset jack creation failed %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
rt5645_set_jack_detect(codec, &ctx->jack, &ctx->jack, &ctx->jack);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int cht_codec_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);
|
|
|
|
/* The DSP will covert the FE rate to 48k, stereo, 24bits */
|
|
rate->min = rate->max = 48000;
|
|
channels->min = channels->max = 2;
|
|
|
|
/* set SSP2 to 24-bit */
|
|
params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
|
|
return 0;
|
|
}
|
|
|
|
static int cht_aif1_startup(struct snd_pcm_substream *substream)
|
|
{
|
|
return snd_pcm_hw_constraint_single(substream->runtime,
|
|
SNDRV_PCM_HW_PARAM_RATE, 48000);
|
|
}
|
|
|
|
static struct snd_soc_ops cht_aif1_ops = {
|
|
.startup = cht_aif1_startup,
|
|
};
|
|
|
|
static struct snd_soc_ops cht_be_ssp2_ops = {
|
|
.hw_params = cht_aif1_hw_params,
|
|
};
|
|
|
|
static struct snd_soc_dai_link cht_dailink[] = {
|
|
[MERR_DPCM_AUDIO] = {
|
|
.name = "Audio Port",
|
|
.stream_name = "Audio",
|
|
.cpu_dai_name = "media-cpu-dai",
|
|
.codec_dai_name = "snd-soc-dummy-dai",
|
|
.codec_name = "snd-soc-dummy",
|
|
.platform_name = "sst-mfld-platform",
|
|
.nonatomic = true,
|
|
.dynamic = 1,
|
|
.dpcm_playback = 1,
|
|
.dpcm_capture = 1,
|
|
.ops = &cht_aif1_ops,
|
|
},
|
|
[MERR_DPCM_COMPR] = {
|
|
.name = "Compressed Port",
|
|
.stream_name = "Compress",
|
|
.cpu_dai_name = "compress-cpu-dai",
|
|
.codec_dai_name = "snd-soc-dummy-dai",
|
|
.codec_name = "snd-soc-dummy",
|
|
.platform_name = "sst-mfld-platform",
|
|
},
|
|
/* CODEC<->CODEC link */
|
|
/* back ends */
|
|
{
|
|
.name = "SSP2-Codec",
|
|
.be_id = 1,
|
|
.cpu_dai_name = "ssp2-port",
|
|
.platform_name = "sst-mfld-platform",
|
|
.no_pcm = 1,
|
|
.codec_dai_name = "rt5645-aif1",
|
|
.codec_name = "i2c-10EC5645:00",
|
|
.dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF
|
|
| SND_SOC_DAIFMT_CBS_CFS,
|
|
.init = cht_codec_init,
|
|
.be_hw_params_fixup = cht_codec_fixup,
|
|
.nonatomic = true,
|
|
.dpcm_playback = 1,
|
|
.dpcm_capture = 1,
|
|
.ops = &cht_be_ssp2_ops,
|
|
},
|
|
};
|
|
|
|
/* SoC card */
|
|
static struct snd_soc_card snd_soc_card_chtrt5645 = {
|
|
.name = "chtrt5645",
|
|
.owner = THIS_MODULE,
|
|
.dai_link = cht_dailink,
|
|
.num_links = ARRAY_SIZE(cht_dailink),
|
|
.dapm_widgets = cht_dapm_widgets,
|
|
.num_dapm_widgets = ARRAY_SIZE(cht_dapm_widgets),
|
|
.dapm_routes = cht_rt5645_audio_map,
|
|
.num_dapm_routes = ARRAY_SIZE(cht_rt5645_audio_map),
|
|
.controls = cht_mc_controls,
|
|
.num_controls = ARRAY_SIZE(cht_mc_controls),
|
|
};
|
|
|
|
static struct snd_soc_card snd_soc_card_chtrt5650 = {
|
|
.name = "chtrt5650",
|
|
.owner = THIS_MODULE,
|
|
.dai_link = cht_dailink,
|
|
.num_links = ARRAY_SIZE(cht_dailink),
|
|
.dapm_widgets = cht_dapm_widgets,
|
|
.num_dapm_widgets = ARRAY_SIZE(cht_dapm_widgets),
|
|
.dapm_routes = cht_rt5650_audio_map,
|
|
.num_dapm_routes = ARRAY_SIZE(cht_rt5650_audio_map),
|
|
.controls = cht_mc_controls,
|
|
.num_controls = ARRAY_SIZE(cht_mc_controls),
|
|
};
|
|
|
|
static struct cht_acpi_card snd_soc_cards[] = {
|
|
{"10EC5645", CODEC_TYPE_RT5645, &snd_soc_card_chtrt5645},
|
|
{"10EC5650", CODEC_TYPE_RT5650, &snd_soc_card_chtrt5650},
|
|
};
|
|
|
|
static acpi_status snd_acpi_codec_match(acpi_handle handle, u32 level,
|
|
void *context, void **ret)
|
|
{
|
|
*(bool *)context = true;
|
|
return AE_OK;
|
|
}
|
|
|
|
static int snd_cht_mc_probe(struct platform_device *pdev)
|
|
{
|
|
int ret_val = 0;
|
|
int i;
|
|
struct cht_mc_private *drv;
|
|
struct snd_soc_card *card = snd_soc_cards[0].soc_card;
|
|
bool found = false;
|
|
char codec_name[16];
|
|
|
|
drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_ATOMIC);
|
|
if (!drv)
|
|
return -ENOMEM;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(snd_soc_cards); i++) {
|
|
if (ACPI_SUCCESS(acpi_get_devices(
|
|
snd_soc_cards[i].codec_id,
|
|
snd_acpi_codec_match,
|
|
&found, NULL)) && found) {
|
|
dev_dbg(&pdev->dev,
|
|
"found codec %s\n", snd_soc_cards[i].codec_id);
|
|
card = snd_soc_cards[i].soc_card;
|
|
drv->acpi_card = &snd_soc_cards[i];
|
|
break;
|
|
}
|
|
}
|
|
card->dev = &pdev->dev;
|
|
sprintf(codec_name, "i2c-%s:00", drv->acpi_card->codec_id);
|
|
/* set correct codec name */
|
|
strcpy((char *)card->dai_link[2].codec_name, codec_name);
|
|
snd_soc_card_set_drvdata(card, drv);
|
|
ret_val = devm_snd_soc_register_card(&pdev->dev, card);
|
|
if (ret_val) {
|
|
dev_err(&pdev->dev,
|
|
"snd_soc_register_card failed %d\n", ret_val);
|
|
return ret_val;
|
|
}
|
|
platform_set_drvdata(pdev, card);
|
|
return ret_val;
|
|
}
|
|
|
|
static struct platform_driver snd_cht_mc_driver = {
|
|
.driver = {
|
|
.name = "cht-bsw-rt5645",
|
|
},
|
|
.probe = snd_cht_mc_probe,
|
|
};
|
|
|
|
module_platform_driver(snd_cht_mc_driver)
|
|
|
|
MODULE_DESCRIPTION("ASoC Intel(R) Braswell Machine driver");
|
|
MODULE_AUTHOR("Fang, Yang A,N,Harshapriya");
|
|
MODULE_LICENSE("GPL v2");
|
|
MODULE_ALIAS("platform:cht-bsw-rt5645");
|