// SPDX-License-Identifier: GPL-2.0-only
// Copyright(c) 2019-2020 Intel Corporation.
/*
* Intel SOF Machine Driver with Realtek rt5682 Codec
* and speaker codec MAX98357A or RT1015.
*/
#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/dmi.h>
#include <sound/core.h>
#include <sound/jack.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/sof.h>
#include <sound/rt5682.h>
#include <sound/rt5682s.h>
#include <sound/soc-acpi.h>
#include "../../codecs/rt5682.h"
#include "../../codecs/rt5682s.h"
#include "../../codecs/rt5645.h"
#include "../common/soc-intel-quirks.h"
#include "sof_board_helpers.h"
#include "sof_maxim_common.h"
#include "sof_realtek_common.h"
/* Driver-specific board quirks: from bit 0 to 7 */
#define SOF_RT5682_MCLK_EN BIT(0)
/* Default: MCLK on, MCLK 19.2M, SSP0 */
static unsigned long sof_rt5682_quirk = SOF_RT5682_MCLK_EN |
SOF_SSP_PORT_CODEC(0);
static int quirk_override = -1;
module_param_named(quirk, quirk_override, int, 0444);
MODULE_PARM_DESC(quirk, "Board-specific quirk override");
static int sof_rt5682_quirk_cb(const struct dmi_system_id *id)
{
sof_rt5682_quirk = (unsigned long)id->driver_data;
return 1;
}
static const struct dmi_system_id sof_rt5682_quirk_table[] = {
{
.callback = sof_rt5682_quirk_cb,
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Circuitco"),
DMI_MATCH(DMI_PRODUCT_NAME, "Minnowboard Max"),
},
.driver_data = (void *)(SOF_SSP_PORT_CODEC(2)),
},
{
.callback = sof_rt5682_quirk_cb,
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "AAEON"),
DMI_MATCH(DMI_PRODUCT_NAME, "UP-CHT01"),
},
.driver_data = (void *)(SOF_SSP_PORT_CODEC(2)),
},
{
.callback = sof_rt5682_quirk_cb,
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
DMI_MATCH(DMI_PRODUCT_NAME, "WhiskeyLake Client"),
},
.driver_data = (void *)(SOF_RT5682_MCLK_EN |
SOF_SSP_PORT_CODEC(1)),
},
{
.callback = sof_rt5682_quirk_cb,
.matches = {
DMI_MATCH(DMI_PRODUCT_FAMILY, "Google_Volteer"),
DMI_MATCH(DMI_OEM_STRING, "AUDIO-MAX98373_ALC5682I_I2S_UP4"),
},
.driver_data = (void *)(SOF_RT5682_MCLK_EN |
SOF_SSP_PORT_CODEC(0) |
SOF_SSP_PORT_AMP(2) |
SOF_NUM_IDISP_HDMI(4)),
},
{
.callback = sof_rt5682_quirk_cb,
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
DMI_MATCH(DMI_PRODUCT_NAME, "Alder Lake Client Platform"),
DMI_MATCH(DMI_OEM_STRING, "AUDIO-ADL_MAX98373_ALC5682I_I2S"),
},
.driver_data = (void *)(SOF_RT5682_MCLK_EN |
SOF_SSP_PORT_CODEC(0) |
SOF_SSP_PORT_AMP(2) |
SOF_NUM_IDISP_HDMI(4)),
},
{
.callback = sof_rt5682_quirk_cb,
.matches = {
DMI_MATCH(DMI_PRODUCT_FAMILY, "Google_Brya"),
DMI_MATCH(DMI_OEM_STRING, "AUDIO-MAX98390_ALC5682I_I2S"),
},
.driver_data = (void *)(SOF_RT5682_MCLK_EN |
SOF_SSP_PORT_CODEC(0) |
SOF_SSP_PORT_AMP(2) |
SOF_NUM_IDISP_HDMI(4)),
},
{
.callback = sof_rt5682_quirk_cb,
.matches = {
DMI_MATCH(DMI_PRODUCT_FAMILY, "Google_Brya"),
DMI_MATCH(DMI_OEM_STRING, "AUDIO-MAX98360_ALC5682I_I2S_AMP_SSP2"),
},
.driver_data = (void *)(SOF_RT5682_MCLK_EN |
SOF_SSP_PORT_CODEC(0) |
SOF_SSP_PORT_AMP(2) |
SOF_NUM_IDISP_HDMI(4)),
},
{
.callback = sof_rt5682_quirk_cb,
.matches = {
DMI_MATCH(DMI_PRODUCT_FAMILY, "Google_Rex"),
},
.driver_data = (void *)(SOF_RT5682_MCLK_EN |
SOF_SSP_PORT_CODEC(2) |
SOF_SSP_PORT_AMP(0) |
SOF_SSP_PORT_BT_OFFLOAD(1) |
SOF_BT_OFFLOAD_PRESENT
),
},
{}
};
static struct snd_soc_jack_pin jack_pins[] = {
{
.pin = "Headphone Jack",
.mask = SND_JACK_HEADPHONE,
},
{
.pin = "Headset Mic",
.mask = SND_JACK_MICROPHONE,
},
};
static int sof_rt5682_codec_init(struct snd_soc_pcm_runtime *rtd)
{
struct sof_card_private *ctx = snd_soc_card_get_drvdata(rtd->card);
struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd, 0)->component;
struct snd_soc_jack *jack = &ctx->headset_jack;
int extra_jack_data;
int ret, mclk_freq;
if (ctx->rt5682.mclk_en) {
mclk_freq = sof_dai_get_mclk(rtd);
if (mclk_freq <= 0) {