162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci// Copyright (c) 2018, Linaro Limited
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#include <linux/module.h>
562306a36Sopenharmony_ci#include <linux/platform_device.h>
662306a36Sopenharmony_ci#include <linux/of_device.h>
762306a36Sopenharmony_ci#include <sound/soc.h>
862306a36Sopenharmony_ci#include <sound/soc-dapm.h>
962306a36Sopenharmony_ci#include <sound/pcm.h>
1062306a36Sopenharmony_ci#include "common.h"
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#define SLIM_MAX_TX_PORTS 16
1362306a36Sopenharmony_ci#define SLIM_MAX_RX_PORTS 16
1462306a36Sopenharmony_ci#define WCD9335_DEFAULT_MCLK_RATE	9600000
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_cistatic int apq8096_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
1762306a36Sopenharmony_ci				      struct snd_pcm_hw_params *params)
1862306a36Sopenharmony_ci{
1962306a36Sopenharmony_ci	struct snd_interval *rate = hw_param_interval(params,
2062306a36Sopenharmony_ci					SNDRV_PCM_HW_PARAM_RATE);
2162306a36Sopenharmony_ci	struct snd_interval *channels = hw_param_interval(params,
2262306a36Sopenharmony_ci					SNDRV_PCM_HW_PARAM_CHANNELS);
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci	rate->min = rate->max = 48000;
2562306a36Sopenharmony_ci	channels->min = channels->max = 2;
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci	return 0;
2862306a36Sopenharmony_ci}
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistatic int msm_snd_hw_params(struct snd_pcm_substream *substream,
3162306a36Sopenharmony_ci			     struct snd_pcm_hw_params *params)
3262306a36Sopenharmony_ci{
3362306a36Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
3462306a36Sopenharmony_ci	struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
3562306a36Sopenharmony_ci	struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
3662306a36Sopenharmony_ci	u32 rx_ch[SLIM_MAX_RX_PORTS], tx_ch[SLIM_MAX_TX_PORTS];
3762306a36Sopenharmony_ci	u32 rx_ch_cnt = 0, tx_ch_cnt = 0;
3862306a36Sopenharmony_ci	int ret = 0;
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	ret = snd_soc_dai_get_channel_map(codec_dai,
4162306a36Sopenharmony_ci				&tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch);
4262306a36Sopenharmony_ci	if (ret != 0 && ret != -ENOTSUPP) {
4362306a36Sopenharmony_ci		pr_err("failed to get codec chan map, err:%d\n", ret);
4462306a36Sopenharmony_ci		goto end;
4562306a36Sopenharmony_ci	} else if (ret == -ENOTSUPP) {
4662306a36Sopenharmony_ci		return 0;
4762306a36Sopenharmony_ci	}
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
5062306a36Sopenharmony_ci		ret = snd_soc_dai_set_channel_map(cpu_dai, 0, NULL,
5162306a36Sopenharmony_ci						  rx_ch_cnt, rx_ch);
5262306a36Sopenharmony_ci	else
5362306a36Sopenharmony_ci		ret = snd_soc_dai_set_channel_map(cpu_dai, tx_ch_cnt, tx_ch,
5462306a36Sopenharmony_ci						  0, NULL);
5562306a36Sopenharmony_ci	if (ret != 0 && ret != -ENOTSUPP)
5662306a36Sopenharmony_ci		pr_err("Failed to set cpu chan map, err:%d\n", ret);
5762306a36Sopenharmony_ci	else if (ret == -ENOTSUPP)
5862306a36Sopenharmony_ci		ret = 0;
5962306a36Sopenharmony_ciend:
6062306a36Sopenharmony_ci	return ret;
6162306a36Sopenharmony_ci}
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_cistatic const struct snd_soc_ops apq8096_ops = {
6462306a36Sopenharmony_ci	.hw_params = msm_snd_hw_params,
6562306a36Sopenharmony_ci};
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_cistatic int apq8096_init(struct snd_soc_pcm_runtime *rtd)
6862306a36Sopenharmony_ci{
6962306a36Sopenharmony_ci	struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	/*
7262306a36Sopenharmony_ci	 * Codec SLIMBUS configuration
7362306a36Sopenharmony_ci	 * RX1, RX2, RX3, RX4, RX5, RX6, RX7, RX8, RX9, RX10, RX11, RX12, RX13
7462306a36Sopenharmony_ci	 * TX1, TX2, TX3, TX4, TX5, TX6, TX7, TX8, TX9, TX10, TX11, TX12, TX13
7562306a36Sopenharmony_ci	 * TX14, TX15, TX16
7662306a36Sopenharmony_ci	 */
7762306a36Sopenharmony_ci	unsigned int rx_ch[SLIM_MAX_RX_PORTS] = {144, 145, 146, 147, 148, 149,
7862306a36Sopenharmony_ci					150, 151, 152, 153, 154, 155, 156};
7962306a36Sopenharmony_ci	unsigned int tx_ch[SLIM_MAX_TX_PORTS] = {128, 129, 130, 131, 132, 133,
8062306a36Sopenharmony_ci					    134, 135, 136, 137, 138, 139,
8162306a36Sopenharmony_ci					    140, 141, 142, 143};
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch),
8462306a36Sopenharmony_ci					tx_ch, ARRAY_SIZE(rx_ch), rx_ch);
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	snd_soc_dai_set_sysclk(codec_dai, 0, WCD9335_DEFAULT_MCLK_RATE,
8762306a36Sopenharmony_ci				SNDRV_PCM_STREAM_PLAYBACK);
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	return 0;
9062306a36Sopenharmony_ci}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_cistatic void apq8096_add_be_ops(struct snd_soc_card *card)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci	struct snd_soc_dai_link *link;
9562306a36Sopenharmony_ci	int i;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	for_each_card_prelinks(card, i, link) {
9862306a36Sopenharmony_ci		if (link->no_pcm == 1) {
9962306a36Sopenharmony_ci			link->be_hw_params_fixup = apq8096_be_hw_params_fixup;
10062306a36Sopenharmony_ci			link->init = apq8096_init;
10162306a36Sopenharmony_ci			link->ops = &apq8096_ops;
10262306a36Sopenharmony_ci		}
10362306a36Sopenharmony_ci	}
10462306a36Sopenharmony_ci}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_cistatic int apq8096_platform_probe(struct platform_device *pdev)
10762306a36Sopenharmony_ci{
10862306a36Sopenharmony_ci	struct snd_soc_card *card;
10962306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
11062306a36Sopenharmony_ci	int ret;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
11362306a36Sopenharmony_ci	if (!card)
11462306a36Sopenharmony_ci		return -ENOMEM;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	card->driver_name = "apq8096";
11762306a36Sopenharmony_ci	card->dev = dev;
11862306a36Sopenharmony_ci	card->owner = THIS_MODULE;
11962306a36Sopenharmony_ci	dev_set_drvdata(dev, card);
12062306a36Sopenharmony_ci	ret = qcom_snd_parse_of(card);
12162306a36Sopenharmony_ci	if (ret)
12262306a36Sopenharmony_ci		return ret;
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	apq8096_add_be_ops(card);
12562306a36Sopenharmony_ci	return devm_snd_soc_register_card(dev, card);
12662306a36Sopenharmony_ci}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_cistatic const struct of_device_id msm_snd_apq8096_dt_match[] = {
12962306a36Sopenharmony_ci	{.compatible = "qcom,apq8096-sndcard"},
13062306a36Sopenharmony_ci	{}
13162306a36Sopenharmony_ci};
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, msm_snd_apq8096_dt_match);
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_cistatic struct platform_driver msm_snd_apq8096_driver = {
13662306a36Sopenharmony_ci	.probe  = apq8096_platform_probe,
13762306a36Sopenharmony_ci	.driver = {
13862306a36Sopenharmony_ci		.name = "msm-snd-apq8096",
13962306a36Sopenharmony_ci		.of_match_table = msm_snd_apq8096_dt_match,
14062306a36Sopenharmony_ci	},
14162306a36Sopenharmony_ci};
14262306a36Sopenharmony_cimodule_platform_driver(msm_snd_apq8096_driver);
14362306a36Sopenharmony_ciMODULE_AUTHOR("Srinivas Kandagatla <srinivas.kandagatla@linaro.org");
14462306a36Sopenharmony_ciMODULE_DESCRIPTION("APQ8096 ASoC Machine Driver");
14562306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
146