Path: blob/master/sound/soc/intel/boards/cht_bsw_max98090_ti.c
26493 views
// SPDX-License-Identifier: GPL-2.0-only1/*2* cht-bsw-max98090.c - ASoc Machine driver for Intel Cherryview-based3* platforms Cherrytrail and Braswell, with max98090 & TI codec.4*5* Copyright (C) 2015 Intel Corp6* Author: Fang, Yang A <[email protected]>7* This file is modified from cht_bsw_rt5645.c8* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~9*10* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~11*/1213#include <linux/dmi.h>14#include <linux/gpio/consumer.h>15#include <linux/module.h>16#include <linux/platform_device.h>17#include <linux/slab.h>18#include <linux/acpi.h>19#include <linux/clk.h>20#include <sound/pcm.h>21#include <sound/pcm_params.h>22#include <sound/soc.h>23#include <sound/soc-acpi.h>24#include <sound/jack.h>25#include "../../codecs/max98090.h"26#include "../atom/sst-atom-controls.h"27#include "../../codecs/ts3a227e.h"2829#define CHT_PLAT_CLK_3_HZ 1920000030#define CHT_CODEC_DAI "HiFi"3132#define QUIRK_PMC_PLT_CLK_0 0x013334struct cht_mc_private {35struct clk *mclk;36struct snd_soc_jack jack;37bool ts3a227e_present;38int quirks;39};4041static int platform_clock_control(struct snd_soc_dapm_widget *w,42struct snd_kcontrol *k, int event)43{44struct snd_soc_dapm_context *dapm = w->dapm;45struct snd_soc_card *card = dapm->card;46struct snd_soc_dai *codec_dai;47struct cht_mc_private *ctx = snd_soc_card_get_drvdata(card);48int ret;4950/* See the comment in snd_cht_mc_probe() */51if (ctx->quirks & QUIRK_PMC_PLT_CLK_0)52return 0;5354codec_dai = snd_soc_card_get_codec_dai(card, CHT_CODEC_DAI);55if (!codec_dai) {56dev_err(card->dev, "Codec dai not found; Unable to set platform clock\n");57return -EIO;58}5960if (SND_SOC_DAPM_EVENT_ON(event)) {61ret = clk_prepare_enable(ctx->mclk);62if (ret < 0) {63dev_err(card->dev,64"could not configure MCLK state");65return ret;66}67} else {68clk_disable_unprepare(ctx->mclk);69}7071return 0;72}7374static const struct snd_soc_dapm_widget cht_dapm_widgets[] = {75SND_SOC_DAPM_HP("Headphone", NULL),76SND_SOC_DAPM_MIC("Headset Mic", NULL),77SND_SOC_DAPM_MIC("Int Mic", NULL),78SND_SOC_DAPM_SPK("Ext Spk", NULL),79SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,80platform_clock_control, SND_SOC_DAPM_PRE_PMU |81SND_SOC_DAPM_POST_PMD),82};8384static const struct snd_soc_dapm_route cht_audio_map[] = {85{"IN34", NULL, "Headset Mic"},86{"Headset Mic", NULL, "MICBIAS"},87{"DMICL", NULL, "Int Mic"},88{"Headphone", NULL, "HPL"},89{"Headphone", NULL, "HPR"},90{"Ext Spk", NULL, "SPKL"},91{"Ext Spk", NULL, "SPKR"},92{"HiFi Playback", NULL, "ssp2 Tx"},93{"ssp2 Tx", NULL, "codec_out0"},94{"ssp2 Tx", NULL, "codec_out1"},95{"codec_in0", NULL, "ssp2 Rx" },96{"codec_in1", NULL, "ssp2 Rx" },97{"ssp2 Rx", NULL, "HiFi Capture"},98{"Headphone", NULL, "Platform Clock"},99{"Headset Mic", NULL, "Platform Clock"},100{"Int Mic", NULL, "Platform Clock"},101{"Ext Spk", NULL, "Platform Clock"},102};103104static const struct snd_kcontrol_new cht_mc_controls[] = {105SOC_DAPM_PIN_SWITCH("Headphone"),106SOC_DAPM_PIN_SWITCH("Headset Mic"),107SOC_DAPM_PIN_SWITCH("Int Mic"),108SOC_DAPM_PIN_SWITCH("Ext Spk"),109};110111static int cht_aif1_hw_params(struct snd_pcm_substream *substream,112struct snd_pcm_hw_params *params)113{114struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);115struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);116int ret;117118ret = snd_soc_dai_set_sysclk(codec_dai, M98090_REG_SYSTEM_CLOCK,119CHT_PLAT_CLK_3_HZ, SND_SOC_CLOCK_IN);120if (ret < 0) {121dev_err(rtd->dev, "can't set codec sysclk: %d\n", ret);122return ret;123}124125return 0;126}127128static int cht_ti_jack_event(struct notifier_block *nb,129unsigned long event, void *data)130{131struct snd_soc_jack *jack = (struct snd_soc_jack *)data;132struct snd_soc_dapm_context *dapm = &jack->card->dapm;133134if (event & SND_JACK_MICROPHONE) {135snd_soc_dapm_force_enable_pin(dapm, "SHDN");136snd_soc_dapm_force_enable_pin(dapm, "MICBIAS");137snd_soc_dapm_sync(dapm);138} else {139snd_soc_dapm_disable_pin(dapm, "MICBIAS");140snd_soc_dapm_disable_pin(dapm, "SHDN");141snd_soc_dapm_sync(dapm);142}143144return 0;145}146147static struct notifier_block cht_jack_nb = {148.notifier_call = cht_ti_jack_event,149};150151static struct snd_soc_jack_pin hs_jack_pins[] = {152{153.pin = "Headphone",154.mask = SND_JACK_HEADPHONE,155},156{157.pin = "Headset Mic",158.mask = SND_JACK_MICROPHONE,159},160};161162static struct snd_soc_jack_gpio hs_jack_gpios[] = {163{164.name = "hp",165.report = SND_JACK_HEADPHONE | SND_JACK_LINEOUT,166.debounce_time = 200,167},168{169.name = "mic",170.invert = 1,171.report = SND_JACK_MICROPHONE,172.debounce_time = 200,173},174};175176static const struct acpi_gpio_params hp_gpios = { 0, 0, false };177static const struct acpi_gpio_params mic_gpios = { 1, 0, false };178179static const struct acpi_gpio_mapping acpi_max98090_gpios[] = {180{ "hp-gpios", &hp_gpios, 1 },181{ "mic-gpios", &mic_gpios, 1 },182{},183};184185static int cht_codec_init(struct snd_soc_pcm_runtime *runtime)186{187int ret;188int jack_type;189struct cht_mc_private *ctx = snd_soc_card_get_drvdata(runtime->card);190struct snd_soc_jack *jack = &ctx->jack;191192if (ctx->ts3a227e_present) {193/*194* The jack has already been created in the195* cht_max98090_headset_init() function.196*/197snd_soc_jack_notifier_register(jack, &cht_jack_nb);198return 0;199}200201jack_type = SND_JACK_HEADPHONE | SND_JACK_MICROPHONE;202203ret = snd_soc_card_jack_new_pins(runtime->card, "Headset Jack",204jack_type, jack,205hs_jack_pins,206ARRAY_SIZE(hs_jack_pins));207if (ret) {208dev_err(runtime->dev, "Headset Jack creation failed %d\n", ret);209return ret;210}211212ret = snd_soc_jack_add_gpiods(runtime->card->dev->parent, jack,213ARRAY_SIZE(hs_jack_gpios),214hs_jack_gpios);215if (ret) {216/*217* flag error but don't bail if jack detect is broken218* due to platform issues or bad BIOS/configuration219*/220dev_err(runtime->dev,221"jack detection gpios not added, error %d\n", ret);222}223224/* See the comment in snd_cht_mc_probe() */225if (ctx->quirks & QUIRK_PMC_PLT_CLK_0)226return 0;227228/*229* The firmware might enable the clock at230* boot (this information may or may not231* be reflected in the enable clock register).232* To change the rate we must disable the clock233* first to cover these cases. Due to common234* clock framework restrictions that do not allow235* to disable a clock that has not been enabled,236* we need to enable the clock first.237*/238ret = clk_prepare_enable(ctx->mclk);239if (!ret)240clk_disable_unprepare(ctx->mclk);241242ret = clk_set_rate(ctx->mclk, CHT_PLAT_CLK_3_HZ);243244if (ret)245dev_err(runtime->dev, "unable to set MCLK rate\n");246247return ret;248}249250static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd,251struct snd_pcm_hw_params *params)252{253struct snd_interval *rate = hw_param_interval(params,254SNDRV_PCM_HW_PARAM_RATE);255struct snd_interval *channels = hw_param_interval(params,256SNDRV_PCM_HW_PARAM_CHANNELS);257int ret = 0;258unsigned int fmt = 0;259260ret = snd_soc_dai_set_tdm_slot(snd_soc_rtd_to_cpu(rtd, 0), 0x3, 0x3, 2, 16);261if (ret < 0) {262dev_err(rtd->dev, "can't set cpu_dai slot fmt: %d\n", ret);263return ret;264}265266fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_BP_FP;267268ret = snd_soc_dai_set_fmt(snd_soc_rtd_to_cpu(rtd, 0), fmt);269if (ret < 0) {270dev_err(rtd->dev, "can't set cpu_dai set fmt: %d\n", ret);271return ret;272}273274/* The DSP will convert the FE rate to 48k, stereo, 24bits */275rate->min = rate->max = 48000;276channels->min = channels->max = 2;277278/* set SSP2 to 16-bit */279params_set_format(params, SNDRV_PCM_FORMAT_S16_LE);280return 0;281}282283static int cht_aif1_startup(struct snd_pcm_substream *substream)284{285return snd_pcm_hw_constraint_single(substream->runtime,286SNDRV_PCM_HW_PARAM_RATE, 48000);287}288289static int cht_max98090_headset_init(struct snd_soc_component *component)290{291struct snd_soc_card *card = component->card;292struct cht_mc_private *ctx = snd_soc_card_get_drvdata(card);293struct snd_soc_jack *jack = &ctx->jack;294int jack_type;295int ret;296297/*298* TI supports 4 buttons headset detection299* KEY_MEDIA300* KEY_VOICECOMMAND301* KEY_VOLUMEUP302* KEY_VOLUMEDOWN303*/304jack_type = SND_JACK_HEADPHONE | SND_JACK_MICROPHONE |305SND_JACK_BTN_0 | SND_JACK_BTN_1 |306SND_JACK_BTN_2 | SND_JACK_BTN_3;307308ret = snd_soc_card_jack_new(card, "Headset Jack", jack_type, jack);309if (ret) {310dev_err(card->dev, "Headset Jack creation failed %d\n", ret);311return ret;312}313314return ts3a227e_enable_jack_detect(component, jack);315}316317static const struct snd_soc_ops cht_aif1_ops = {318.startup = cht_aif1_startup,319};320321static const struct snd_soc_ops cht_be_ssp2_ops = {322.hw_params = cht_aif1_hw_params,323};324325static struct snd_soc_aux_dev cht_max98090_headset_dev = {326.dlc = COMP_AUX("i2c-104C227E:00"),327.init = cht_max98090_headset_init,328};329330SND_SOC_DAILINK_DEF(dummy,331DAILINK_COMP_ARRAY(COMP_DUMMY()));332333SND_SOC_DAILINK_DEF(media,334DAILINK_COMP_ARRAY(COMP_CPU("media-cpu-dai")));335336SND_SOC_DAILINK_DEF(deepbuffer,337DAILINK_COMP_ARRAY(COMP_CPU("deepbuffer-cpu-dai")));338339SND_SOC_DAILINK_DEF(ssp2_port,340DAILINK_COMP_ARRAY(COMP_CPU("ssp2-port")));341SND_SOC_DAILINK_DEF(ssp2_codec,342DAILINK_COMP_ARRAY(COMP_CODEC("i2c-193C9890:00", "HiFi")));343344SND_SOC_DAILINK_DEF(platform,345DAILINK_COMP_ARRAY(COMP_PLATFORM("sst-mfld-platform")));346347static struct snd_soc_dai_link cht_dailink[] = {348[MERR_DPCM_AUDIO] = {349.name = "Audio Port",350.stream_name = "Audio",351.nonatomic = true,352.dynamic = 1,353.ops = &cht_aif1_ops,354SND_SOC_DAILINK_REG(media, dummy, platform),355},356[MERR_DPCM_DEEP_BUFFER] = {357.name = "Deep-Buffer Audio Port",358.stream_name = "Deep-Buffer Audio",359.nonatomic = true,360.dynamic = 1,361.playback_only = 1,362.ops = &cht_aif1_ops,363SND_SOC_DAILINK_REG(deepbuffer, dummy, platform),364},365/* back ends */366{367.name = "SSP2-Codec",368.id = 0,369.no_pcm = 1,370.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF371| SND_SOC_DAIFMT_CBC_CFC,372.init = cht_codec_init,373.be_hw_params_fixup = cht_codec_fixup,374.ops = &cht_be_ssp2_ops,375SND_SOC_DAILINK_REG(ssp2_port, ssp2_codec, platform),376},377};378379/* use space before codec name to simplify card ID, and simplify driver name */380#define SOF_CARD_NAME "bytcht max98090" /* card name will be 'sof-bytcht max98090 */381#define SOF_DRIVER_NAME "SOF"382383#define CARD_NAME "chtmax98090"384#define DRIVER_NAME NULL /* card name will be used for driver name */385386/* SoC card */387static struct snd_soc_card snd_soc_card_cht = {388.owner = THIS_MODULE,389.dai_link = cht_dailink,390.num_links = ARRAY_SIZE(cht_dailink),391.aux_dev = &cht_max98090_headset_dev,392.num_aux_devs = 1,393.dapm_widgets = cht_dapm_widgets,394.num_dapm_widgets = ARRAY_SIZE(cht_dapm_widgets),395.dapm_routes = cht_audio_map,396.num_dapm_routes = ARRAY_SIZE(cht_audio_map),397.controls = cht_mc_controls,398.num_controls = ARRAY_SIZE(cht_mc_controls),399};400401static const struct dmi_system_id cht_max98090_quirk_table[] = {402{403/* Banjo model Chromebook */404.matches = {405DMI_MATCH(DMI_PRODUCT_NAME, "Banjo"),406},407.driver_data = (void *)QUIRK_PMC_PLT_CLK_0,408},409{410/* Candy model Chromebook */411.matches = {412DMI_MATCH(DMI_PRODUCT_NAME, "Candy"),413},414.driver_data = (void *)QUIRK_PMC_PLT_CLK_0,415},416{417/* Clapper model Chromebook */418.matches = {419DMI_MATCH(DMI_PRODUCT_NAME, "Clapper"),420},421.driver_data = (void *)QUIRK_PMC_PLT_CLK_0,422},423{424/* Cyan model Chromebook */425.matches = {426DMI_MATCH(DMI_PRODUCT_NAME, "Cyan"),427},428.driver_data = (void *)QUIRK_PMC_PLT_CLK_0,429},430{431/* Enguarde model Chromebook */432.matches = {433DMI_MATCH(DMI_PRODUCT_NAME, "Enguarde"),434},435.driver_data = (void *)QUIRK_PMC_PLT_CLK_0,436},437{438/* Glimmer model Chromebook */439.matches = {440DMI_MATCH(DMI_PRODUCT_NAME, "Glimmer"),441},442.driver_data = (void *)QUIRK_PMC_PLT_CLK_0,443},444{445/* Gnawty model Chromebook (Acer Chromebook CB3-111) */446.matches = {447DMI_MATCH(DMI_PRODUCT_NAME, "Gnawty"),448},449.driver_data = (void *)QUIRK_PMC_PLT_CLK_0,450},451{452/* Heli model Chromebook */453.matches = {454DMI_MATCH(DMI_PRODUCT_NAME, "Heli"),455},456.driver_data = (void *)QUIRK_PMC_PLT_CLK_0,457},458{459/* Kip model Chromebook */460.matches = {461DMI_MATCH(DMI_PRODUCT_NAME, "Kip"),462},463.driver_data = (void *)QUIRK_PMC_PLT_CLK_0,464},465{466/* Ninja model Chromebook */467.matches = {468DMI_MATCH(DMI_PRODUCT_NAME, "Ninja"),469},470.driver_data = (void *)QUIRK_PMC_PLT_CLK_0,471},472{473/* Orco model Chromebook */474.matches = {475DMI_MATCH(DMI_PRODUCT_NAME, "Orco"),476},477.driver_data = (void *)QUIRK_PMC_PLT_CLK_0,478},479{480/* Quawks model Chromebook */481.matches = {482DMI_MATCH(DMI_PRODUCT_NAME, "Quawks"),483},484.driver_data = (void *)QUIRK_PMC_PLT_CLK_0,485},486{487/* Rambi model Chromebook */488.matches = {489DMI_MATCH(DMI_PRODUCT_NAME, "Rambi"),490},491.driver_data = (void *)QUIRK_PMC_PLT_CLK_0,492},493{494/* Squawks model Chromebook */495.matches = {496DMI_MATCH(DMI_PRODUCT_NAME, "Squawks"),497},498.driver_data = (void *)QUIRK_PMC_PLT_CLK_0,499},500{501/* Sumo model Chromebook */502.matches = {503DMI_MATCH(DMI_PRODUCT_NAME, "Sumo"),504},505.driver_data = (void *)QUIRK_PMC_PLT_CLK_0,506},507{508/* Swanky model Chromebook (Toshiba Chromebook 2) */509.matches = {510DMI_MATCH(DMI_PRODUCT_NAME, "Swanky"),511},512.driver_data = (void *)QUIRK_PMC_PLT_CLK_0,513},514{515/* Winky model Chromebook */516.matches = {517DMI_MATCH(DMI_PRODUCT_NAME, "Winky"),518},519.driver_data = (void *)QUIRK_PMC_PLT_CLK_0,520},521{}522};523524static int snd_cht_mc_probe(struct platform_device *pdev)525{526const struct dmi_system_id *dmi_id;527struct device *dev = &pdev->dev;528int ret_val = 0;529struct cht_mc_private *drv;530const char *mclk_name;531struct snd_soc_acpi_mach *mach;532const char *platform_name;533bool sof_parent;534535drv = devm_kzalloc(dev, sizeof(*drv), GFP_KERNEL);536if (!drv)537return -ENOMEM;538539dmi_id = dmi_first_match(cht_max98090_quirk_table);540if (dmi_id)541drv->quirks = (unsigned long)dmi_id->driver_data;542543drv->ts3a227e_present = acpi_dev_found("104C227E");544if (!drv->ts3a227e_present) {545/* no need probe TI jack detection chip */546snd_soc_card_cht.aux_dev = NULL;547snd_soc_card_cht.num_aux_devs = 0;548549ret_val = devm_acpi_dev_add_driver_gpios(dev->parent,550acpi_max98090_gpios);551if (ret_val)552dev_dbg(dev, "Unable to add GPIO mapping table\n");553}554555/* override platform name, if required */556snd_soc_card_cht.dev = dev;557mach = dev->platform_data;558platform_name = mach->mach_params.platform;559560ret_val = snd_soc_fixup_dai_links_platform_name(&snd_soc_card_cht,561platform_name);562if (ret_val)563return ret_val;564565/* register the soc card */566snd_soc_card_set_drvdata(&snd_soc_card_cht, drv);567568if (drv->quirks & QUIRK_PMC_PLT_CLK_0)569mclk_name = "pmc_plt_clk_0";570else571mclk_name = "pmc_plt_clk_3";572573drv->mclk = devm_clk_get(dev, mclk_name);574if (IS_ERR(drv->mclk)) {575dev_err(dev,576"Failed to get MCLK from %s: %ld\n",577mclk_name, PTR_ERR(drv->mclk));578return PTR_ERR(drv->mclk);579}580581/*582* Boards which have the MAX98090's clk connected to clk_0 do not seem583* to like it if we muck with the clock. If we disable the clock when584* it is unused we get "max98090 i2c-193C9890:00: PLL unlocked" errors585* and the PLL never seems to lock again.586* So for these boards we enable it here once and leave it at that.587*/588if (drv->quirks & QUIRK_PMC_PLT_CLK_0) {589ret_val = clk_prepare_enable(drv->mclk);590if (ret_val < 0) {591dev_err(dev, "MCLK enable error: %d\n", ret_val);592return ret_val;593}594}595596sof_parent = snd_soc_acpi_sof_parent(dev);597598/* set card and driver name */599if (sof_parent) {600snd_soc_card_cht.name = SOF_CARD_NAME;601snd_soc_card_cht.driver_name = SOF_DRIVER_NAME;602} else {603snd_soc_card_cht.name = CARD_NAME;604snd_soc_card_cht.driver_name = DRIVER_NAME;605}606607/* set pm ops */608if (sof_parent)609dev->driver->pm = &snd_soc_pm_ops;610611ret_val = devm_snd_soc_register_card(dev, &snd_soc_card_cht);612if (ret_val) {613dev_err(dev,614"snd_soc_register_card failed %d\n", ret_val);615return ret_val;616}617platform_set_drvdata(pdev, &snd_soc_card_cht);618return ret_val;619}620621static void snd_cht_mc_remove(struct platform_device *pdev)622{623struct snd_soc_card *card = platform_get_drvdata(pdev);624struct cht_mc_private *ctx = snd_soc_card_get_drvdata(card);625626if (ctx->quirks & QUIRK_PMC_PLT_CLK_0)627clk_disable_unprepare(ctx->mclk);628}629630static struct platform_driver snd_cht_mc_driver = {631.driver = {632.name = "cht-bsw-max98090",633},634.probe = snd_cht_mc_probe,635.remove = snd_cht_mc_remove,636};637638module_platform_driver(snd_cht_mc_driver)639640MODULE_DESCRIPTION("ASoC Intel(R) Braswell Machine driver");641MODULE_AUTHOR("Fang, Yang A <[email protected]>");642MODULE_LICENSE("GPL v2");643MODULE_ALIAS("platform:cht-bsw-max98090");644645646