brintos

brintos / linux-shallow public Read only

0
0
Text · 6.7 KiB · ce98d22 Raw
255 lines · c
1// SPDX-License-Identifier: GPL-2.0+2// Copyright 2017-2020 NXP3 4#include <linux/module.h>5#include <linux/of_platform.h>6#include <linux/of_reserved_mem.h>7#include <linux/i2c.h>8#include <linux/slab.h>9#include <linux/clk.h>10#include <sound/soc.h>11#include <sound/jack.h>12#include <sound/control.h>13#include <sound/pcm_params.h>14#include <sound/soc-dapm.h>15#include "imx-pcm-rpmsg.h"16 17struct imx_rpmsg {18	struct snd_soc_dai_link dai;19	struct snd_soc_card card;20	unsigned long sysclk;21	bool lpa;22};23 24static struct dev_pm_ops lpa_pm;25 26static const struct snd_soc_dapm_widget imx_rpmsg_dapm_widgets[] = {27	SND_SOC_DAPM_HP("Headphone Jack", NULL),28	SND_SOC_DAPM_SPK("Ext Spk", NULL),29	SND_SOC_DAPM_MIC("Mic Jack", NULL),30	SND_SOC_DAPM_MIC("Main MIC", NULL),31};32 33static int imx_rpmsg_late_probe(struct snd_soc_card *card)34{35	struct imx_rpmsg *data = snd_soc_card_get_drvdata(card);36	struct snd_soc_pcm_runtime *rtd = list_first_entry(&card->rtd_list,37							   struct snd_soc_pcm_runtime, list);38	struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);39	struct device *dev = card->dev;40	int ret;41 42	if (data->lpa) {43		struct snd_soc_component *codec_comp;44		struct device_node *codec_np;45		struct device_driver *codec_drv;46		struct device *codec_dev = NULL;47 48		codec_np = data->dai.codecs->of_node;49		if (codec_np) {50			struct platform_device *codec_pdev;51			struct i2c_client *codec_i2c;52 53			codec_i2c = of_find_i2c_device_by_node(codec_np);54			if (codec_i2c)55				codec_dev = &codec_i2c->dev;56			if (!codec_dev) {57				codec_pdev = of_find_device_by_node(codec_np);58				if (codec_pdev)59					codec_dev = &codec_pdev->dev;60			}61		}62		if (codec_dev) {63			codec_comp = snd_soc_lookup_component_nolocked(codec_dev, NULL);64			if (codec_comp) {65				int i, num_widgets;66				const char *widgets;67				struct snd_soc_dapm_context *dapm;68 69				num_widgets = of_property_count_strings(data->card.dev->of_node,70									"ignore-suspend-widgets");71				for (i = 0; i < num_widgets; i++) {72					of_property_read_string_index(data->card.dev->of_node,73								      "ignore-suspend-widgets",74								      i, &widgets);75					dapm = snd_soc_component_get_dapm(codec_comp);76					snd_soc_dapm_ignore_suspend(dapm, widgets);77				}78			}79			codec_drv = codec_dev->driver;80			if (codec_drv->pm) {81				memcpy(&lpa_pm, codec_drv->pm, sizeof(lpa_pm));82				lpa_pm.suspend = NULL;83				lpa_pm.resume = NULL;84				lpa_pm.freeze = NULL;85				lpa_pm.thaw = NULL;86				lpa_pm.poweroff = NULL;87				lpa_pm.restore = NULL;88				codec_drv->pm = &lpa_pm;89			}90			put_device(codec_dev);91		}92	}93 94	if (!data->sysclk)95		return 0;96 97	ret = snd_soc_dai_set_sysclk(codec_dai, 0, data->sysclk, SND_SOC_CLOCK_IN);98	if (ret && ret != -ENOTSUPP) {99		dev_err(dev, "failed to set sysclk in %s\n", __func__);100		return ret;101	}102 103	return 0;104}105 106static int imx_rpmsg_probe(struct platform_device *pdev)107{108	struct snd_soc_dai_link_component *dlc;109	struct snd_soc_dai *cpu_dai;110	struct device_node *np = NULL;111	struct of_phandle_args args;112	const char *platform_name;113	struct imx_rpmsg *data;114	int ret = 0;115 116	dlc = devm_kzalloc(&pdev->dev, 3 * sizeof(*dlc), GFP_KERNEL);117	if (!dlc)118		return -ENOMEM;119 120	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);121	if (!data) {122		ret = -ENOMEM;123		goto fail;124	}125 126	data->dai.cpus = &dlc[0];127	data->dai.num_cpus = 1;128	data->dai.platforms = &dlc[1];129	data->dai.num_platforms = 1;130	data->dai.codecs = &dlc[2];131	data->dai.num_codecs = 1;132 133	data->dai.name = "rpmsg hifi";134	data->dai.stream_name = "rpmsg hifi";135	data->dai.dai_fmt = SND_SOC_DAIFMT_I2S |136			    SND_SOC_DAIFMT_NB_NF |137			    SND_SOC_DAIFMT_CBC_CFC;138 139	/*140	 * i.MX rpmsg sound cards work on codec slave mode. MCLK will be141	 * disabled by CPU DAI driver in hw_free(). Some codec requires MCLK142	 * present at power up/down sequence. So need to set ignore_pmdown_time143	 * to power down codec immediately before MCLK is turned off.144	 */145	data->dai.ignore_pmdown_time = 1;146 147	data->dai.cpus->dai_name = pdev->dev.platform_data;148	cpu_dai = snd_soc_find_dai(data->dai.cpus);149	if (!cpu_dai) {150		ret = -EPROBE_DEFER;151		goto fail;152	}153	np = cpu_dai->dev->of_node;154	if (!np) {155		dev_err(&pdev->dev, "failed to parse CPU DAI device node\n");156		ret = -ENODEV;157		goto fail;158	}159 160	ret = of_reserved_mem_device_init_by_idx(&pdev->dev, np, 0);161	if (ret)162		dev_warn(&pdev->dev, "no reserved DMA memory\n");163 164	/* Optional codec node */165	ret = of_parse_phandle_with_fixed_args(np, "audio-codec", 0, 0, &args);166	if (ret) {167		*data->dai.codecs = snd_soc_dummy_dlc;168	} else {169		struct clk *clk;170 171		ret = snd_soc_get_dlc(&args, data->dai.codecs);172		if (ret) {173			dev_err(&pdev->dev, "Unable to get codec_dai_name\n");174			goto fail;175		}176 177		clk = devm_get_clk_from_child(&pdev->dev, args.np, NULL);178		if (!IS_ERR(clk))179			data->sysclk = clk_get_rate(clk);180	}181 182	if (!of_property_read_string(np, "fsl,rpmsg-channel-name", &platform_name))183		data->dai.platforms->name = platform_name;184	else185		data->dai.platforms->name = "rpmsg-audio-channel";186	data->dai.playback_only = true;187	data->dai.capture_only = true;188	data->card.num_links = 1;189	data->card.dai_link = &data->dai;190 191	if (of_property_read_bool(np, "fsl,rpmsg-out"))192		data->dai.capture_only = false;193 194	if (of_property_read_bool(np, "fsl,rpmsg-in"))195		data->dai.playback_only = false;196 197	if (data->dai.playback_only && data->dai.capture_only) {198		dev_err(&pdev->dev, "no enabled rpmsg DAI link\n");199		ret = -EINVAL;200		goto fail;201	}202 203	if (of_property_read_bool(np, "fsl,enable-lpa"))204		data->lpa = true;205 206	data->card.dev = &pdev->dev;207	data->card.owner = THIS_MODULE;208	data->card.dapm_widgets = imx_rpmsg_dapm_widgets;209	data->card.num_dapm_widgets = ARRAY_SIZE(imx_rpmsg_dapm_widgets);210	data->card.late_probe = imx_rpmsg_late_probe;211	/*212	 * Inoder to use common api to get card name and audio routing.213	 * Use parent of_node for this device, revert it after finishing using214	 */215	data->card.dev->of_node = np;216 217	ret = snd_soc_of_parse_card_name(&data->card, "model");218	if (ret)219		goto fail;220 221	if (of_property_read_bool(np, "audio-routing")) {222		ret = snd_soc_of_parse_audio_routing(&data->card, "audio-routing");223		if (ret) {224			dev_err(&pdev->dev, "failed to parse audio-routing: %d\n", ret);225			goto fail;226		}227	}228 229	platform_set_drvdata(pdev, &data->card);230	snd_soc_card_set_drvdata(&data->card, data);231	ret = devm_snd_soc_register_card(&pdev->dev, &data->card);232	if (ret) {233		dev_err_probe(&pdev->dev, ret, "snd_soc_register_card failed\n");234		goto fail;235	}236 237fail:238	pdev->dev.of_node = NULL;239	return ret;240}241 242static struct platform_driver imx_rpmsg_driver = {243	.driver = {244		.name = "imx-audio-rpmsg",245		.pm = &snd_soc_pm_ops,246	},247	.probe = imx_rpmsg_probe,248};249module_platform_driver(imx_rpmsg_driver);250 251MODULE_DESCRIPTION("Freescale SoC Audio RPMSG Machine Driver");252MODULE_AUTHOR("Shengjiu Wang <shengjiu.wang@nxp.com>");253MODULE_ALIAS("platform:imx-audio-rpmsg");254MODULE_LICENSE("GPL v2");255