This machine driver provides support for different configurations: RT700, RT711, RT1308 (1x and 2x, I2S or SoundWire mode), and RT715 CometLake, Icelake, TigerLake. PDM digital microphones HDMI To avoid introducing one driver per configuration, this common machine driver relies on platform-specific information, tables and quirks to dynamically create the relevant dailinks. Unlike a lot of machine drivers, we use different DAI links for SoundWire capture and playback since the Cadence PDIs can do capture OR playback, not both simultaneously. For each configuration, the card component string is updated so that UCM can select the relevant parts. Signed-off-by: Rander Wang <rander.wang@linux.intel.com> Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com> Signed-off-by: Bard Liao <yung-chuan.liao@linux.intel.com> Link: https://lore.kernel.org/r/20200325220746.29601-3-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown <broonie@kernel.org>
151 lines
3.7 KiB
C
151 lines
3.7 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
// Copyright (c) 2020 Intel Corporation
|
|
|
|
/*
|
|
* sof_sdw_rt1308 - Helpers to handle RT1308 from generic machine driver
|
|
*/
|
|
|
|
#include <linux/device.h>
|
|
#include <linux/errno.h>
|
|
#include <sound/soc.h>
|
|
#include <sound/soc-acpi.h>
|
|
#include "sof_sdw_common.h"
|
|
#include "../../codecs/rt1308.h"
|
|
|
|
static const struct snd_soc_dapm_widget rt1308_widgets[] = {
|
|
SND_SOC_DAPM_SPK("Speaker", NULL),
|
|
};
|
|
|
|
/*
|
|
* dapm routes for rt1308 will be registered dynamically according
|
|
* to the number of rt1308 used. The first two entries will be registered
|
|
* for one codec case, and the last two entries are also registered
|
|
* if two 1308s are used.
|
|
*/
|
|
static const struct snd_soc_dapm_route rt1308_map[] = {
|
|
{ "Speaker", NULL, "rt1308-1 SPOL" },
|
|
{ "Speaker", NULL, "rt1308-1 SPOR" },
|
|
{ "Speaker", NULL, "rt1308-2 SPOL" },
|
|
{ "Speaker", NULL, "rt1308-2 SPOR" },
|
|
};
|
|
|
|
static const struct snd_kcontrol_new rt1308_controls[] = {
|
|
SOC_DAPM_PIN_SWITCH("Speaker"),
|
|
};
|
|
|
|
static int first_spk_init(struct snd_soc_pcm_runtime *rtd)
|
|
{
|
|
struct snd_soc_card *card = rtd->card;
|
|
int ret;
|
|
|
|
card->components = devm_kasprintf(card->dev, GFP_KERNEL,
|
|
"%s spk:rt1308",
|
|
card->components);
|
|
if (!card->components)
|
|
return -ENOMEM;
|
|
|
|
ret = snd_soc_add_card_controls(card, rt1308_controls,
|
|
ARRAY_SIZE(rt1308_controls));
|
|
if (ret) {
|
|
dev_err(card->dev, "rt1308 controls addition failed: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
ret = snd_soc_dapm_new_controls(&card->dapm, rt1308_widgets,
|
|
ARRAY_SIZE(rt1308_widgets));
|
|
if (ret) {
|
|
dev_err(card->dev, "rt1308 widgets addition failed: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
ret = snd_soc_dapm_add_routes(&card->dapm, rt1308_map, 2);
|
|
if (ret)
|
|
dev_err(rtd->dev, "failed to add first SPK map: %d\n", ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int second_spk_init(struct snd_soc_pcm_runtime *rtd)
|
|
{
|
|
struct snd_soc_card *card = rtd->card;
|
|
int ret;
|
|
|
|
ret = snd_soc_dapm_add_routes(&card->dapm, rt1308_map + 2, 2);
|
|
if (ret)
|
|
dev_err(rtd->dev, "failed to add second SPK map: %d\n", ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int all_spk_init(struct snd_soc_pcm_runtime *rtd)
|
|
{
|
|
int ret;
|
|
|
|
ret = first_spk_init(rtd);
|
|
if (ret)
|
|
return ret;
|
|
|
|
return second_spk_init(rtd);
|
|
}
|
|
|
|
static int rt1308_i2s_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_card *card = rtd->card;
|
|
struct snd_soc_dai *codec_dai = rtd->codec_dai;
|
|
int clk_id, clk_freq, pll_out;
|
|
int err;
|
|
|
|
clk_id = RT1308_PLL_S_MCLK;
|
|
clk_freq = 38400000;
|
|
|
|
pll_out = params_rate(params) * 512;
|
|
|
|
/* Set rt1308 pll */
|
|
err = snd_soc_dai_set_pll(codec_dai, 0, clk_id, clk_freq, pll_out);
|
|
if (err < 0) {
|
|
dev_err(card->dev, "Failed to set RT1308 PLL: %d\n", err);
|
|
return err;
|
|
}
|
|
|
|
/* Set rt1308 sysclk */
|
|
err = snd_soc_dai_set_sysclk(codec_dai, RT1308_FS_SYS_S_PLL, pll_out,
|
|
SND_SOC_CLOCK_IN);
|
|
if (err < 0) {
|
|
dev_err(card->dev, "Failed to set RT1308 SYSCLK: %d\n", err);
|
|
return err;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* machine stream operations */
|
|
struct snd_soc_ops sof_sdw_rt1308_i2s_ops = {
|
|
.hw_params = rt1308_i2s_hw_params,
|
|
};
|
|
|
|
int sof_sdw_rt1308_init(const struct snd_soc_acpi_link_adr *link,
|
|
struct snd_soc_dai_link *dai_links,
|
|
struct sof_sdw_codec_info *info,
|
|
bool playback)
|
|
{
|
|
info->amp_num++;
|
|
if (info->amp_num == 1)
|
|
dai_links->init = first_spk_init;
|
|
|
|
if (info->amp_num == 2) {
|
|
/*
|
|
* if two 1308s are in one dai link, the init function
|
|
* in this dai link will be first set for the first speaker,
|
|
* and it should be reset to initialize all speakers when
|
|
* the second speaker is found.
|
|
*/
|
|
if (dai_links->init)
|
|
dai_links->init = all_spk_init;
|
|
else
|
|
dai_links->init = second_spk_init;
|
|
}
|
|
|
|
return 0;
|
|
}
|