312 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * bytcht-da7213.c - ASoc Machine driver for Intel Baytrail and4 * Cherrytrail-based platforms, with Dialog DA7213 codec5 *6 * Copyright (C) 2017 Intel Corporation7 * Author: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>8 *9 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~10 *11 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~12 */13 14#include <linux/module.h>15#include <linux/acpi.h>16#include <linux/platform_device.h>17#include <linux/slab.h>18#include <sound/pcm.h>19#include <sound/pcm_params.h>20#include <sound/soc.h>21#include <sound/soc-acpi.h>22#include "../../codecs/da7213.h"23#include "../atom/sst-atom-controls.h"24 25static const struct snd_kcontrol_new controls[] = {26 SOC_DAPM_PIN_SWITCH("Headphone Jack"),27 SOC_DAPM_PIN_SWITCH("Headset Mic"),28 SOC_DAPM_PIN_SWITCH("Mic"),29 SOC_DAPM_PIN_SWITCH("Aux In"),30};31 32static const struct snd_soc_dapm_widget dapm_widgets[] = {33 SND_SOC_DAPM_HP("Headphone Jack", NULL),34 SND_SOC_DAPM_MIC("Headset Mic", NULL),35 SND_SOC_DAPM_MIC("Mic", NULL),36 SND_SOC_DAPM_LINE("Aux In", NULL),37};38 39static const struct snd_soc_dapm_route audio_map[] = {40 {"Headphone Jack", NULL, "HPL"},41 {"Headphone Jack", NULL, "HPR"},42 43 {"AUXL", NULL, "Aux In"},44 {"AUXR", NULL, "Aux In"},45 46 /* Assume Mic1 is linked to Headset and Mic2 to on-board mic */47 {"MIC1", NULL, "Headset Mic"},48 {"MIC2", NULL, "Mic"},49 50 /* SOC-codec link */51 {"ssp2 Tx", NULL, "codec_out0"},52 {"ssp2 Tx", NULL, "codec_out1"},53 {"codec_in0", NULL, "ssp2 Rx"},54 {"codec_in1", NULL, "ssp2 Rx"},55 56 {"Playback", NULL, "ssp2 Tx"},57 {"ssp2 Rx", NULL, "Capture"},58};59 60static int codec_fixup(struct snd_soc_pcm_runtime *rtd,61 struct snd_pcm_hw_params *params)62{63 int ret;64 struct snd_interval *rate = hw_param_interval(params,65 SNDRV_PCM_HW_PARAM_RATE);66 struct snd_interval *channels = hw_param_interval(params,67 SNDRV_PCM_HW_PARAM_CHANNELS);68 69 /* The DSP will convert the FE rate to 48k, stereo, 24bits */70 rate->min = rate->max = 48000;71 channels->min = channels->max = 2;72 73 /* set SSP2 to 24-bit */74 params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);75 76 /*77 * Default mode for SSP configuration is TDM 4 slot, override config78 * with explicit setting to I2S 2ch 24-bit. The word length is set with79 * dai_set_tdm_slot() since there is no other API exposed80 */81 ret = snd_soc_dai_set_fmt(snd_soc_rtd_to_cpu(rtd, 0),82 SND_SOC_DAIFMT_I2S |83 SND_SOC_DAIFMT_NB_NF |84 SND_SOC_DAIFMT_BP_FP);85 if (ret < 0) {86 dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret);87 return ret;88 }89 90 ret = snd_soc_dai_set_tdm_slot(snd_soc_rtd_to_cpu(rtd, 0), 0x3, 0x3, 2, 24);91 if (ret < 0) {92 dev_err(rtd->dev, "can't set I2S config, err %d\n", ret);93 return ret;94 }95 96 return 0;97}98 99static int aif1_startup(struct snd_pcm_substream *substream)100{101 return snd_pcm_hw_constraint_single(substream->runtime,102 SNDRV_PCM_HW_PARAM_RATE, 48000);103}104 105static int aif1_hw_params(struct snd_pcm_substream *substream,106 struct snd_pcm_hw_params *params)107{108 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);109 struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);110 int ret;111 112 ret = snd_soc_dai_set_sysclk(codec_dai, DA7213_CLKSRC_MCLK,113 19200000, SND_SOC_CLOCK_IN);114 if (ret < 0)115 dev_err(codec_dai->dev, "can't set codec sysclk configuration\n");116 117 ret = snd_soc_dai_set_pll(codec_dai, 0,118 DA7213_SYSCLK_PLL_SRM, 0, DA7213_PLL_FREQ_OUT_98304000);119 if (ret < 0) {120 dev_err(codec_dai->dev, "failed to start PLL: %d\n", ret);121 return -EIO;122 }123 124 return ret;125}126 127static int aif1_hw_free(struct snd_pcm_substream *substream)128{129 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);130 struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);131 int ret;132 133 ret = snd_soc_dai_set_pll(codec_dai, 0,134 DA7213_SYSCLK_MCLK, 0, 0);135 if (ret < 0) {136 dev_err(codec_dai->dev, "failed to stop PLL: %d\n", ret);137 return -EIO;138 }139 140 return ret;141}142 143static const struct snd_soc_ops aif1_ops = {144 .startup = aif1_startup,145};146 147static const struct snd_soc_ops ssp2_ops = {148 .hw_params = aif1_hw_params,149 .hw_free = aif1_hw_free,150 151};152 153SND_SOC_DAILINK_DEF(dummy,154 DAILINK_COMP_ARRAY(COMP_DUMMY()));155 156SND_SOC_DAILINK_DEF(media,157 DAILINK_COMP_ARRAY(COMP_CPU("media-cpu-dai")));158 159SND_SOC_DAILINK_DEF(deepbuffer,160 DAILINK_COMP_ARRAY(COMP_CPU("deepbuffer-cpu-dai")));161 162SND_SOC_DAILINK_DEF(ssp2_port,163 DAILINK_COMP_ARRAY(COMP_CPU("ssp2-port")));164SND_SOC_DAILINK_DEF(ssp2_codec,165 DAILINK_COMP_ARRAY(COMP_CODEC("i2c-DLGS7213:00",166 "da7213-hifi")));167 168SND_SOC_DAILINK_DEF(platform,169 DAILINK_COMP_ARRAY(COMP_PLATFORM("sst-mfld-platform")));170 171static struct snd_soc_dai_link dailink[] = {172 [MERR_DPCM_AUDIO] = {173 .name = "Audio Port",174 .stream_name = "Audio",175 .nonatomic = true,176 .dynamic = 1,177 .dpcm_playback = 1,178 .dpcm_capture = 1,179 .ops = &aif1_ops,180 SND_SOC_DAILINK_REG(media, dummy, platform),181 },182 [MERR_DPCM_DEEP_BUFFER] = {183 .name = "Deep-Buffer Audio Port",184 .stream_name = "Deep-Buffer Audio",185 .nonatomic = true,186 .dynamic = 1,187 .dpcm_playback = 1,188 .ops = &aif1_ops,189 SND_SOC_DAILINK_REG(deepbuffer, dummy, platform),190 },191 /* CODEC<->CODEC link */192 /* back ends */193 {194 .name = "SSP2-Codec",195 .id = 0,196 .no_pcm = 1,197 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF198 | SND_SOC_DAIFMT_CBC_CFC,199 .be_hw_params_fixup = codec_fixup,200 .dpcm_playback = 1,201 .dpcm_capture = 1,202 .ops = &ssp2_ops,203 SND_SOC_DAILINK_REG(ssp2_port, ssp2_codec, platform),204 },205};206 207/* use space before codec name to simplify card ID, and simplify driver name */208#define SOF_CARD_NAME "bytcht da7213" /* card name will be 'sof-bytcht da7213' */209#define SOF_DRIVER_NAME "SOF"210 211#define CARD_NAME "bytcht-da7213"212#define DRIVER_NAME NULL /* card name will be used for driver name */213 214/* SoC card */215static struct snd_soc_card bytcht_da7213_card = {216 .name = CARD_NAME,217 .driver_name = DRIVER_NAME,218 .owner = THIS_MODULE,219 .dai_link = dailink,220 .num_links = ARRAY_SIZE(dailink),221 .controls = controls,222 .num_controls = ARRAY_SIZE(controls),223 .dapm_widgets = dapm_widgets,224 .num_dapm_widgets = ARRAY_SIZE(dapm_widgets),225 .dapm_routes = audio_map,226 .num_dapm_routes = ARRAY_SIZE(audio_map),227};228 229static char codec_name[SND_ACPI_I2C_ID_LEN];230 231static int bytcht_da7213_probe(struct platform_device *pdev)232{233 struct snd_soc_card *card;234 struct snd_soc_acpi_mach *mach;235 const char *platform_name;236 struct acpi_device *adev;237 bool sof_parent;238 int dai_index = 0;239 int ret_val = 0;240 int i;241 242 mach = pdev->dev.platform_data;243 card = &bytcht_da7213_card;244 card->dev = &pdev->dev;245 246 /* fix index of codec dai */247 for (i = 0; i < ARRAY_SIZE(dailink); i++) {248 if (dailink[i].num_codecs &&249 !strcmp(dailink[i].codecs->name, "i2c-DLGS7213:00")) {250 dai_index = i;251 break;252 }253 }254 255 /* fixup codec name based on HID */256 adev = acpi_dev_get_first_match_dev(mach->id, NULL, -1);257 if (adev) {258 snprintf(codec_name, sizeof(codec_name),259 "i2c-%s", acpi_dev_name(adev));260 dailink[dai_index].codecs->name = codec_name;261 } else {262 dev_err(&pdev->dev, "Error cannot find '%s' dev\n", mach->id);263 return -ENOENT;264 }265 266 acpi_dev_put(adev);267 268 /* override platform name, if required */269 platform_name = mach->mach_params.platform;270 271 ret_val = snd_soc_fixup_dai_links_platform_name(card, platform_name);272 if (ret_val)273 return ret_val;274 275 sof_parent = snd_soc_acpi_sof_parent(&pdev->dev);276 277 /* set card and driver name */278 if (sof_parent) {279 bytcht_da7213_card.name = SOF_CARD_NAME;280 bytcht_da7213_card.driver_name = SOF_DRIVER_NAME;281 } else {282 bytcht_da7213_card.name = CARD_NAME;283 bytcht_da7213_card.driver_name = DRIVER_NAME;284 }285 286 /* set pm ops */287 if (sof_parent)288 pdev->dev.driver->pm = &snd_soc_pm_ops;289 290 ret_val = devm_snd_soc_register_card(&pdev->dev, card);291 if (ret_val) {292 dev_err(&pdev->dev,293 "snd_soc_register_card failed %d\n", ret_val);294 return ret_val;295 }296 platform_set_drvdata(pdev, card);297 return ret_val;298}299 300static struct platform_driver bytcht_da7213_driver = {301 .driver = {302 .name = "bytcht_da7213",303 },304 .probe = bytcht_da7213_probe,305};306module_platform_driver(bytcht_da7213_driver);307 308MODULE_DESCRIPTION("ASoC Intel(R) Baytrail/Cherrytrail+DA7213 Machine driver");309MODULE_AUTHOR("Pierre-Louis Bossart");310MODULE_LICENSE("GPL v2");311MODULE_ALIAS("platform:bytcht_da7213");312