-
Notifications
You must be signed in to change notification settings - Fork 41
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
ANDROID: ASoC: Intel: avs: Add rt5640 machine board
To support AVS-rt5640 configuration add machine board connecting AVS platform component driver with rt5640 codec one. Change-Id: I4190240202fd6f5653914e75e1447d0d2ffaa7f6 Signed-off-by: Cezary Rojewski <cezary.rojewski@intel.com>
- Loading branch information
1 parent
50ac5fd
commit 301e1f8
Showing
4 changed files
with
345 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,306 @@ | ||
// SPDX-License-Identifier: GPL-2.0-only | ||
// | ||
// Copyright(c) 2022 Intel Corporation. All rights reserved. | ||
// | ||
// Authors: Cezary Rojewski <cezary.rojewski@intel.com> | ||
// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> | ||
// | ||
|
||
#include <linux/module.h> | ||
#include <sound/jack.h> | ||
#include <sound/pcm.h> | ||
#include <sound/pcm_params.h> | ||
#include <sound/soc.h> | ||
#include <sound/soc-acpi.h> | ||
#include "../../../codecs/rt5640.h" | ||
|
||
#define AVS_RT5640_FREQ_OUT 24000000 | ||
#define AVS_RT5640_BE_FIXUP_RATE 48000 | ||
#define RT5640_CODEC_DAI "rt5640-aif1" | ||
#define AVS_ADI_POC 0 | ||
|
||
static const struct snd_soc_dapm_widget card_widgets[] = { | ||
SND_SOC_DAPM_HP("Headphone Jack", NULL), | ||
SND_SOC_DAPM_MIC("Mic Jack", NULL), | ||
SND_SOC_DAPM_SPK("Speaker", NULL), | ||
}; | ||
|
||
static const struct snd_soc_dapm_route card_base_routes[] = { | ||
{ "Headphone Jack", NULL, "HPOR" }, | ||
{ "Headphone Jack", NULL, "HPOL" }, | ||
{ "IN2P", NULL, "Mic Jack" }, | ||
{ "IN2P", NULL, "MICBIAS1" }, | ||
{ "Speaker", NULL, "SPOLP" }, | ||
{ "Speaker", NULL, "SPOLN" }, | ||
{ "Speaker", NULL, "SPORP" }, | ||
{ "Speaker", NULL, "SPORN" }, | ||
}; | ||
|
||
#if AVS_ADI_POC | ||
static struct snd_soc_jack_pin card_headset_pins[] = { | ||
{ | ||
.pin = "Headphone Jack", | ||
.mask = SND_JACK_HEADPHONE, | ||
}, | ||
{ | ||
.pin = "Mic Jack", | ||
.mask = SND_JACK_MICROPHONE, | ||
}, | ||
}; | ||
#endif | ||
static int avs_rt5640_codec_init(struct snd_soc_pcm_runtime *runtime) | ||
{ | ||
//struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(runtime, 0); | ||
//struct snd_soc_component *component = codec_dai->component; | ||
//struct snd_soc_jack_pin *pins; | ||
//struct snd_soc_jack *jack; | ||
struct snd_soc_card *card = runtime->card; | ||
//int num_pins, ret; | ||
#if AVS_ADI_POC | ||
jack = snd_soc_card_get_drvdata(card); | ||
num_pins = ARRAY_SIZE(card_headset_pins); | ||
|
||
pins = devm_kmemdup(card->dev, card_headset_pins, sizeof(*pins) * num_pins, GFP_KERNEL); | ||
if (!pins) | ||
return -ENOMEM; | ||
|
||
ret = snd_soc_card_jack_new(card, "Headset", SND_JACK_HEADSET, jack, pins, num_pins); | ||
if (ret) | ||
return ret; | ||
|
||
snd_soc_component_set_jack(component, jack, NULL); | ||
#endif | ||
card->dapm.idle_bias_off = true; | ||
|
||
return 0; | ||
} | ||
|
||
static void avs_rt5640_codec_exit(struct snd_soc_pcm_runtime *runtime) | ||
{ | ||
//snd_soc_component_set_jack(asoc_rtd_to_codec(runtime, 0)->component, NULL, NULL); | ||
} | ||
|
||
static int avs_rt5640_be_fixup(struct snd_soc_pcm_runtime *runtime, | ||
struct snd_pcm_hw_params *params) | ||
{ | ||
struct snd_interval *channels, *rate; | ||
struct snd_mask *fmt; | ||
|
||
channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); | ||
rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); | ||
fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); | ||
|
||
/* The ADSP will convert the FE rate to 48kHz, stereo */ | ||
channels->min = channels->max = 2; | ||
rate->min = rate->max = AVS_RT5640_BE_FIXUP_RATE; | ||
|
||
/* set SSPN to 24 bit */ | ||
snd_mask_none(fmt); | ||
snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE); | ||
|
||
return 0; | ||
} | ||
|
||
static int | ||
avs_rt5640_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) | ||
{ | ||
struct snd_soc_pcm_runtime *runtime = asoc_substream_to_rtd(substream); | ||
struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(runtime, 0); | ||
struct snd_soc_dai_link *dai_link = runtime->dai_link; | ||
int ret; | ||
|
||
ret = snd_soc_dai_set_pll(codec_dai, 0, RT5640_PLL1_S_MCLK, 19200000, | ||
params_rate(params) * 512); | ||
if (ret < 0) | ||
dev_err(runtime->dev, "Set codec pll failed: %d\n", ret); | ||
|
||
ret = snd_soc_dai_set_sysclk(codec_dai, RT5640_SCLK_S_PLL1, params_rate(params) * 512, | ||
SND_SOC_CLOCK_IN); | ||
if (ret < 0) | ||
dev_err(runtime->dev, "Set codec sysclk failed: %d\n", ret); | ||
|
||
/* Set correct codec filter for DAI format and clock config. */ | ||
if (dai_link->dai_fmt & SND_SOC_DAIFMT_CBS_CFS) | ||
rt5640_sel_asrc_clk_src(codec_dai->component, | ||
RT5640_DA_STEREO_FILTER | RT5640_DA_MONO_L_FILTER | | ||
RT5640_DA_MONO_R_FILTER | RT5640_AD_STEREO_FILTER, | ||
RT5640_CLK_SEL_ASRC); | ||
|
||
/* specific setting for bclk=50FS */ | ||
snd_soc_component_update_bits(codec_dai->component, 0xfb, 0x0004, 0x0004); | ||
|
||
return ret; | ||
} | ||
|
||
static const struct snd_soc_ops avs_rt5640_ops = { | ||
.hw_params = avs_rt5640_hw_params, | ||
}; | ||
|
||
static int avs_create_dai_link(struct device *dev, const char *platform_name, int ssp_port, | ||
struct snd_soc_acpi_mach *mach, struct snd_soc_dai_link **dai_link) | ||
{ | ||
struct snd_soc_dai_link_component *platform; | ||
struct snd_soc_dai_link *dl; | ||
u64 uid; | ||
int ret; | ||
|
||
ret = kstrtou64(mach->uid, 0, &uid); | ||
if (ret) | ||
return ret; | ||
|
||
dl = devm_kzalloc(dev, sizeof(*dl), GFP_KERNEL); | ||
platform = devm_kzalloc(dev, sizeof(*platform), GFP_KERNEL); | ||
if (!dl || !platform) | ||
return -ENOMEM; | ||
|
||
platform->name = platform_name; | ||
|
||
dl->name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-Codec", ssp_port); | ||
dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL); | ||
dl->codecs = devm_kzalloc(dev, sizeof(*dl->codecs), GFP_KERNEL); | ||
if (!dl->name || !dl->cpus || !dl->codecs) | ||
return -ENOMEM; | ||
|
||
dl->cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d Pin", ssp_port); | ||
dl->codecs->name = devm_kasprintf(dev, GFP_KERNEL, "i2c-10EC5640:0%llu", uid - 1); | ||
dl->codecs->dai_name = devm_kasprintf(dev, GFP_KERNEL, RT5640_CODEC_DAI); | ||
if (!dl->cpus->dai_name || !dl->codecs->name || !dl->codecs->dai_name) | ||
return -ENOMEM; | ||
|
||
dl->num_cpus = 1; | ||
dl->num_codecs = 1; | ||
dl->platforms = platform; | ||
dl->num_platforms = 1; | ||
dl->id = 0; | ||
// codec as slave | ||
dl->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS; | ||
// codec as master | ||
//dl->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM; | ||
dl->init = avs_rt5640_codec_init; | ||
dl->exit = avs_rt5640_codec_exit; | ||
dl->be_hw_params_fixup = avs_rt5640_be_fixup; | ||
dl->ops = &avs_rt5640_ops; | ||
dl->nonatomic = 1; | ||
dl->no_pcm = 1; | ||
dl->dpcm_capture = 1; | ||
dl->dpcm_playback = 1; | ||
|
||
*dai_link = dl; | ||
|
||
return 0; | ||
} | ||
|
||
static int avs_create_dapm_routes(struct device *dev, int ssp_port, | ||
struct snd_soc_dapm_route **routes, int *num_routes) | ||
{ | ||
struct snd_soc_dapm_route *dr; | ||
const int num_base = ARRAY_SIZE(card_base_routes); | ||
const int num_dr = num_base + 2; | ||
int idx; | ||
|
||
dr = devm_kcalloc(dev, num_dr, sizeof(*dr), GFP_KERNEL); | ||
if (!dr) | ||
return -ENOMEM; | ||
|
||
memcpy(dr, card_base_routes, num_base * sizeof(*dr)); | ||
|
||
idx = num_base; | ||
dr[idx].sink = devm_kasprintf(dev, GFP_KERNEL, "AIF1 Playback"); | ||
dr[idx].source = devm_kasprintf(dev, GFP_KERNEL, "ssp%d Tx", ssp_port); | ||
if (!dr[idx].sink || !dr[idx].source) | ||
return -ENOMEM; | ||
|
||
idx++; | ||
dr[idx].sink = devm_kasprintf(dev, GFP_KERNEL, "ssp%d Rx", ssp_port); | ||
dr[idx].source = devm_kasprintf(dev, GFP_KERNEL, "AIF1 Capture"); | ||
if (!dr[idx].sink || !dr[idx].source) | ||
return -ENOMEM; | ||
|
||
*routes = dr; | ||
*num_routes = num_dr; | ||
|
||
return 0; | ||
} | ||
|
||
static int avs_card_suspend_pre(struct snd_soc_card *card) | ||
{ | ||
//struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, RT5640_CODEC_DAI); | ||
|
||
//return snd_soc_component_set_jack(codec_dai->component, NULL, NULL); | ||
return 0; | ||
} | ||
|
||
static int avs_card_resume_post(struct snd_soc_card *card) | ||
{ | ||
//struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, RT5640_CODEC_DAI); | ||
//struct snd_soc_jack *jack = snd_soc_card_get_drvdata(card); | ||
|
||
//return snd_soc_component_set_jack(codec_dai->component, jack, NULL); | ||
return 0; | ||
} | ||
|
||
static int avs_rt5640_probe(struct platform_device *pdev) | ||
{ | ||
struct snd_soc_dapm_route *routes; | ||
struct snd_soc_dai_link *dai_link; | ||
struct snd_soc_acpi_mach *mach; | ||
struct snd_soc_card *card; | ||
struct snd_soc_jack *jack; | ||
struct device *dev = &pdev->dev; | ||
const char *pname; | ||
int num_routes, ssp_port, ret; | ||
|
||
mach = dev_get_platdata(dev); | ||
pname = mach->mach_params.platform; | ||
ssp_port = __ffs(mach->mach_params.i2s_link_mask); | ||
|
||
ret = avs_create_dai_link(dev, pname, ssp_port, mach, &dai_link); | ||
if (ret) { | ||
dev_err(dev, "Failed to create dai link: %d", ret); | ||
return ret; | ||
} | ||
|
||
ret = avs_create_dapm_routes(dev, ssp_port, &routes, &num_routes); | ||
if (ret) { | ||
dev_err(dev, "Failed to create dapm routes: %d", ret); | ||
return ret; | ||
} | ||
|
||
jack = devm_kzalloc(dev, sizeof(*jack), GFP_KERNEL); | ||
card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL); | ||
if (!jack || !card) | ||
return -ENOMEM; | ||
|
||
card->name = dev_name(&pdev->dev); | ||
card->dev = dev; | ||
card->owner = THIS_MODULE; | ||
card->suspend_pre = avs_card_suspend_pre; | ||
card->resume_post = avs_card_resume_post; | ||
card->dai_link = dai_link; | ||
card->num_links = 1; | ||
card->dapm_widgets = card_widgets; | ||
card->num_dapm_widgets = ARRAY_SIZE(card_widgets); | ||
card->dapm_routes = routes; | ||
card->num_dapm_routes = num_routes; | ||
card->fully_routed = true; | ||
snd_soc_card_set_drvdata(card, jack); | ||
|
||
ret = snd_soc_fixup_dai_links_platform_name(card, pname); | ||
if (ret) | ||
return ret; | ||
|
||
return devm_snd_soc_register_card(dev, card); | ||
} | ||
|
||
static struct platform_driver avs_rt5640_driver = { | ||
.probe = avs_rt5640_probe, | ||
.driver = { | ||
.name = "avs_rt5640", | ||
.pm = &snd_soc_pm_ops, | ||
}, | ||
}; | ||
|
||
module_platform_driver(avs_rt5640_driver); | ||
|
||
MODULE_LICENSE("GPL"); | ||
MODULE_ALIAS("platform:avs_rt5640"); |