brintos

brintos / linux-shallow public Read only

0
0
Text · 11.4 KiB · 8d237f6 Raw
445 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2// Copyright(c) 2018-2020 Intel Corporation.3 4/*5 * Intel SOF Machine Driver for Intel platforms with TI PCM512x codec,6 * e.g. Up or Up2 with Hifiberry DAC+ HAT7 */8#include <linux/clk.h>9#include <linux/dmi.h>10#include <linux/i2c.h>11#include <linux/input.h>12#include <linux/module.h>13#include <linux/platform_device.h>14#include <linux/types.h>15#include <sound/core.h>16#include <sound/jack.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 "../../codecs/pcm512x.h"22#include "../common/soc-intel-quirks.h"23#include "hda_dsp_common.h"24 25#define NAME_SIZE 3226 27#define SOF_PCM512X_SSP_CODEC(quirk)		((quirk) & GENMASK(3, 0))28#define SOF_PCM512X_SSP_CODEC_MASK			(GENMASK(3, 0))29#define SOF_PCM512X_ENABLE_SSP_CAPTURE		BIT(4)30#define SOF_PCM512X_ENABLE_DMIC			BIT(5)31 32#define IDISP_CODEC_MASK	0x433 34/* Default: SSP5 */35static unsigned long sof_pcm512x_quirk =36	SOF_PCM512X_SSP_CODEC(5) |37	SOF_PCM512X_ENABLE_SSP_CAPTURE |38	SOF_PCM512X_ENABLE_DMIC;39 40static bool is_legacy_cpu;41 42struct sof_hdmi_pcm {43	struct list_head head;44	struct snd_soc_dai *codec_dai;45	int device;46};47 48struct sof_card_private {49	struct list_head hdmi_pcm_list;50	bool idisp_codec;51};52 53static int sof_pcm512x_quirk_cb(const struct dmi_system_id *id)54{55	sof_pcm512x_quirk = (unsigned long)id->driver_data;56	return 1;57}58 59static const struct dmi_system_id sof_pcm512x_quirk_table[] = {60	{61		.callback = sof_pcm512x_quirk_cb,62		.matches = {63			DMI_MATCH(DMI_SYS_VENDOR, "AAEON"),64			DMI_MATCH(DMI_PRODUCT_NAME, "UP-CHT01"),65		},66		.driver_data = (void *)(SOF_PCM512X_SSP_CODEC(2)),67	},68	{}69};70 71static int sof_hdmi_init(struct snd_soc_pcm_runtime *rtd)72{73	struct sof_card_private *ctx = snd_soc_card_get_drvdata(rtd->card);74	struct snd_soc_dai *dai = snd_soc_rtd_to_codec(rtd, 0);75	struct sof_hdmi_pcm *pcm;76 77	pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL);78	if (!pcm)79		return -ENOMEM;80 81	/* dai_link id is 1:1 mapped to the PCM device */82	pcm->device = rtd->dai_link->id;83	pcm->codec_dai = dai;84 85	list_add_tail(&pcm->head, &ctx->hdmi_pcm_list);86 87	return 0;88}89 90static int sof_pcm512x_codec_init(struct snd_soc_pcm_runtime *rtd)91{92	struct snd_soc_component *codec = snd_soc_rtd_to_codec(rtd, 0)->component;93 94	snd_soc_component_update_bits(codec, PCM512x_GPIO_EN, 0x08, 0x08);95	snd_soc_component_update_bits(codec, PCM512x_GPIO_OUTPUT_4, 0x0f, 0x02);96	snd_soc_component_update_bits(codec, PCM512x_GPIO_CONTROL_1,97				      0x08, 0x08);98 99	return 0;100}101 102static int aif1_startup(struct snd_pcm_substream *substream)103{104	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);105	struct snd_soc_component *codec = snd_soc_rtd_to_codec(rtd, 0)->component;106 107	snd_soc_component_update_bits(codec, PCM512x_GPIO_CONTROL_1,108				      0x08, 0x08);109 110	return 0;111}112 113static void aif1_shutdown(struct snd_pcm_substream *substream)114{115	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);116	struct snd_soc_component *codec = snd_soc_rtd_to_codec(rtd, 0)->component;117 118	snd_soc_component_update_bits(codec, PCM512x_GPIO_CONTROL_1,119				      0x08, 0x00);120}121 122static const struct snd_soc_ops sof_pcm512x_ops = {123	.startup = aif1_startup,124	.shutdown = aif1_shutdown,125};126 127static struct snd_soc_dai_link_component platform_component[] = {128	{129		/* name might be overridden during probe */130		.name = "0000:00:1f.3"131	}132};133 134static int sof_card_late_probe(struct snd_soc_card *card)135{136	struct sof_card_private *ctx = snd_soc_card_get_drvdata(card);137	struct sof_hdmi_pcm *pcm;138 139	/* HDMI is not supported by SOF on Baytrail/CherryTrail */140	if (is_legacy_cpu)141		return 0;142 143	if (list_empty(&ctx->hdmi_pcm_list))144		return -EINVAL;145 146	if (!ctx->idisp_codec)147		return 0;148 149	pcm = list_first_entry(&ctx->hdmi_pcm_list, struct sof_hdmi_pcm, head);150 151	return hda_dsp_hdmi_build_controls(card, pcm->codec_dai->component);152}153 154static const struct snd_kcontrol_new sof_controls[] = {155	SOC_DAPM_PIN_SWITCH("Ext Spk"),156};157 158static const struct snd_soc_dapm_widget sof_widgets[] = {159	SND_SOC_DAPM_SPK("Ext Spk", NULL),160};161 162static const struct snd_soc_dapm_widget dmic_widgets[] = {163	SND_SOC_DAPM_MIC("SoC DMIC", NULL),164};165 166static const struct snd_soc_dapm_route sof_map[] = {167	/* Speaker */168	{"Ext Spk", NULL, "OUTR"},169	{"Ext Spk", NULL, "OUTL"},170};171 172static const struct snd_soc_dapm_route dmic_map[] = {173	/* digital mics */174	{"DMic", NULL, "SoC DMIC"},175};176 177static int dmic_init(struct snd_soc_pcm_runtime *rtd)178{179	struct snd_soc_card *card = rtd->card;180	int ret;181 182	ret = snd_soc_dapm_new_controls(&card->dapm, dmic_widgets,183					ARRAY_SIZE(dmic_widgets));184	if (ret) {185		dev_err(card->dev, "DMic widget addition failed: %d\n", ret);186		/* Don't need to add routes if widget addition failed */187		return ret;188	}189 190	ret = snd_soc_dapm_add_routes(&card->dapm, dmic_map,191				      ARRAY_SIZE(dmic_map));192 193	if (ret)194		dev_err(card->dev, "DMic map addition failed: %d\n", ret);195 196	return ret;197}198 199/* sof audio machine driver for pcm512x codec */200static struct snd_soc_card sof_audio_card_pcm512x = {201	.name = "pcm512x",202	.owner = THIS_MODULE,203	.controls = sof_controls,204	.num_controls = ARRAY_SIZE(sof_controls),205	.dapm_widgets = sof_widgets,206	.num_dapm_widgets = ARRAY_SIZE(sof_widgets),207	.dapm_routes = sof_map,208	.num_dapm_routes = ARRAY_SIZE(sof_map),209	.fully_routed = true,210	.late_probe = sof_card_late_probe,211};212 213SND_SOC_DAILINK_DEF(pcm512x_component,214	DAILINK_COMP_ARRAY(COMP_CODEC("i2c-104C5122:00", "pcm512x-hifi")));215SND_SOC_DAILINK_DEF(dmic_component,216	DAILINK_COMP_ARRAY(COMP_CODEC("dmic-codec", "dmic-hifi")));217 218static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev,219							  int ssp_codec,220							  int dmic_be_num,221							  int hdmi_num,222							  bool idisp_codec)223{224	struct snd_soc_dai_link_component *idisp_components;225	struct snd_soc_dai_link_component *cpus;226	struct snd_soc_dai_link *links;227	int i, id = 0;228 229	links = devm_kcalloc(dev, sof_audio_card_pcm512x.num_links,230			sizeof(struct snd_soc_dai_link), GFP_KERNEL);231	cpus = devm_kcalloc(dev, sof_audio_card_pcm512x.num_links,232			sizeof(struct snd_soc_dai_link_component), GFP_KERNEL);233	if (!links || !cpus)234		goto devm_err;235 236	/* codec SSP */237	links[id].name = devm_kasprintf(dev, GFP_KERNEL,238					"SSP%d-Codec", ssp_codec);239	if (!links[id].name)240		goto devm_err;241 242	links[id].id = id;243	links[id].codecs = pcm512x_component;244	links[id].num_codecs = ARRAY_SIZE(pcm512x_component);245	links[id].platforms = platform_component;246	links[id].num_platforms = ARRAY_SIZE(platform_component);247	links[id].init = sof_pcm512x_codec_init;248	links[id].ops = &sof_pcm512x_ops;249	links[id].dpcm_playback = 1;250	/*251	 * capture only supported with specific versions of the Hifiberry DAC+252	 */253	if (sof_pcm512x_quirk & SOF_PCM512X_ENABLE_SSP_CAPTURE)254		links[id].dpcm_capture = 1;255	links[id].no_pcm = 1;256	links[id].cpus = &cpus[id];257	links[id].num_cpus = 1;258	if (is_legacy_cpu) {259		links[id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,260							  "ssp%d-port",261							  ssp_codec);262		if (!links[id].cpus->dai_name)263			goto devm_err;264	} else {265		links[id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,266							  "SSP%d Pin",267							  ssp_codec);268		if (!links[id].cpus->dai_name)269			goto devm_err;270	}271	id++;272 273	/* dmic */274	if (dmic_be_num > 0) {275		/* at least we have dmic01 */276		links[id].name = "dmic01";277		links[id].cpus = &cpus[id];278		links[id].cpus->dai_name = "DMIC01 Pin";279		links[id].init = dmic_init;280		if (dmic_be_num > 1) {281			/* set up 2 BE links at most */282			links[id + 1].name = "dmic16k";283			links[id + 1].cpus = &cpus[id + 1];284			links[id + 1].cpus->dai_name = "DMIC16k Pin";285			dmic_be_num = 2;286		}287	}288 289	for (i = 0; i < dmic_be_num; i++) {290		links[id].id = id;291		links[id].num_cpus = 1;292		links[id].codecs = dmic_component;293		links[id].num_codecs = ARRAY_SIZE(dmic_component);294		links[id].platforms = platform_component;295		links[id].num_platforms = ARRAY_SIZE(platform_component);296		links[id].ignore_suspend = 1;297		links[id].dpcm_capture = 1;298		links[id].no_pcm = 1;299		id++;300	}301 302	/* HDMI */303	if (hdmi_num > 0) {304		idisp_components = devm_kcalloc(dev, hdmi_num,305				sizeof(struct snd_soc_dai_link_component),306				GFP_KERNEL);307		if (!idisp_components)308			goto devm_err;309	}310	for (i = 1; i <= hdmi_num; i++) {311		links[id].name = devm_kasprintf(dev, GFP_KERNEL,312						"iDisp%d", i);313		if (!links[id].name)314			goto devm_err;315 316		links[id].id = id;317		links[id].cpus = &cpus[id];318		links[id].num_cpus = 1;319		links[id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,320							  "iDisp%d Pin", i);321		if (!links[id].cpus->dai_name)322			goto devm_err;323 324		/*325		 * topology cannot be loaded if codec is missing, so326		 * use the dummy codec if needed327		 */328		if (idisp_codec) {329			idisp_components[i - 1].name = "ehdaudio0D2";330			idisp_components[i - 1].dai_name =331				devm_kasprintf(dev, GFP_KERNEL,332					       "intel-hdmi-hifi%d", i);333		} else {334			idisp_components[i - 1] = snd_soc_dummy_dlc;335		}336		if (!idisp_components[i - 1].dai_name)337			goto devm_err;338 339		links[id].codecs = &idisp_components[i - 1];340		links[id].num_codecs = 1;341		links[id].platforms = platform_component;342		links[id].num_platforms = ARRAY_SIZE(platform_component);343		links[id].init = sof_hdmi_init;344		links[id].dpcm_playback = 1;345		links[id].no_pcm = 1;346		id++;347	}348 349	return links;350devm_err:351	return NULL;352}353 354static int sof_audio_probe(struct platform_device *pdev)355{356	struct snd_soc_acpi_mach *mach = pdev->dev.platform_data;357	struct snd_soc_dai_link *dai_links;358	struct sof_card_private *ctx;359	int dmic_be_num, hdmi_num;360	int ret, ssp_codec;361 362	ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);363	if (!ctx)364		return -ENOMEM;365 366	hdmi_num = 0;367	if (soc_intel_is_byt() || soc_intel_is_cht()) {368		is_legacy_cpu = true;369		dmic_be_num = 0;370		/* default quirk for legacy cpu */371		sof_pcm512x_quirk = SOF_PCM512X_SSP_CODEC(2);372	} else {373		dmic_be_num = 2;374		if (mach->mach_params.codec_mask & IDISP_CODEC_MASK)375			ctx->idisp_codec = true;376 377		/* links are always present in topology */378		hdmi_num = 3;379	}380 381	dmi_check_system(sof_pcm512x_quirk_table);382 383	dev_dbg(&pdev->dev, "sof_pcm512x_quirk = %lx\n", sof_pcm512x_quirk);384 385	ssp_codec = sof_pcm512x_quirk & SOF_PCM512X_SSP_CODEC_MASK;386 387	if (!(sof_pcm512x_quirk & SOF_PCM512X_ENABLE_DMIC))388		dmic_be_num = 0;389 390	/* compute number of dai links */391	sof_audio_card_pcm512x.num_links = 1 + dmic_be_num + hdmi_num;392 393	dai_links = sof_card_dai_links_create(&pdev->dev, ssp_codec,394					      dmic_be_num, hdmi_num,395					      ctx->idisp_codec);396	if (!dai_links)397		return -ENOMEM;398 399	sof_audio_card_pcm512x.dai_link = dai_links;400 401	INIT_LIST_HEAD(&ctx->hdmi_pcm_list);402 403	sof_audio_card_pcm512x.dev = &pdev->dev;404 405	/* set platform name for each dailink */406	ret = snd_soc_fixup_dai_links_platform_name(&sof_audio_card_pcm512x,407						    mach->mach_params.platform);408	if (ret)409		return ret;410 411	snd_soc_card_set_drvdata(&sof_audio_card_pcm512x, ctx);412 413	return devm_snd_soc_register_card(&pdev->dev,414					  &sof_audio_card_pcm512x);415}416 417static void sof_pcm512x_remove(struct platform_device *pdev)418{419	struct snd_soc_card *card = platform_get_drvdata(pdev);420	struct snd_soc_component *component;421 422	for_each_card_components(card, component) {423		if (!strcmp(component->name, pcm512x_component[0].name)) {424			snd_soc_component_set_jack(component, NULL, NULL);425			break;426		}427	}428}429 430static struct platform_driver sof_audio = {431	.probe = sof_audio_probe,432	.remove = sof_pcm512x_remove,433	.driver = {434		.name = "sof_pcm512x",435		.pm = &snd_soc_pm_ops,436	},437};438module_platform_driver(sof_audio)439 440MODULE_DESCRIPTION("ASoC Intel(R) SOF + PCM512x Machine driver");441MODULE_AUTHOR("Pierre-Louis Bossart");442MODULE_LICENSE("GPL v2");443MODULE_ALIAS("platform:sof_pcm512x");444MODULE_IMPORT_NS(SND_SOC_INTEL_HDA_DSP_COMMON);445