313 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * cht-bsw-nau8824.c - ASoc Machine driver for Intel Cherryview-based4 * platforms Cherrytrail and Braswell, with nau8824 codec.5 *6 * Copyright (C) 2018 Intel Corp7 * Copyright (C) 2018 Nuvoton Technology Corp8 *9 * Author: Wang, Joseph C <joequant@gmail.com>10 * Co-author: John Hsu <KCHSU0@nuvoton.com>11 * This file is based on cht_bsw_rt5672.c and cht-bsw-max98090.c12 */13 14#include <linux/module.h>15#include <linux/platform_device.h>16#include <linux/slab.h>17#include <sound/pcm.h>18#include <sound/pcm_params.h>19#include <sound/soc.h>20#include <sound/soc-acpi.h>21#include <sound/jack.h>22#include <linux/input.h>23#include "../atom/sst-atom-controls.h"24#include "../../codecs/nau8824.h"25 26struct cht_mc_private {27 struct snd_soc_jack jack;28};29 30static struct snd_soc_jack_pin cht_bsw_jack_pins[] = {31 {32 .pin = "Headphone",33 .mask = SND_JACK_HEADPHONE,34 },35 {36 .pin = "Headset Mic",37 .mask = SND_JACK_MICROPHONE,38 },39};40 41static const struct snd_soc_dapm_widget cht_dapm_widgets[] = {42 SND_SOC_DAPM_HP("Headphone", NULL),43 SND_SOC_DAPM_MIC("Headset Mic", NULL),44 SND_SOC_DAPM_MIC("Int Mic", NULL),45 SND_SOC_DAPM_SPK("Ext Spk", NULL),46};47 48static const struct snd_soc_dapm_route cht_audio_map[] = {49 {"Ext Spk", NULL, "SPKOUTL"},50 {"Ext Spk", NULL, "SPKOUTR"},51 {"Headphone", NULL, "HPOL"},52 {"Headphone", NULL, "HPOR"},53 {"MIC1", NULL, "Int Mic"},54 {"MIC2", NULL, "Int Mic"},55 {"HSMIC1", NULL, "Headset Mic"},56 {"HSMIC2", NULL, "Headset Mic"},57 {"Playback", NULL, "ssp2 Tx"},58 {"ssp2 Tx", NULL, "codec_out0"},59 {"ssp2 Tx", NULL, "codec_out1"},60 {"codec_in0", NULL, "ssp2 Rx" },61 {"codec_in1", NULL, "ssp2 Rx" },62 {"ssp2 Rx", NULL, "Capture"},63};64 65static const struct snd_kcontrol_new cht_mc_controls[] = {66 SOC_DAPM_PIN_SWITCH("Headphone"),67 SOC_DAPM_PIN_SWITCH("Headset Mic"),68 SOC_DAPM_PIN_SWITCH("Int Mic"),69 SOC_DAPM_PIN_SWITCH("Ext Spk"),70};71 72static int cht_aif1_hw_params(struct snd_pcm_substream *substream,73 struct snd_pcm_hw_params *params)74{75 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);76 struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);77 int ret;78 79 ret = snd_soc_dai_set_sysclk(codec_dai, NAU8824_CLK_FLL_FS, 0,80 SND_SOC_CLOCK_IN);81 if (ret < 0) {82 dev_err(codec_dai->dev, "can't set FS clock %d\n", ret);83 return ret;84 }85 ret = snd_soc_dai_set_pll(codec_dai, 0, 0, params_rate(params),86 params_rate(params) * 256);87 if (ret < 0) {88 dev_err(codec_dai->dev, "can't set FLL: %d\n", ret);89 return ret;90 }91 92 return 0;93}94 95static int cht_codec_init(struct snd_soc_pcm_runtime *runtime)96{97 struct cht_mc_private *ctx = snd_soc_card_get_drvdata(runtime->card);98 struct snd_soc_jack *jack = &ctx->jack;99 struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(runtime, 0);100 struct snd_soc_component *component = codec_dai->component;101 int ret, jack_type;102 103 /* NAU88L24 supports 4 buttons headset detection104 * KEY_PLAYPAUSE105 * KEY_VOICECOMMAND106 * KEY_VOLUMEUP107 * KEY_VOLUMEDOWN108 */109 jack_type = SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 |110 SND_JACK_BTN_2 | SND_JACK_BTN_3;111 ret = snd_soc_card_jack_new_pins(runtime->card, "Headset", jack_type,112 jack, cht_bsw_jack_pins, ARRAY_SIZE(cht_bsw_jack_pins));113 if (ret) {114 dev_err(runtime->dev,115 "Headset Jack creation failed %d\n", ret);116 return ret;117 }118 snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);119 snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);120 snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEUP);121 snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);122 123 nau8824_enable_jack_detect(component, jack);124 125 return ret;126}127 128static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd,129 struct snd_pcm_hw_params *params)130{131 struct snd_interval *rate = hw_param_interval(params,132 SNDRV_PCM_HW_PARAM_RATE);133 struct snd_interval *channels = hw_param_interval(params,134 SNDRV_PCM_HW_PARAM_CHANNELS);135 struct snd_mask *fmt =136 hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);137 int ret;138 139 /* The DSP will convert the FE rate to 48k, stereo, 24bits */140 rate->min = rate->max = 48000;141 channels->min = channels->max = 2;142 143 /* set SSP2 to 24-bit */144 snd_mask_none(fmt);145 params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);146 147 /* TDM 4 slots 24 bit, set Rx & Tx bitmask to 4 active slots */148 ret = snd_soc_dai_set_tdm_slot(snd_soc_rtd_to_codec(rtd, 0), 0xf, 0x1, 4, 24);149 if (ret < 0) {150 dev_err(rtd->dev, "can't set codec TDM slot %d\n", ret);151 return ret;152 }153 154 return 0;155}156 157static int cht_aif1_startup(struct snd_pcm_substream *substream)158{159 return snd_pcm_hw_constraint_single(substream->runtime,160 SNDRV_PCM_HW_PARAM_RATE, 48000);161}162 163static const struct snd_soc_ops cht_aif1_ops = {164 .startup = cht_aif1_startup,165};166 167static const struct snd_soc_ops cht_be_ssp2_ops = {168 .hw_params = cht_aif1_hw_params,169};170 171SND_SOC_DAILINK_DEF(dummy,172 DAILINK_COMP_ARRAY(COMP_DUMMY()));173 174SND_SOC_DAILINK_DEF(media,175 DAILINK_COMP_ARRAY(COMP_CPU("media-cpu-dai")));176 177SND_SOC_DAILINK_DEF(deepbuffer,178 DAILINK_COMP_ARRAY(COMP_CPU("deepbuffer-cpu-dai")));179 180SND_SOC_DAILINK_DEF(ssp2_port,181 DAILINK_COMP_ARRAY(COMP_CPU("ssp2-port")));182SND_SOC_DAILINK_DEF(ssp2_codec,183 DAILINK_COMP_ARRAY(COMP_CODEC("i2c-10508824:00",184 NAU8824_CODEC_DAI)));185 186SND_SOC_DAILINK_DEF(platform,187 DAILINK_COMP_ARRAY(COMP_PLATFORM("sst-mfld-platform")));188 189static struct snd_soc_dai_link cht_dailink[] = {190 /* Front End DAI links */191 [MERR_DPCM_AUDIO] = {192 .name = "Audio Port",193 .stream_name = "Audio",194 .nonatomic = true,195 .dynamic = 1,196 .dpcm_playback = 1,197 .dpcm_capture = 1,198 .ops = &cht_aif1_ops,199 SND_SOC_DAILINK_REG(media, dummy, platform),200 },201 [MERR_DPCM_DEEP_BUFFER] = {202 .name = "Deep-Buffer Audio Port",203 .stream_name = "Deep-Buffer Audio",204 .nonatomic = true,205 .dynamic = 1,206 .dpcm_playback = 1,207 .ops = &cht_aif1_ops,208 SND_SOC_DAILINK_REG(deepbuffer, dummy, platform),209 },210 /* Back End DAI links */211 {212 /* SSP2 - Codec */213 .name = "SSP2-Codec",214 .id = 0,215 .no_pcm = 1,216 .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF217 | SND_SOC_DAIFMT_CBC_CFC,218 .init = cht_codec_init,219 .be_hw_params_fixup = cht_codec_fixup,220 .dpcm_playback = 1,221 .dpcm_capture = 1,222 .ops = &cht_be_ssp2_ops,223 SND_SOC_DAILINK_REG(ssp2_port, ssp2_codec, platform),224 },225};226 227/* use space before codec name to simplify card ID, and simplify driver name */228#define SOF_CARD_NAME "bytcht nau8824" /* card name will be 'sof-bytcht nau8824 */229#define SOF_DRIVER_NAME "SOF"230 231#define CARD_NAME "chtnau8824"232#define DRIVER_NAME NULL /* card name will be used for driver name */233 234/* SoC card */235static struct snd_soc_card snd_soc_card_cht = {236 .owner = THIS_MODULE,237 .dai_link = cht_dailink,238 .num_links = ARRAY_SIZE(cht_dailink),239 .dapm_widgets = cht_dapm_widgets,240 .num_dapm_widgets = ARRAY_SIZE(cht_dapm_widgets),241 .dapm_routes = cht_audio_map,242 .num_dapm_routes = ARRAY_SIZE(cht_audio_map),243 .controls = cht_mc_controls,244 .num_controls = ARRAY_SIZE(cht_mc_controls),245};246 247static int snd_cht_mc_probe(struct platform_device *pdev)248{249 struct cht_mc_private *drv;250 struct snd_soc_acpi_mach *mach;251 const char *platform_name;252 bool sof_parent;253 int ret_val;254 255 drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);256 if (!drv)257 return -ENOMEM;258 snd_soc_card_set_drvdata(&snd_soc_card_cht, drv);259 260 /* override platform name, if required */261 snd_soc_card_cht.dev = &pdev->dev;262 mach = pdev->dev.platform_data;263 platform_name = mach->mach_params.platform;264 265 ret_val = snd_soc_fixup_dai_links_platform_name(&snd_soc_card_cht,266 platform_name);267 if (ret_val)268 return ret_val;269 270 sof_parent = snd_soc_acpi_sof_parent(&pdev->dev);271 272 /* set card and driver name */273 if (sof_parent) {274 snd_soc_card_cht.name = SOF_CARD_NAME;275 snd_soc_card_cht.driver_name = SOF_DRIVER_NAME;276 } else {277 snd_soc_card_cht.name = CARD_NAME;278 snd_soc_card_cht.driver_name = DRIVER_NAME;279 }280 281 snd_soc_card_cht.components = nau8824_components();282 283 /* set pm ops */284 if (sof_parent)285 pdev->dev.driver->pm = &snd_soc_pm_ops;286 287 /* register the soc card */288 ret_val = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_cht);289 if (ret_val) {290 dev_err(&pdev->dev,291 "snd_soc_register_card failed %d\n", ret_val);292 return ret_val;293 }294 platform_set_drvdata(pdev, &snd_soc_card_cht);295 296 return ret_val;297}298 299static struct platform_driver snd_cht_mc_driver = {300 .driver = {301 .name = "cht-bsw-nau8824",302 },303 .probe = snd_cht_mc_probe,304};305 306module_platform_driver(snd_cht_mc_driver);307 308MODULE_DESCRIPTION("ASoC Intel(R) Baytrail CR Machine driver");309MODULE_AUTHOR("Wang, Joseph C <joequant@gmail.com>");310MODULE_AUTHOR("John Hsu <KCHSU0@nuvoton.com>");311MODULE_LICENSE("GPL v2");312MODULE_ALIAS("platform:cht-bsw-nau8824");313