brintos

brintos / linux-shallow public Read only

0
0
Text · 7.8 KiB · 90d93e6 Raw
319 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2// Copyright (c) 2020 Intel Corporation3 4/*5 * ehl_rt5660 - ASOC Machine driver for Elkhart Lake platforms6 * with rt5660 codec7 */8 9#include <linux/acpi.h>10#include <sound/core.h>11#include <linux/device.h>12#include <linux/errno.h>13#include <linux/gfp.h>14#include <sound/jack.h>15#include <linux/kernel.h>16#include <linux/list.h>17#include <linux/module.h>18#include <sound/pcm.h>19#include <sound/pcm_params.h>20#include <sound/soc.h>21#include <sound/soc-acpi.h>22 23#include "hda_dsp_common.h"24#include "../../codecs/rt5660.h"25 26#define DUAL_CHANNEL 227#define HDMI_LINK_START 328#define HDMI_LINE_END 629#define NAME_SIZE	3230#define IDISP_CODEC_MASK	0x431 32struct sof_card_private {33	struct list_head hdmi_pcm_list;34	bool idisp_codec;35};36 37static const struct snd_kcontrol_new rt5660_controls[] = {38	SOC_DAPM_PIN_SWITCH("Speaker"),39	/* There are two MICBIAS in rt5660, each for one MIC */40	SOC_DAPM_PIN_SWITCH("Headset Mic"),41	SOC_DAPM_PIN_SWITCH("Headset Mic2"),42	SOC_DAPM_PIN_SWITCH("Line Out"),43};44 45static const struct snd_soc_dapm_widget rt5660_widgets[] = {46	SND_SOC_DAPM_SPK("Speaker", NULL),47	SND_SOC_DAPM_MIC("Headset Mic", NULL),48	SND_SOC_DAPM_MIC("Headset Mic2", NULL),49	SND_SOC_DAPM_MIC("SoC DMIC", NULL),50	SND_SOC_DAPM_LINE("Line Out", NULL),51};52 53static const struct snd_soc_dapm_route rt5660_map[] = {54	{"Speaker", NULL, "SPO"},55 56	{"Headset Mic", NULL, "MICBIAS1"},57	{"Headset Mic2", NULL, "MICBIAS2"},58 59	{"IN1P", NULL, "Headset Mic"},60	{"IN2P", NULL, "Headset Mic2"},61 62	{"Line Out", NULL, "LOUTL"},63	{"Line Out", NULL, "LOUTR"},64 65	{"DMic", NULL, "SoC DMIC"},66};67 68struct sof_hdmi_pcm {69	struct list_head head;70	struct snd_soc_dai *codec_dai;71	int device;72};73 74static int hdmi_init(struct snd_soc_pcm_runtime *rtd)75{76	struct sof_card_private *ctx = snd_soc_card_get_drvdata(rtd->card);77	struct snd_soc_dai *dai = snd_soc_rtd_to_codec(rtd, 0);78	struct sof_hdmi_pcm *pcm;79 80	pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL);81	if (!pcm)82		return -ENOMEM;83 84	/* dai_link id is 1:1 mapped to the PCM device */85	pcm->device = rtd->dai_link->id;86	pcm->codec_dai = dai;87 88	list_add_tail(&pcm->head, &ctx->hdmi_pcm_list);89 90	return 0;91}92 93static int card_late_probe(struct snd_soc_card *card)94{95	struct sof_card_private *ctx = snd_soc_card_get_drvdata(card);96	struct sof_hdmi_pcm *pcm;97 98	if (list_empty(&ctx->hdmi_pcm_list))99		return -ENOENT;100 101	if (!ctx->idisp_codec)102		return 0;103 104	pcm = list_first_entry(&ctx->hdmi_pcm_list, struct sof_hdmi_pcm, head);105 106	return hda_dsp_hdmi_build_controls(card, pcm->codec_dai->component);107}108 109static int rt5660_hw_params(struct snd_pcm_substream *substream,110			    struct snd_pcm_hw_params *params)111{112	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);113	struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);114	int ret;115 116	ret = snd_soc_dai_set_sysclk(codec_dai,117				     RT5660_SCLK_S_PLL1,118				     params_rate(params) * 512,119				     SND_SOC_CLOCK_IN);120	if (ret < 0) {121		dev_err(rtd->dev, "snd_soc_dai_set_sysclk err = %d\n", ret);122		return ret;123	}124 125	ret = snd_soc_dai_set_pll(codec_dai, 0,126				  RT5660_PLL1_S_BCLK,127				  params_rate(params) * 50,128				  params_rate(params) * 512);129	if (ret < 0)130		dev_err(codec_dai->dev, "can't set codec pll: %d\n", ret);131 132	return ret;133}134 135static const struct snd_soc_ops rt5660_ops = {136	.hw_params = rt5660_hw_params,137};138 139SND_SOC_DAILINK_DEF(ssp0_pin,140	DAILINK_COMP_ARRAY(COMP_CPU("SSP0 Pin")));141 142SND_SOC_DAILINK_DEF(rt5660_codec,143	DAILINK_COMP_ARRAY(COMP_CODEC("i2c-10EC5660:00", "rt5660-aif1")));144 145SND_SOC_DAILINK_DEF(platform,146	DAILINK_COMP_ARRAY(COMP_PLATFORM("0000:00:1f.3")));147 148SND_SOC_DAILINK_DEF(dmic_pin,149	DAILINK_COMP_ARRAY(COMP_CPU("DMIC01 Pin")));150SND_SOC_DAILINK_DEF(dmic_codec,151	DAILINK_COMP_ARRAY(COMP_CODEC("dmic-codec", "dmic-hifi")));152SND_SOC_DAILINK_DEF(dmic16k,153	DAILINK_COMP_ARRAY(COMP_CPU("DMIC16k Pin")));154 155SND_SOC_DAILINK_DEF(idisp1_pin,156	DAILINK_COMP_ARRAY(COMP_CPU("iDisp1 Pin")));157SND_SOC_DAILINK_DEF(idisp1_codec,158	DAILINK_COMP_ARRAY(COMP_CODEC("ehdaudio0D2", "intel-hdmi-hifi1")));159 160SND_SOC_DAILINK_DEF(idisp2_pin,161	DAILINK_COMP_ARRAY(COMP_CPU("iDisp2 Pin")));162SND_SOC_DAILINK_DEF(idisp2_codec,163	DAILINK_COMP_ARRAY(COMP_CODEC("ehdaudio0D2", "intel-hdmi-hifi2")));164 165SND_SOC_DAILINK_DEF(idisp3_pin,166	DAILINK_COMP_ARRAY(COMP_CPU("iDisp3 Pin")));167SND_SOC_DAILINK_DEF(idisp3_codec,168	DAILINK_COMP_ARRAY(COMP_CODEC("ehdaudio0D2", "intel-hdmi-hifi3")));169 170SND_SOC_DAILINK_DEF(idisp4_pin,171	DAILINK_COMP_ARRAY(COMP_CPU("iDisp4 Pin")));172SND_SOC_DAILINK_DEF(idisp4_codec,173	DAILINK_COMP_ARRAY(COMP_CODEC("ehdaudio0D2", "intel-hdmi-hifi4")));174 175static struct snd_soc_dai_link ehl_rt5660_dailink[] = {176	/* back ends */177	{178		.name = "SSP0-Codec",179		.id = 0,180		.no_pcm = 1,181		.dpcm_playback = 1,182		.dpcm_capture = 1,183		.ops = &rt5660_ops,184		SND_SOC_DAILINK_REG(ssp0_pin, rt5660_codec, platform),185	},186	{187		.name = "dmic48k",188		.id = 1,189		.ignore_suspend = 1,190		.dpcm_capture = 1,191		.no_pcm = 1,192		SND_SOC_DAILINK_REG(dmic_pin, dmic_codec, platform),193	},194	{195		.name = "dmic16k",196		.id = 2,197		.ignore_suspend = 1,198		.dpcm_capture = 1,199		.no_pcm = 1,200		SND_SOC_DAILINK_REG(dmic16k, dmic_codec, platform),201	},202	{203		.name = "iDisp1",204		.id = 5,205		.init = hdmi_init,206		.dpcm_playback = 1,207		.no_pcm = 1,208		SND_SOC_DAILINK_REG(idisp1_pin, idisp1_codec, platform),209	},210	{211		.name = "iDisp2",212		.id = 6,213		.init = hdmi_init,214		.dpcm_playback = 1,215		.no_pcm = 1,216		SND_SOC_DAILINK_REG(idisp2_pin, idisp2_codec, platform),217	},218	{219		.name = "iDisp3",220		.id = 7,221		.init = hdmi_init,222		.dpcm_playback = 1,223		.no_pcm = 1,224		SND_SOC_DAILINK_REG(idisp3_pin, idisp3_codec, platform),225	},226	{227		.name = "iDisp4",228		.id = 8,229		.init = hdmi_init,230		.dpcm_playback = 1,231		.no_pcm = 1,232		SND_SOC_DAILINK_REG(idisp4_pin, idisp4_codec, platform),233	},234};235 236/* SoC card */237static struct snd_soc_card snd_soc_card_ehl_rt5660 = {238	.name = "ehl-rt5660",239	.owner = THIS_MODULE,240	.dai_link = ehl_rt5660_dailink,241	.num_links = ARRAY_SIZE(ehl_rt5660_dailink),242	.dapm_widgets = rt5660_widgets,243	.num_dapm_widgets = ARRAY_SIZE(rt5660_widgets),244	.dapm_routes = rt5660_map,245	.num_dapm_routes = ARRAY_SIZE(rt5660_map),246	.controls = rt5660_controls,247	.num_controls = ARRAY_SIZE(rt5660_controls),248	.fully_routed = true,249	.late_probe = card_late_probe,250};251 252/* If hdmi codec is not supported, switch to use dummy codec */253static void hdmi_link_init(struct snd_soc_card *card,254			   struct sof_card_private *ctx,255			   struct snd_soc_acpi_mach *mach)256{257	int i;258 259	if (mach->mach_params.codec_mask & IDISP_CODEC_MASK) {260		ctx->idisp_codec = true;261		return;262	}263 264	/*265	 * if HDMI is not enabled in kernel config, or266	 * hdmi codec is not supported267	 */268	for (i = HDMI_LINK_START; i <= HDMI_LINE_END; i++)269		card->dai_link[i].codecs[0] = snd_soc_dummy_dlc;270}271 272static int snd_ehl_rt5660_probe(struct platform_device *pdev)273{274	struct snd_soc_acpi_mach *mach;275	struct snd_soc_card *card = &snd_soc_card_ehl_rt5660;276	struct sof_card_private *ctx;277	int ret;278 279	card->dev = &pdev->dev;280 281	ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);282	if (!ctx)283		return -ENOMEM;284	INIT_LIST_HEAD(&ctx->hdmi_pcm_list);285	snd_soc_card_set_drvdata(card, ctx);286 287	mach = pdev->dev.platform_data;288	ret = snd_soc_fixup_dai_links_platform_name(card,289						    mach->mach_params.platform);290	if (ret)291		return ret;292 293	hdmi_link_init(card, ctx, mach);294 295	return devm_snd_soc_register_card(&pdev->dev, card);296}297 298static const struct platform_device_id ehl_board_ids[] = {299	{ .name = "ehl_rt5660" },300	{ }301};302MODULE_DEVICE_TABLE(platform, ehl_board_ids);303 304static struct platform_driver snd_ehl_rt5660_driver = {305	.driver = {306		.name = "ehl_rt5660",307		.pm = &snd_soc_pm_ops,308	},309	.probe = snd_ehl_rt5660_probe,310	.id_table = ehl_board_ids,311};312 313module_platform_driver(snd_ehl_rt5660_driver);314 315MODULE_DESCRIPTION("ASoC Intel(R) Elkhartlake + rt5660 Machine driver");316MODULE_AUTHOR("libin.yang@intel.com");317MODULE_LICENSE("GPL v2");318MODULE_IMPORT_NS(SND_SOC_INTEL_HDA_DSP_COMMON);319