Path: blob/master/sound/soc/intel/boards/sof_maxim_common.c
26493 views
// SPDX-License-Identifier: GPL-2.0-only1//2// Copyright(c) 2020 Intel Corporation3#include <linux/module.h>4#include <linux/string.h>5#include <sound/pcm.h>6#include <sound/pcm_params.h>7#include <sound/soc.h>8#include <sound/soc-acpi.h>9#include <sound/soc-dai.h>10#include <sound/soc-dapm.h>11#include <sound/sof.h>12#include <uapi/sound/asound.h>13#include "../common/soc-intel-quirks.h"14#include "sof_maxim_common.h"1516/*17* Common structures and functions18*/19static const struct snd_kcontrol_new maxim_2spk_kcontrols[] = {20SOC_DAPM_PIN_SWITCH("Left Spk"),21SOC_DAPM_PIN_SWITCH("Right Spk"),2223};2425static const struct snd_soc_dapm_widget maxim_2spk_widgets[] = {26SND_SOC_DAPM_SPK("Left Spk", NULL),27SND_SOC_DAPM_SPK("Right Spk", NULL),28};2930/* helper function to get the number of specific codec */31static unsigned int get_num_codecs(const char *hid)32{33struct acpi_device *adev;34unsigned int dev_num = 0;3536for_each_acpi_dev_match(adev, hid, NULL, -1)37dev_num++;3839return dev_num;40}4142/*43* Maxim MAX9837344*/45#define MAX_98373_PIN_NAME 164647static const struct snd_soc_dapm_route max_98373_dapm_routes[] = {48/* speaker */49{ "Left Spk", NULL, "Left BE_OUT" },50{ "Right Spk", NULL, "Right BE_OUT" },51};5253static struct snd_soc_codec_conf max_98373_codec_conf[] = {54{55.dlc = COMP_CODEC_CONF(MAX_98373_DEV0_NAME),56.name_prefix = "Right",57},58{59.dlc = COMP_CODEC_CONF(MAX_98373_DEV1_NAME),60.name_prefix = "Left",61},62};6364static struct snd_soc_dai_link_component max_98373_components[] = {65{ /* For Right */66.name = MAX_98373_DEV0_NAME,67.dai_name = MAX_98373_CODEC_DAI,68},69{ /* For Left */70.name = MAX_98373_DEV1_NAME,71.dai_name = MAX_98373_CODEC_DAI,72},73};7475/*76* According to the definition of 'DAI Sel Mux' mixer in max98373.c, rx mask77* should choose two channels from TDM slots, the LSB of rx mask is left channel78* and the other one is right channel.79*/80static const struct {81unsigned int rx;82} max_98373_tdm_mask[] = {83{.rx = 0x3},84{.rx = 0x3},85};8687/*88* The tx mask indicates which channel(s) contains output IV-sense data and89* others should set to Hi-Z. Here we get the channel number from codec's ACPI90* device property "maxim,vmon-slot-no" and "maxim,imon-slot-no" to generate the91* mask. Refer to the max98373_slot_config() function in max98373.c codec driver.92*/93static unsigned int max_98373_get_tx_mask(struct device *dev)94{95int vmon_slot;96int imon_slot;9798if (device_property_read_u32(dev, "maxim,vmon-slot-no", &vmon_slot))99vmon_slot = 0;100101if (device_property_read_u32(dev, "maxim,imon-slot-no", &imon_slot))102imon_slot = 1;103104dev_dbg(dev, "vmon_slot %d imon_slot %d\n", vmon_slot, imon_slot);105106return (0x1 << vmon_slot) | (0x1 << imon_slot);107}108109static int max_98373_hw_params(struct snd_pcm_substream *substream,110struct snd_pcm_hw_params *params)111{112struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);113struct snd_soc_dai_link *dai_link = rtd->dai_link;114struct snd_soc_dai *codec_dai;115int i;116int tdm_slots;117unsigned int tx_mask;118unsigned int tx_mask_used = 0x0;119int ret = 0;120121for_each_rtd_codec_dais(rtd, i, codec_dai) {122if (i >= ARRAY_SIZE(max_98373_tdm_mask)) {123dev_err(codec_dai->dev, "only 2 amps are supported\n");124return -EINVAL;125}126127switch (dai_link->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) {128case SND_SOC_DAIFMT_DSP_A:129case SND_SOC_DAIFMT_DSP_B:130/* get the tplg configured tdm slot number */131tdm_slots = sof_dai_get_tdm_slots(rtd);132if (tdm_slots <= 0) {133dev_err(rtd->dev, "invalid tdm slots %d\n",134tdm_slots);135return -EINVAL;136}137138/* get the tx mask from ACPI device properties */139tx_mask = max_98373_get_tx_mask(codec_dai->dev);140if (!tx_mask)141return -EINVAL;142143if (tx_mask & tx_mask_used) {144dev_err(codec_dai->dev, "invalid tx mask 0x%x, used 0x%x\n",145tx_mask, tx_mask_used);146return -EINVAL;147}148149tx_mask_used |= tx_mask;150151/*152* check if tdm slot number is too small for channel153* allocation154*/155if (fls(tx_mask) > tdm_slots) {156dev_err(codec_dai->dev, "slot mismatch, tx %d slots %d\n",157fls(tx_mask), tdm_slots);158return -EINVAL;159}160161if (fls(max_98373_tdm_mask[i].rx) > tdm_slots) {162dev_err(codec_dai->dev, "slot mismatch, rx %d slots %d\n",163fls(max_98373_tdm_mask[i].rx), tdm_slots);164return -EINVAL;165}166167dev_dbg(codec_dai->dev, "set tdm slot: tx 0x%x rx 0x%x slots %d width %d\n",168tx_mask, max_98373_tdm_mask[i].rx,169tdm_slots, params_width(params));170171ret = snd_soc_dai_set_tdm_slot(codec_dai, tx_mask,172max_98373_tdm_mask[i].rx,173tdm_slots,174params_width(params));175if (ret < 0) {176dev_err(codec_dai->dev, "fail to set tdm slot, ret %d\n",177ret);178return ret;179}180break;181default:182dev_dbg(codec_dai->dev, "codec is in I2S mode\n");183break;184}185}186return 0;187}188189static int max_98373_trigger(struct snd_pcm_substream *substream, int cmd)190{191struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);192struct snd_soc_dai *codec_dai;193struct snd_soc_dai *cpu_dai;194int j;195int ret = 0;196197/* set spk pin by playback only */198if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)199return 0;200201cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);202for_each_rtd_codec_dais(rtd, j, codec_dai) {203struct snd_soc_dapm_context *dapm =204snd_soc_component_get_dapm(cpu_dai->component);205char pin_name[MAX_98373_PIN_NAME];206207snprintf(pin_name, ARRAY_SIZE(pin_name), "%s Spk",208codec_dai->component->name_prefix);209210switch (cmd) {211case SNDRV_PCM_TRIGGER_START:212case SNDRV_PCM_TRIGGER_RESUME:213case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:214ret = snd_soc_dapm_enable_pin(dapm, pin_name);215if (!ret)216snd_soc_dapm_sync(dapm);217break;218case SNDRV_PCM_TRIGGER_STOP:219case SNDRV_PCM_TRIGGER_SUSPEND:220case SNDRV_PCM_TRIGGER_PAUSE_PUSH:221ret = snd_soc_dapm_disable_pin(dapm, pin_name);222if (!ret)223snd_soc_dapm_sync(dapm);224break;225default:226break;227}228}229230return ret;231}232233static const struct snd_soc_ops max_98373_ops = {234.hw_params = max_98373_hw_params,235.trigger = max_98373_trigger,236};237238static int max_98373_spk_codec_init(struct snd_soc_pcm_runtime *rtd)239{240struct snd_soc_card *card = rtd->card;241unsigned int num_codecs = get_num_codecs(MAX_98373_ACPI_HID);242int ret;243244switch (num_codecs) {245case 2:246ret = snd_soc_dapm_new_controls(&card->dapm, maxim_2spk_widgets,247ARRAY_SIZE(maxim_2spk_widgets));248if (ret) {249dev_err(rtd->dev, "fail to add max98373 widgets, ret %d\n",250ret);251return ret;252}253254ret = snd_soc_add_card_controls(card, maxim_2spk_kcontrols,255ARRAY_SIZE(maxim_2spk_kcontrols));256if (ret) {257dev_err(rtd->dev, "fail to add max98373 kcontrols, ret %d\n",258ret);259return ret;260}261262ret = snd_soc_dapm_add_routes(&card->dapm, max_98373_dapm_routes,263ARRAY_SIZE(max_98373_dapm_routes));264if (ret) {265dev_err(rtd->dev, "fail to add max98373 routes, ret %d\n",266ret);267return ret;268}269break;270default:271dev_err(rtd->dev, "max98373: invalid num_codecs %d\n", num_codecs);272return -EINVAL;273}274275return ret;276}277278void max_98373_dai_link(struct device *dev, struct snd_soc_dai_link *link)279{280link->codecs = max_98373_components;281link->num_codecs = ARRAY_SIZE(max_98373_components);282link->init = max_98373_spk_codec_init;283link->ops = &max_98373_ops;284}285EXPORT_SYMBOL_NS(max_98373_dai_link, "SND_SOC_INTEL_SOF_MAXIM_COMMON");286287void max_98373_set_codec_conf(struct snd_soc_card *card)288{289card->codec_conf = max_98373_codec_conf;290card->num_configs = ARRAY_SIZE(max_98373_codec_conf);291}292EXPORT_SYMBOL_NS(max_98373_set_codec_conf, "SND_SOC_INTEL_SOF_MAXIM_COMMON");293294/*295* Maxim MAX98390296*/297static const struct snd_soc_dapm_route max_98390_dapm_routes[] = {298/* speaker */299{ "Left Spk", NULL, "Left BE_OUT" },300{ "Right Spk", NULL, "Right BE_OUT" },301};302303static const struct snd_kcontrol_new max_98390_tt_kcontrols[] = {304SOC_DAPM_PIN_SWITCH("TL Spk"),305SOC_DAPM_PIN_SWITCH("TR Spk"),306};307308static const struct snd_soc_dapm_widget max_98390_tt_dapm_widgets[] = {309SND_SOC_DAPM_SPK("TL Spk", NULL),310SND_SOC_DAPM_SPK("TR Spk", NULL),311};312313static const struct snd_soc_dapm_route max_98390_tt_dapm_routes[] = {314/* Tweeter speaker */315{ "TL Spk", NULL, "Tweeter Left BE_OUT" },316{ "TR Spk", NULL, "Tweeter Right BE_OUT" },317};318319static struct snd_soc_codec_conf max_98390_cml_codec_conf[] = {320{321.dlc = COMP_CODEC_CONF(MAX_98390_DEV0_NAME),322.name_prefix = "Left",323},324{325.dlc = COMP_CODEC_CONF(MAX_98390_DEV1_NAME),326.name_prefix = "Right",327},328};329330static struct snd_soc_codec_conf max_98390_codec_conf[] = {331{332.dlc = COMP_CODEC_CONF(MAX_98390_DEV0_NAME),333.name_prefix = "Right",334},335{336.dlc = COMP_CODEC_CONF(MAX_98390_DEV1_NAME),337.name_prefix = "Left",338},339{340.dlc = COMP_CODEC_CONF(MAX_98390_DEV2_NAME),341.name_prefix = "Tweeter Right",342},343{344.dlc = COMP_CODEC_CONF(MAX_98390_DEV3_NAME),345.name_prefix = "Tweeter Left",346},347};348349static struct snd_soc_dai_link_component max_98390_components[] = {350{351.name = MAX_98390_DEV0_NAME,352.dai_name = MAX_98390_CODEC_DAI,353},354{355.name = MAX_98390_DEV1_NAME,356.dai_name = MAX_98390_CODEC_DAI,357},358{359.name = MAX_98390_DEV2_NAME,360.dai_name = MAX_98390_CODEC_DAI,361},362{363.name = MAX_98390_DEV3_NAME,364.dai_name = MAX_98390_CODEC_DAI,365},366};367368static const struct {369unsigned int tx;370unsigned int rx;371} max_98390_tdm_mask[] = {372{.tx = 0x01, .rx = 0x3},373{.tx = 0x02, .rx = 0x3},374{.tx = 0x04, .rx = 0x3},375{.tx = 0x08, .rx = 0x3},376};377378static int max_98390_hw_params(struct snd_pcm_substream *substream,379struct snd_pcm_hw_params *params)380{381struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);382struct snd_soc_dai_link *dai_link = rtd->dai_link;383struct snd_soc_dai *codec_dai;384int i, ret;385386for_each_rtd_codec_dais(rtd, i, codec_dai) {387if (i >= ARRAY_SIZE(max_98390_tdm_mask)) {388dev_err(codec_dai->dev, "invalid codec index %d\n", i);389return -ENODEV;390}391392switch (dai_link->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) {393case SND_SOC_DAIFMT_DSP_A:394case SND_SOC_DAIFMT_DSP_B:395/* 4-slot TDM */396ret = snd_soc_dai_set_tdm_slot(codec_dai,397max_98390_tdm_mask[i].tx,398max_98390_tdm_mask[i].rx,3994,400params_width(params));401if (ret < 0) {402dev_err(codec_dai->dev, "fail to set tdm slot, ret %d\n",403ret);404return ret;405}406break;407default:408dev_dbg(codec_dai->dev, "codec is in I2S mode\n");409break;410}411}412return 0;413}414415static int max_98390_init(struct snd_soc_pcm_runtime *rtd)416{417struct snd_soc_card *card = rtd->card;418unsigned int num_codecs = get_num_codecs(MAX_98390_ACPI_HID);419int ret;420421switch (num_codecs) {422case 4:423/* add widgets/controls/dapm for tweeter speakers */424ret = snd_soc_dapm_new_controls(&card->dapm, max_98390_tt_dapm_widgets,425ARRAY_SIZE(max_98390_tt_dapm_widgets));426if (ret) {427dev_err(rtd->dev, "unable to add tweeter dapm widgets, ret %d\n",428ret);429/* Don't need to add routes if widget addition failed */430return ret;431}432433ret = snd_soc_add_card_controls(card, max_98390_tt_kcontrols,434ARRAY_SIZE(max_98390_tt_kcontrols));435if (ret) {436dev_err(rtd->dev, "unable to add tweeter controls, ret %d\n",437ret);438return ret;439}440441ret = snd_soc_dapm_add_routes(&card->dapm, max_98390_tt_dapm_routes,442ARRAY_SIZE(max_98390_tt_dapm_routes));443if (ret) {444dev_err(rtd->dev, "unable to add tweeter dapm routes, ret %d\n",445ret);446return ret;447}448449fallthrough;450case 2:451/* add regular speakers dapm route */452ret = snd_soc_dapm_new_controls(&card->dapm, maxim_2spk_widgets,453ARRAY_SIZE(maxim_2spk_widgets));454if (ret) {455dev_err(rtd->dev, "fail to add max98390 woofer widgets, ret %d\n",456ret);457return ret;458}459460ret = snd_soc_add_card_controls(card, maxim_2spk_kcontrols,461ARRAY_SIZE(maxim_2spk_kcontrols));462if (ret) {463dev_err(rtd->dev, "fail to add max98390 woofer kcontrols, ret %d\n",464ret);465return ret;466}467468ret = snd_soc_dapm_add_routes(&card->dapm, max_98390_dapm_routes,469ARRAY_SIZE(max_98390_dapm_routes));470if (ret) {471dev_err(rtd->dev, "unable to add dapm routes, ret %d\n",472ret);473return ret;474}475break;476default:477dev_err(rtd->dev, "invalid codec number %d\n", num_codecs);478return -EINVAL;479}480481return ret;482}483484static const struct snd_soc_ops max_98390_ops = {485.hw_params = max_98390_hw_params,486};487488void max_98390_dai_link(struct device *dev, struct snd_soc_dai_link *link)489{490unsigned int num_codecs = get_num_codecs(MAX_98390_ACPI_HID);491492link->codecs = max_98390_components;493494switch (num_codecs) {495case 2:496case 4:497link->num_codecs = num_codecs;498break;499default:500dev_err(dev, "invalid codec number %d for %s\n", num_codecs,501MAX_98390_ACPI_HID);502break;503}504505link->init = max_98390_init;506link->ops = &max_98390_ops;507}508EXPORT_SYMBOL_NS(max_98390_dai_link, "SND_SOC_INTEL_SOF_MAXIM_COMMON");509510void max_98390_set_codec_conf(struct device *dev, struct snd_soc_card *card)511{512unsigned int num_codecs = get_num_codecs(MAX_98390_ACPI_HID);513514card->codec_conf = max_98390_codec_conf;515516switch (num_codecs) {517case 2:518if (soc_intel_is_cml())519card->codec_conf = max_98390_cml_codec_conf;520521fallthrough;522case 4:523card->num_configs = num_codecs;524break;525default:526dev_err(dev, "invalid codec number %d for %s\n", num_codecs,527MAX_98390_ACPI_HID);528break;529}530}531EXPORT_SYMBOL_NS(max_98390_set_codec_conf, "SND_SOC_INTEL_SOF_MAXIM_COMMON");532533/*534* Maxim MAX98357A/MAX98360A535*/536static const struct snd_kcontrol_new max_98357a_kcontrols[] = {537SOC_DAPM_PIN_SWITCH("Spk"),538};539540static const struct snd_soc_dapm_widget max_98357a_dapm_widgets[] = {541SND_SOC_DAPM_SPK("Spk", NULL),542};543544static const struct snd_soc_dapm_route max_98357a_dapm_routes[] = {545/* speaker */546{"Spk", NULL, "Speaker"},547};548549static struct snd_soc_dai_link_component max_98357a_components[] = {550{551.name = MAX_98357A_DEV0_NAME,552.dai_name = MAX_98357A_CODEC_DAI,553}554};555556static struct snd_soc_dai_link_component max_98360a_components[] = {557{558.name = MAX_98360A_DEV0_NAME,559.dai_name = MAX_98357A_CODEC_DAI,560}561};562563static int max_98357a_init(struct snd_soc_pcm_runtime *rtd)564{565struct snd_soc_card *card = rtd->card;566int ret;567568ret = snd_soc_dapm_new_controls(&card->dapm, max_98357a_dapm_widgets,569ARRAY_SIZE(max_98357a_dapm_widgets));570if (ret) {571dev_err(rtd->dev, "unable to add dapm controls, ret %d\n", ret);572/* Don't need to add routes if widget addition failed */573return ret;574}575576ret = snd_soc_add_card_controls(card, max_98357a_kcontrols,577ARRAY_SIZE(max_98357a_kcontrols));578if (ret) {579dev_err(rtd->dev, "unable to add card controls, ret %d\n", ret);580return ret;581}582583ret = snd_soc_dapm_add_routes(&card->dapm, max_98357a_dapm_routes,584ARRAY_SIZE(max_98357a_dapm_routes));585586if (ret)587dev_err(rtd->dev, "unable to add dapm routes, ret %d\n", ret);588589return ret;590}591592void max_98357a_dai_link(struct snd_soc_dai_link *link)593{594link->codecs = max_98357a_components;595link->num_codecs = ARRAY_SIZE(max_98357a_components);596link->init = max_98357a_init;597}598EXPORT_SYMBOL_NS(max_98357a_dai_link, "SND_SOC_INTEL_SOF_MAXIM_COMMON");599600void max_98360a_dai_link(struct snd_soc_dai_link *link)601{602link->codecs = max_98360a_components;603link->num_codecs = ARRAY_SIZE(max_98360a_components);604link->init = max_98357a_init;605}606EXPORT_SYMBOL_NS(max_98360a_dai_link, "SND_SOC_INTEL_SOF_MAXIM_COMMON");607608MODULE_DESCRIPTION("ASoC Intel SOF Maxim helpers");609MODULE_LICENSE("GPL");610611612