Path: blob/master/sound/soc/intel/boards/cht_bsw_rt5672.c
26493 views
// SPDX-License-Identifier: GPL-2.0-only1/*2* cht_bsw_rt5672.c - ASoc Machine driver for Intel Cherryview-based platforms3* Cherrytrail and Braswell, with RT5672 codec.4*5* Copyright (C) 2014 Intel Corp6* Author: Subhransu S. Prusty <[email protected]>7* Mengdong Lin <[email protected]>8*/910#include <linux/gpio/consumer.h>11#include <linux/input.h>12#include <linux/module.h>13#include <linux/platform_device.h>14#include <linux/slab.h>15#include <linux/clk.h>16#include <linux/string.h>17#include <sound/pcm.h>18#include <sound/pcm_params.h>19#include <sound/soc.h>20#include <sound/jack.h>21#include <sound/soc-acpi.h>22#include "../../codecs/rt5670.h"23#include "../atom/sst-atom-controls.h"24#include "../common/soc-intel-quirks.h"252627/* The platform clock #3 outputs 19.2Mhz clock to codec as I2S MCLK */28#define CHT_PLAT_CLK_3_HZ 1920000029#define CHT_CODEC_DAI "rt5670-aif1"3031struct cht_mc_private {32struct snd_soc_jack headset;33char codec_name[SND_ACPI_I2C_ID_LEN];34struct clk *mclk;35bool use_ssp0;36};3738/* Headset jack detection DAPM pins */39static struct snd_soc_jack_pin cht_bsw_headset_pins[] = {40{41.pin = "Headset Mic",42.mask = SND_JACK_MICROPHONE,43},44{45.pin = "Headphone",46.mask = SND_JACK_HEADPHONE,47},48};4950static int platform_clock_control(struct snd_soc_dapm_widget *w,51struct snd_kcontrol *k, int event)52{53struct snd_soc_dapm_context *dapm = w->dapm;54struct snd_soc_card *card = dapm->card;55struct snd_soc_dai *codec_dai;56struct cht_mc_private *ctx = snd_soc_card_get_drvdata(card);57int ret;5859codec_dai = snd_soc_card_get_codec_dai(card, CHT_CODEC_DAI);60if (!codec_dai) {61dev_err(card->dev, "Codec dai not found; Unable to set platform clock\n");62return -EIO;63}6465if (SND_SOC_DAPM_EVENT_ON(event)) {66if (ctx->mclk) {67ret = clk_prepare_enable(ctx->mclk);68if (ret < 0) {69dev_err(card->dev,70"could not configure MCLK state");71return ret;72}73}7475/* set codec PLL source to the 19.2MHz platform clock (MCLK) */76ret = snd_soc_dai_set_pll(codec_dai, 0, RT5670_PLL1_S_MCLK,77CHT_PLAT_CLK_3_HZ, 48000 * 512);78if (ret < 0) {79dev_err(card->dev, "can't set codec pll: %d\n", ret);80return ret;81}8283/* set codec sysclk source to PLL */84ret = snd_soc_dai_set_sysclk(codec_dai, RT5670_SCLK_S_PLL1,8548000 * 512, SND_SOC_CLOCK_IN);86if (ret < 0) {87dev_err(card->dev, "can't set codec sysclk: %d\n", ret);88return ret;89}90} else {91/* Set codec sysclk source to its internal clock because codec92* PLL will be off when idle and MCLK will also be off by ACPI93* when codec is runtime suspended. Codec needs clock for jack94* detection and button press.95*/96ret = snd_soc_dai_set_sysclk(codec_dai, RT5670_SCLK_S_RCCLK,9748000 * 512, SND_SOC_CLOCK_IN);98if (ret < 0) {99dev_err(card->dev, "failed to set codec sysclk: %d\n", ret);100return ret;101}102103if (ctx->mclk)104clk_disable_unprepare(ctx->mclk);105}106return 0;107}108109static const struct snd_soc_dapm_widget cht_dapm_widgets[] = {110SND_SOC_DAPM_HP("Headphone", NULL),111SND_SOC_DAPM_MIC("Headset Mic", NULL),112SND_SOC_DAPM_MIC("Int Mic", NULL),113SND_SOC_DAPM_SPK("Ext Spk", NULL),114SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,115platform_clock_control, SND_SOC_DAPM_PRE_PMU |116SND_SOC_DAPM_POST_PMD),117};118119static const struct snd_soc_dapm_route cht_audio_map[] = {120{"IN1P", NULL, "Headset Mic"},121{"IN1N", NULL, "Headset Mic"},122{"DMIC L1", NULL, "Int Mic"},123{"DMIC R1", NULL, "Int Mic"},124{"Headphone", NULL, "HPOL"},125{"Headphone", NULL, "HPOR"},126{"Ext Spk", NULL, "SPOLP"},127{"Ext Spk", NULL, "SPOLN"},128{"Ext Spk", NULL, "SPORP"},129{"Ext Spk", NULL, "SPORN"},130{"Headphone", NULL, "Platform Clock"},131{"Headset Mic", NULL, "Platform Clock"},132{"Int Mic", NULL, "Platform Clock"},133{"Ext Spk", NULL, "Platform Clock"},134};135136static const struct snd_soc_dapm_route cht_audio_ssp0_map[] = {137{"AIF1 Playback", NULL, "ssp0 Tx"},138{"ssp0 Tx", NULL, "modem_out"},139{"modem_in", NULL, "ssp0 Rx"},140{"ssp0 Rx", NULL, "AIF1 Capture"},141};142143static const struct snd_soc_dapm_route cht_audio_ssp2_map[] = {144{"AIF1 Playback", NULL, "ssp2 Tx"},145{"ssp2 Tx", NULL, "codec_out0"},146{"ssp2 Tx", NULL, "codec_out1"},147{"codec_in0", NULL, "ssp2 Rx"},148{"codec_in1", NULL, "ssp2 Rx"},149{"ssp2 Rx", NULL, "AIF1 Capture"},150};151152static const struct snd_kcontrol_new cht_mc_controls[] = {153SOC_DAPM_PIN_SWITCH("Headphone"),154SOC_DAPM_PIN_SWITCH("Headset Mic"),155SOC_DAPM_PIN_SWITCH("Int Mic"),156SOC_DAPM_PIN_SWITCH("Ext Spk"),157};158159static int cht_aif1_hw_params(struct snd_pcm_substream *substream,160struct snd_pcm_hw_params *params)161{162struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);163struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);164int ret;165166/* set codec PLL source to the 19.2MHz platform clock (MCLK) */167ret = snd_soc_dai_set_pll(codec_dai, 0, RT5670_PLL1_S_MCLK,168CHT_PLAT_CLK_3_HZ, params_rate(params) * 512);169if (ret < 0) {170dev_err(rtd->dev, "can't set codec pll: %d\n", ret);171return ret;172}173174/* set codec sysclk source to PLL */175ret = snd_soc_dai_set_sysclk(codec_dai, RT5670_SCLK_S_PLL1,176params_rate(params) * 512,177SND_SOC_CLOCK_IN);178if (ret < 0) {179dev_err(rtd->dev, "can't set codec sysclk: %d\n", ret);180return ret;181}182return 0;183}184185static const struct acpi_gpio_params headset_gpios = { 0, 0, false };186187static const struct acpi_gpio_mapping cht_rt5672_gpios[] = {188{ "headset-gpios", &headset_gpios, 1 },189{},190};191192static int cht_codec_init(struct snd_soc_pcm_runtime *runtime)193{194int ret;195struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(runtime, 0);196struct snd_soc_component *component = codec_dai->component;197struct cht_mc_private *ctx = snd_soc_card_get_drvdata(runtime->card);198199if (devm_acpi_dev_add_driver_gpios(component->dev, cht_rt5672_gpios))200dev_warn(runtime->dev, "Unable to add GPIO mapping table\n");201202/* Select codec ASRC clock source to track I2S1 clock, because codec203* is in slave mode and 100fs I2S format (BCLK = 100 * LRCLK) cannot204* be supported by RT5672. Otherwise, ASRC will be disabled and cause205* noise.206*/207rt5670_sel_asrc_clk_src(component,208RT5670_DA_STEREO_FILTER209| RT5670_DA_MONO_L_FILTER210| RT5670_DA_MONO_R_FILTER211| RT5670_AD_STEREO_FILTER212| RT5670_AD_MONO_L_FILTER213| RT5670_AD_MONO_R_FILTER,214RT5670_CLK_SEL_I2S1_ASRC);215216if (ctx->use_ssp0) {217ret = snd_soc_dapm_add_routes(&runtime->card->dapm,218cht_audio_ssp0_map,219ARRAY_SIZE(cht_audio_ssp0_map));220} else {221ret = snd_soc_dapm_add_routes(&runtime->card->dapm,222cht_audio_ssp2_map,223ARRAY_SIZE(cht_audio_ssp2_map));224}225if (ret)226return ret;227228ret = snd_soc_card_jack_new_pins(runtime->card, "Headset",229SND_JACK_HEADSET | SND_JACK_BTN_0 |230SND_JACK_BTN_1 | SND_JACK_BTN_2,231&ctx->headset,232cht_bsw_headset_pins,233ARRAY_SIZE(cht_bsw_headset_pins));234if (ret)235return ret;236237snd_jack_set_key(ctx->headset.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);238snd_jack_set_key(ctx->headset.jack, SND_JACK_BTN_1, KEY_VOLUMEUP);239snd_jack_set_key(ctx->headset.jack, SND_JACK_BTN_2, KEY_VOLUMEDOWN);240241rt5670_set_jack_detect(component, &ctx->headset);242if (ctx->mclk) {243/*244* The firmware might enable the clock at245* boot (this information may or may not246* be reflected in the enable clock register).247* To change the rate we must disable the clock248* first to cover these cases. Due to common249* clock framework restrictions that do not allow250* to disable a clock that has not been enabled,251* we need to enable the clock first.252*/253ret = clk_prepare_enable(ctx->mclk);254if (!ret)255clk_disable_unprepare(ctx->mclk);256257ret = clk_set_rate(ctx->mclk, CHT_PLAT_CLK_3_HZ);258259if (ret) {260dev_err(runtime->dev, "unable to set MCLK rate\n");261return ret;262}263}264return 0;265}266267static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd,268struct snd_pcm_hw_params *params)269{270struct cht_mc_private *ctx = snd_soc_card_get_drvdata(rtd->card);271struct snd_interval *rate = hw_param_interval(params,272SNDRV_PCM_HW_PARAM_RATE);273struct snd_interval *channels = hw_param_interval(params,274SNDRV_PCM_HW_PARAM_CHANNELS);275int ret, bits;276277/* The DSP will convert the FE rate to 48k, stereo, 24bits */278rate->min = rate->max = 48000;279channels->min = channels->max = 2;280281if (ctx->use_ssp0) {282/* set SSP0 to 16-bit */283params_set_format(params, SNDRV_PCM_FORMAT_S16_LE);284bits = 16;285} else {286/* set SSP2 to 24-bit */287params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);288bits = 24;289}290291/*292* The default mode for the cpu-dai is TDM 4 slot. The default mode293* for the codec-dai is I2S. So we need to either set the cpu-dai to294* I2S mode to match the codec-dai, or set the codec-dai to TDM 4 slot295* (or program both to yet another mode).296* One board, the Lenovo Miix 2 10, uses not 1 but 2 codecs connected297* to SSP2. The second piggy-backed, output-only codec is inside the298* keyboard-dock (which has extra speakers). Unlike the main rt5672299* codec, we cannot configure this codec, it is hard coded to use300* 2 channel 24 bit I2S. For this to work we must use I2S mode on this301* board. Since we only support 2 channels anyways, there is no need302* for TDM on any cht-bsw-rt5672 designs. So we use I2S 2ch everywhere.303*/304ret = snd_soc_dai_set_fmt(snd_soc_rtd_to_cpu(rtd, 0),305SND_SOC_DAIFMT_I2S |306SND_SOC_DAIFMT_NB_NF |307SND_SOC_DAIFMT_BP_FP);308if (ret < 0) {309dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret);310return ret;311}312313ret = snd_soc_dai_set_tdm_slot(snd_soc_rtd_to_cpu(rtd, 0), 0x3, 0x3, 2, bits);314if (ret < 0) {315dev_err(rtd->dev, "can't set I2S config, err %d\n", ret);316return ret;317}318319return 0;320}321322static int cht_aif1_startup(struct snd_pcm_substream *substream)323{324return snd_pcm_hw_constraint_single(substream->runtime,325SNDRV_PCM_HW_PARAM_RATE, 48000);326}327328static const struct snd_soc_ops cht_aif1_ops = {329.startup = cht_aif1_startup,330};331332static const struct snd_soc_ops cht_be_ssp2_ops = {333.hw_params = cht_aif1_hw_params,334};335336SND_SOC_DAILINK_DEF(dummy,337DAILINK_COMP_ARRAY(COMP_DUMMY()));338339SND_SOC_DAILINK_DEF(media,340DAILINK_COMP_ARRAY(COMP_CPU("media-cpu-dai")));341342SND_SOC_DAILINK_DEF(deepbuffer,343DAILINK_COMP_ARRAY(COMP_CPU("deepbuffer-cpu-dai")));344345SND_SOC_DAILINK_DEF(ssp2_port,346DAILINK_COMP_ARRAY(COMP_CPU("ssp2-port")));347SND_SOC_DAILINK_DEF(ssp2_codec,348DAILINK_COMP_ARRAY(COMP_CODEC("i2c-10EC5670:00",349"rt5670-aif1")));350351SND_SOC_DAILINK_DEF(platform,352DAILINK_COMP_ARRAY(COMP_PLATFORM("sst-mfld-platform")));353354static struct snd_soc_dai_link cht_dailink[] = {355/* Front End DAI links */356[MERR_DPCM_AUDIO] = {357.name = "Audio Port",358.stream_name = "Audio",359.nonatomic = true,360.dynamic = 1,361.ops = &cht_aif1_ops,362SND_SOC_DAILINK_REG(media, dummy, platform),363},364[MERR_DPCM_DEEP_BUFFER] = {365.name = "Deep-Buffer Audio Port",366.stream_name = "Deep-Buffer Audio",367.nonatomic = true,368.dynamic = 1,369.playback_only = 1,370.ops = &cht_aif1_ops,371SND_SOC_DAILINK_REG(deepbuffer, dummy, platform),372},373374/* Back End DAI links */375{376/* SSP2 - Codec */377.name = "SSP2-Codec",378.id = 0,379.no_pcm = 1,380.init = cht_codec_init,381.be_hw_params_fixup = cht_codec_fixup,382.ops = &cht_be_ssp2_ops,383SND_SOC_DAILINK_REG(ssp2_port, ssp2_codec, platform),384},385};386387static int cht_suspend_pre(struct snd_soc_card *card)388{389struct snd_soc_component *component;390struct cht_mc_private *ctx = snd_soc_card_get_drvdata(card);391392for_each_card_components(card, component) {393if (!strncmp(component->name,394ctx->codec_name, sizeof(ctx->codec_name))) {395396dev_dbg(component->dev, "disabling jack detect before going to suspend.\n");397rt5670_jack_suspend(component);398break;399}400}401return 0;402}403404static int cht_resume_post(struct snd_soc_card *card)405{406struct snd_soc_component *component;407struct cht_mc_private *ctx = snd_soc_card_get_drvdata(card);408409for_each_card_components(card, component) {410if (!strncmp(component->name,411ctx->codec_name, sizeof(ctx->codec_name))) {412413dev_dbg(component->dev, "enabling jack detect for resume.\n");414rt5670_jack_resume(component);415break;416}417}418419return 0;420}421422/* use space before codec name to simplify card ID, and simplify driver name */423#define SOF_CARD_NAME "bytcht rt5672" /* card name will be 'sof-bytcht rt5672' */424#define SOF_DRIVER_NAME "SOF"425426#define CARD_NAME "cht-bsw-rt5672"427#define DRIVER_NAME NULL /* card name will be used for driver name */428429/* SoC card */430static struct snd_soc_card snd_soc_card_cht = {431.owner = THIS_MODULE,432.dai_link = cht_dailink,433.num_links = ARRAY_SIZE(cht_dailink),434.dapm_widgets = cht_dapm_widgets,435.num_dapm_widgets = ARRAY_SIZE(cht_dapm_widgets),436.dapm_routes = cht_audio_map,437.num_dapm_routes = ARRAY_SIZE(cht_audio_map),438.controls = cht_mc_controls,439.num_controls = ARRAY_SIZE(cht_mc_controls),440.suspend_pre = cht_suspend_pre,441.resume_post = cht_resume_post,442};443444#define RT5672_I2C_DEFAULT "i2c-10EC5670:00"445446static int snd_cht_mc_probe(struct platform_device *pdev)447{448int ret_val = 0;449struct cht_mc_private *drv;450struct snd_soc_acpi_mach *mach = pdev->dev.platform_data;451const char *platform_name;452struct acpi_device *adev;453bool sof_parent;454int dai_index = 0;455int i;456457drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);458if (!drv)459return -ENOMEM;460461strscpy(drv->codec_name, RT5672_I2C_DEFAULT, sizeof(drv->codec_name));462463/* find index of codec dai */464for (i = 0; i < ARRAY_SIZE(cht_dailink); i++) {465if (cht_dailink[i].num_codecs &&466!strcmp(cht_dailink[i].codecs->name, RT5672_I2C_DEFAULT)) {467dai_index = i;468break;469}470}471472/* fixup codec name based on HID */473adev = acpi_dev_get_first_match_dev(mach->id, NULL, -1);474if (adev) {475snprintf(drv->codec_name, sizeof(drv->codec_name),476"i2c-%s", acpi_dev_name(adev));477cht_dailink[dai_index].codecs->name = drv->codec_name;478} else {479dev_err(&pdev->dev, "Error cannot find '%s' dev\n", mach->id);480return -ENOENT;481}482483acpi_dev_put(adev);484485/* Use SSP0 on Bay Trail CR devices */486if (soc_intel_is_byt() && mach->mach_params.acpi_ipc_irq_index == 0) {487cht_dailink[dai_index].cpus->dai_name = "ssp0-port";488drv->use_ssp0 = true;489}490491/* override platform name, if required */492snd_soc_card_cht.dev = &pdev->dev;493platform_name = mach->mach_params.platform;494495ret_val = snd_soc_fixup_dai_links_platform_name(&snd_soc_card_cht,496platform_name);497if (ret_val)498return ret_val;499500snd_soc_card_cht.components = rt5670_components();501502drv->mclk = devm_clk_get(&pdev->dev, "pmc_plt_clk_3");503if (IS_ERR(drv->mclk)) {504dev_err(&pdev->dev,505"Failed to get MCLK from pmc_plt_clk_3: %ld\n",506PTR_ERR(drv->mclk));507return PTR_ERR(drv->mclk);508}509snd_soc_card_set_drvdata(&snd_soc_card_cht, drv);510511sof_parent = snd_soc_acpi_sof_parent(&pdev->dev);512513/* set card and driver name */514if (sof_parent) {515snd_soc_card_cht.name = SOF_CARD_NAME;516snd_soc_card_cht.driver_name = SOF_DRIVER_NAME;517} else {518snd_soc_card_cht.name = CARD_NAME;519snd_soc_card_cht.driver_name = DRIVER_NAME;520}521522/* set pm ops */523if (sof_parent)524pdev->dev.driver->pm = &snd_soc_pm_ops;525526/* register the soc card */527ret_val = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_cht);528if (ret_val) {529dev_err(&pdev->dev,530"snd_soc_register_card failed %d\n", ret_val);531return ret_val;532}533platform_set_drvdata(pdev, &snd_soc_card_cht);534return ret_val;535}536537static struct platform_driver snd_cht_mc_driver = {538.driver = {539.name = "cht-bsw-rt5672",540},541.probe = snd_cht_mc_probe,542};543544module_platform_driver(snd_cht_mc_driver);545546MODULE_DESCRIPTION("ASoC Intel(R) Baytrail CR Machine driver");547MODULE_AUTHOR("Subhransu S. Prusty, Mengdong Lin");548MODULE_LICENSE("GPL v2");549MODULE_ALIAS("platform:cht-bsw-rt5672");550551552