18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * mtk-afe-platform-driver.c  --  Mediatek afe platform driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) 2016 MediaTek Inc.
68c2ecf20Sopenharmony_ci * Author: Garlic Tseng <garlic.tseng@mediatek.com>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/module.h>
108c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
118c2ecf20Sopenharmony_ci#include <sound/soc.h>
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include "mtk-afe-platform-driver.h"
148c2ecf20Sopenharmony_ci#include "mtk-base-afe.h"
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ciint mtk_afe_combine_sub_dai(struct mtk_base_afe *afe)
178c2ecf20Sopenharmony_ci{
188c2ecf20Sopenharmony_ci	struct mtk_base_afe_dai *dai;
198c2ecf20Sopenharmony_ci	size_t num_dai_drivers = 0, dai_idx = 0;
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci	/* calcualte total dai driver size */
228c2ecf20Sopenharmony_ci	list_for_each_entry(dai, &afe->sub_dais, list) {
238c2ecf20Sopenharmony_ci		num_dai_drivers += dai->num_dai_drivers;
248c2ecf20Sopenharmony_ci	}
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci	dev_info(afe->dev, "%s(), num of dai %zd\n", __func__, num_dai_drivers);
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci	/* combine sub_dais */
298c2ecf20Sopenharmony_ci	afe->num_dai_drivers = num_dai_drivers;
308c2ecf20Sopenharmony_ci	afe->dai_drivers = devm_kcalloc(afe->dev,
318c2ecf20Sopenharmony_ci					num_dai_drivers,
328c2ecf20Sopenharmony_ci					sizeof(struct snd_soc_dai_driver),
338c2ecf20Sopenharmony_ci					GFP_KERNEL);
348c2ecf20Sopenharmony_ci	if (!afe->dai_drivers)
358c2ecf20Sopenharmony_ci		return -ENOMEM;
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	list_for_each_entry(dai, &afe->sub_dais, list) {
388c2ecf20Sopenharmony_ci		/* dai driver */
398c2ecf20Sopenharmony_ci		memcpy(&afe->dai_drivers[dai_idx],
408c2ecf20Sopenharmony_ci		       dai->dai_drivers,
418c2ecf20Sopenharmony_ci		       dai->num_dai_drivers *
428c2ecf20Sopenharmony_ci		       sizeof(struct snd_soc_dai_driver));
438c2ecf20Sopenharmony_ci		dai_idx += dai->num_dai_drivers;
448c2ecf20Sopenharmony_ci	}
458c2ecf20Sopenharmony_ci	return 0;
468c2ecf20Sopenharmony_ci}
478c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mtk_afe_combine_sub_dai);
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ciint mtk_afe_add_sub_dai_control(struct snd_soc_component *component)
508c2ecf20Sopenharmony_ci{
518c2ecf20Sopenharmony_ci	struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component);
528c2ecf20Sopenharmony_ci	struct mtk_base_afe_dai *dai;
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	list_for_each_entry(dai, &afe->sub_dais, list) {
558c2ecf20Sopenharmony_ci		if (dai->controls)
568c2ecf20Sopenharmony_ci			snd_soc_add_component_controls(component,
578c2ecf20Sopenharmony_ci						       dai->controls,
588c2ecf20Sopenharmony_ci						       dai->num_controls);
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci		if (dai->dapm_widgets)
618c2ecf20Sopenharmony_ci			snd_soc_dapm_new_controls(&component->dapm,
628c2ecf20Sopenharmony_ci						  dai->dapm_widgets,
638c2ecf20Sopenharmony_ci						  dai->num_dapm_widgets);
648c2ecf20Sopenharmony_ci	}
658c2ecf20Sopenharmony_ci	/* add routes after all widgets are added */
668c2ecf20Sopenharmony_ci	list_for_each_entry(dai, &afe->sub_dais, list) {
678c2ecf20Sopenharmony_ci		if (dai->dapm_routes)
688c2ecf20Sopenharmony_ci			snd_soc_dapm_add_routes(&component->dapm,
698c2ecf20Sopenharmony_ci						dai->dapm_routes,
708c2ecf20Sopenharmony_ci						dai->num_dapm_routes);
718c2ecf20Sopenharmony_ci	}
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	snd_soc_dapm_new_widgets(component->dapm.card);
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	return 0;
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci}
788c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mtk_afe_add_sub_dai_control);
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_cisnd_pcm_uframes_t mtk_afe_pcm_pointer(struct snd_soc_component *component,
818c2ecf20Sopenharmony_ci				      struct snd_pcm_substream *substream)
828c2ecf20Sopenharmony_ci{
838c2ecf20Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
848c2ecf20Sopenharmony_ci	struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component);
858c2ecf20Sopenharmony_ci	struct mtk_base_afe_memif *memif = &afe->memif[asoc_rtd_to_cpu(rtd, 0)->id];
868c2ecf20Sopenharmony_ci	const struct mtk_base_memif_data *memif_data = memif->data;
878c2ecf20Sopenharmony_ci	struct regmap *regmap = afe->regmap;
888c2ecf20Sopenharmony_ci	struct device *dev = afe->dev;
898c2ecf20Sopenharmony_ci	int reg_ofs_base = memif_data->reg_ofs_base;
908c2ecf20Sopenharmony_ci	int reg_ofs_cur = memif_data->reg_ofs_cur;
918c2ecf20Sopenharmony_ci	unsigned int hw_ptr = 0, hw_base = 0;
928c2ecf20Sopenharmony_ci	int ret, pcm_ptr_bytes;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	ret = regmap_read(regmap, reg_ofs_cur, &hw_ptr);
958c2ecf20Sopenharmony_ci	if (ret || hw_ptr == 0) {
968c2ecf20Sopenharmony_ci		dev_err(dev, "%s hw_ptr err\n", __func__);
978c2ecf20Sopenharmony_ci		pcm_ptr_bytes = 0;
988c2ecf20Sopenharmony_ci		goto POINTER_RETURN_FRAMES;
998c2ecf20Sopenharmony_ci	}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	ret = regmap_read(regmap, reg_ofs_base, &hw_base);
1028c2ecf20Sopenharmony_ci	if (ret || hw_base == 0) {
1038c2ecf20Sopenharmony_ci		dev_err(dev, "%s hw_ptr err\n", __func__);
1048c2ecf20Sopenharmony_ci		pcm_ptr_bytes = 0;
1058c2ecf20Sopenharmony_ci		goto POINTER_RETURN_FRAMES;
1068c2ecf20Sopenharmony_ci	}
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	pcm_ptr_bytes = hw_ptr - hw_base;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ciPOINTER_RETURN_FRAMES:
1118c2ecf20Sopenharmony_ci	return bytes_to_frames(substream->runtime, pcm_ptr_bytes);
1128c2ecf20Sopenharmony_ci}
1138c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mtk_afe_pcm_pointer);
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ciint mtk_afe_pcm_new(struct snd_soc_component *component,
1168c2ecf20Sopenharmony_ci		    struct snd_soc_pcm_runtime *rtd)
1178c2ecf20Sopenharmony_ci{
1188c2ecf20Sopenharmony_ci	size_t size;
1198c2ecf20Sopenharmony_ci	struct snd_pcm *pcm = rtd->pcm;
1208c2ecf20Sopenharmony_ci	struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component);
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	size = afe->mtk_afe_hardware->buffer_bytes_max;
1238c2ecf20Sopenharmony_ci	snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
1248c2ecf20Sopenharmony_ci				       afe->dev, size, size);
1258c2ecf20Sopenharmony_ci	return 0;
1268c2ecf20Sopenharmony_ci}
1278c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mtk_afe_pcm_new);
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ciconst struct snd_soc_component_driver mtk_afe_pcm_platform = {
1308c2ecf20Sopenharmony_ci	.name		= AFE_PCM_NAME,
1318c2ecf20Sopenharmony_ci	.pointer	= mtk_afe_pcm_pointer,
1328c2ecf20Sopenharmony_ci	.pcm_construct	= mtk_afe_pcm_new,
1338c2ecf20Sopenharmony_ci};
1348c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mtk_afe_pcm_platform);
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Mediatek simple platform driver");
1378c2ecf20Sopenharmony_ciMODULE_AUTHOR("Garlic Tseng <garlic.tseng@mediatek.com>");
1388c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
1398c2ecf20Sopenharmony_ci
140