198 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * bytcht_nocodec.c - ASoc Machine driver for MinnowBoard Max and Up4 * to make I2S signals observable on the Low-Speed connector. Audio codec5 * is not managed by ASoC/DAPM6 *7 * Copyright (C) 2015-2017 Intel Corp8 *9 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~10 *11 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~12 */13 14#include <linux/module.h>15#include <sound/pcm.h>16#include <sound/pcm_params.h>17#include <sound/soc.h>18#include "../atom/sst-atom-controls.h"19 20static const struct snd_soc_dapm_widget widgets[] = {21 SND_SOC_DAPM_MIC("Mic", NULL),22 SND_SOC_DAPM_SPK("Speaker", NULL),23};24 25static const struct snd_kcontrol_new controls[] = {26 SOC_DAPM_PIN_SWITCH("Mic"),27 SOC_DAPM_PIN_SWITCH("Speaker"),28};29 30static const struct snd_soc_dapm_route audio_map[] = {31 {"ssp2 Tx", NULL, "codec_out0"},32 {"ssp2 Tx", NULL, "codec_out1"},33 {"codec_in0", NULL, "ssp2 Rx"},34 {"codec_in1", NULL, "ssp2 Rx"},35 36 {"ssp2 Rx", NULL, "Mic"},37 {"Speaker", NULL, "ssp2 Tx"},38};39 40static int codec_fixup(struct snd_soc_pcm_runtime *rtd,41 struct snd_pcm_hw_params *params)42{43 struct snd_interval *rate = hw_param_interval(params,44 SNDRV_PCM_HW_PARAM_RATE);45 struct snd_interval *channels = hw_param_interval(params,46 SNDRV_PCM_HW_PARAM_CHANNELS);47 int ret;48 49 /* The DSP will convert the FE rate to 48k, stereo, 24bits */50 rate->min = rate->max = 48000;51 channels->min = channels->max = 2;52 53 /* set SSP2 to 24-bit */54 params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);55 56 /*57 * Default mode for SSP configuration is TDM 4 slot, override config58 * with explicit setting to I2S 2ch 24-bit. The word length is set with59 * dai_set_tdm_slot() since there is no other API exposed60 */61 ret = snd_soc_dai_set_fmt(snd_soc_rtd_to_cpu(rtd, 0),62 SND_SOC_DAIFMT_I2S |63 SND_SOC_DAIFMT_NB_NF |64 SND_SOC_DAIFMT_BP_FP);65 66 if (ret < 0) {67 dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret);68 return ret;69 }70 71 ret = snd_soc_dai_set_tdm_slot(snd_soc_rtd_to_cpu(rtd, 0), 0x3, 0x3, 2, 24);72 if (ret < 0) {73 dev_err(rtd->dev, "can't set I2S config, err %d\n", ret);74 return ret;75 }76 77 return 0;78}79 80static const unsigned int rates_48000[] = {81 48000,82};83 84static const struct snd_pcm_hw_constraint_list constraints_48000 = {85 .count = ARRAY_SIZE(rates_48000),86 .list = rates_48000,87};88 89static int aif1_startup(struct snd_pcm_substream *substream)90{91 return snd_pcm_hw_constraint_list(substream->runtime, 0,92 SNDRV_PCM_HW_PARAM_RATE,93 &constraints_48000);94}95 96static const struct snd_soc_ops aif1_ops = {97 .startup = aif1_startup,98};99 100SND_SOC_DAILINK_DEF(dummy,101 DAILINK_COMP_ARRAY(COMP_DUMMY()));102 103SND_SOC_DAILINK_DEF(media,104 DAILINK_COMP_ARRAY(COMP_CPU("media-cpu-dai")));105 106SND_SOC_DAILINK_DEF(deepbuffer,107 DAILINK_COMP_ARRAY(COMP_CPU("deepbuffer-cpu-dai")));108 109SND_SOC_DAILINK_DEF(ssp2_port,110 DAILINK_COMP_ARRAY(COMP_CPU("ssp2-port")));111 112SND_SOC_DAILINK_DEF(platform,113 DAILINK_COMP_ARRAY(COMP_PLATFORM("sst-mfld-platform")));114 115static struct snd_soc_dai_link dais[] = {116 [MERR_DPCM_AUDIO] = {117 .name = "Audio Port",118 .stream_name = "Audio",119 .ignore_suspend = 1,120 .nonatomic = true,121 .dynamic = 1,122 .dpcm_playback = 1,123 .dpcm_capture = 1,124 .ops = &aif1_ops,125 SND_SOC_DAILINK_REG(media, dummy, platform),126 },127 [MERR_DPCM_DEEP_BUFFER] = {128 .name = "Deep-Buffer Audio Port",129 .stream_name = "Deep-Buffer Audio",130 .ignore_suspend = 1,131 .nonatomic = true,132 .dynamic = 1,133 .dpcm_playback = 1,134 .ops = &aif1_ops,135 SND_SOC_DAILINK_REG(deepbuffer, dummy, platform),136 },137 /* CODEC<->CODEC link */138 /* back ends */139 {140 .name = "SSP2-LowSpeed Connector",141 .id = 0,142 .no_pcm = 1,143 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF144 | SND_SOC_DAIFMT_CBC_CFC,145 .be_hw_params_fixup = codec_fixup,146 .ignore_suspend = 1,147 .dpcm_playback = 1,148 .dpcm_capture = 1,149 SND_SOC_DAILINK_REG(ssp2_port, dummy, platform),150 },151};152 153/* SoC card */154static struct snd_soc_card bytcht_nocodec_card = {155 .name = "bytcht-nocodec",156 .owner = THIS_MODULE,157 .dai_link = dais,158 .num_links = ARRAY_SIZE(dais),159 .dapm_widgets = widgets,160 .num_dapm_widgets = ARRAY_SIZE(widgets),161 .dapm_routes = audio_map,162 .num_dapm_routes = ARRAY_SIZE(audio_map),163 .controls = controls,164 .num_controls = ARRAY_SIZE(controls),165 .fully_routed = true,166};167 168static int snd_bytcht_nocodec_mc_probe(struct platform_device *pdev)169{170 int ret_val = 0;171 172 /* register the soc card */173 bytcht_nocodec_card.dev = &pdev->dev;174 175 ret_val = devm_snd_soc_register_card(&pdev->dev, &bytcht_nocodec_card);176 177 if (ret_val) {178 dev_err(&pdev->dev, "devm_snd_soc_register_card failed %d\n",179 ret_val);180 return ret_val;181 }182 platform_set_drvdata(pdev, &bytcht_nocodec_card);183 return ret_val;184}185 186static struct platform_driver snd_bytcht_nocodec_mc_driver = {187 .driver = {188 .name = "bytcht_nocodec",189 },190 .probe = snd_bytcht_nocodec_mc_probe,191};192module_platform_driver(snd_bytcht_nocodec_mc_driver);193 194MODULE_DESCRIPTION("ASoC Intel(R) Baytrail/Cherrytrail Nocodec Machine driver");195MODULE_AUTHOR("Pierre-Louis Bossart <pierre-louis.bossart at linux.intel.com>");196MODULE_LICENSE("GPL v2");197MODULE_ALIAS("platform:bytcht_nocodec");198