brintos

brintos / linux-shallow public Read only

0
0
Text · 8.5 KiB · 7651b83 Raw
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