146 lines · c
1// SPDX-License-Identifier: GPL-2.02// Copyright (c) 2018, Linaro Limited3 4#include <linux/mod_devicetable.h>5#include <linux/module.h>6#include <linux/platform_device.h>7#include <sound/soc.h>8#include <sound/soc-dapm.h>9#include <sound/pcm.h>10#include "common.h"11 12#define SLIM_MAX_TX_PORTS 1613#define SLIM_MAX_RX_PORTS 1614#define WCD9335_DEFAULT_MCLK_RATE 960000015 16static int apq8096_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,17 struct snd_pcm_hw_params *params)18{19 struct snd_interval *rate = hw_param_interval(params,20 SNDRV_PCM_HW_PARAM_RATE);21 struct snd_interval *channels = hw_param_interval(params,22 SNDRV_PCM_HW_PARAM_CHANNELS);23 24 rate->min = rate->max = 48000;25 channels->min = channels->max = 2;26 27 return 0;28}29 30static int msm_snd_hw_params(struct snd_pcm_substream *substream,31 struct snd_pcm_hw_params *params)32{33 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);34 struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);35 struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);36 u32 rx_ch[SLIM_MAX_RX_PORTS], tx_ch[SLIM_MAX_TX_PORTS];37 u32 rx_ch_cnt = 0, tx_ch_cnt = 0;38 int ret = 0;39 40 ret = snd_soc_dai_get_channel_map(codec_dai,41 &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch);42 if (ret != 0 && ret != -ENOTSUPP) {43 pr_err("failed to get codec chan map, err:%d\n", ret);44 goto end;45 } else if (ret == -ENOTSUPP) {46 return 0;47 }48 49 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)50 ret = snd_soc_dai_set_channel_map(cpu_dai, 0, NULL,51 rx_ch_cnt, rx_ch);52 else53 ret = snd_soc_dai_set_channel_map(cpu_dai, tx_ch_cnt, tx_ch,54 0, NULL);55 if (ret != 0 && ret != -ENOTSUPP)56 pr_err("Failed to set cpu chan map, err:%d\n", ret);57 else if (ret == -ENOTSUPP)58 ret = 0;59end:60 return ret;61}62 63static const struct snd_soc_ops apq8096_ops = {64 .hw_params = msm_snd_hw_params,65};66 67static int apq8096_init(struct snd_soc_pcm_runtime *rtd)68{69 struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);70 71 /*72 * Codec SLIMBUS configuration73 * RX1, RX2, RX3, RX4, RX5, RX6, RX7, RX8, RX9, RX10, RX11, RX12, RX1374 * TX1, TX2, TX3, TX4, TX5, TX6, TX7, TX8, TX9, TX10, TX11, TX12, TX1375 * TX14, TX15, TX1676 */77 unsigned int rx_ch[SLIM_MAX_RX_PORTS] = {144, 145, 146, 147, 148, 149,78 150, 151, 152, 153, 154, 155, 156};79 unsigned int tx_ch[SLIM_MAX_TX_PORTS] = {128, 129, 130, 131, 132, 133,80 134, 135, 136, 137, 138, 139,81 140, 141, 142, 143};82 83 snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch),84 tx_ch, ARRAY_SIZE(rx_ch), rx_ch);85 86 snd_soc_dai_set_sysclk(codec_dai, 0, WCD9335_DEFAULT_MCLK_RATE,87 SNDRV_PCM_STREAM_PLAYBACK);88 89 return 0;90}91 92static void apq8096_add_be_ops(struct snd_soc_card *card)93{94 struct snd_soc_dai_link *link;95 int i;96 97 for_each_card_prelinks(card, i, link) {98 if (link->no_pcm == 1) {99 link->be_hw_params_fixup = apq8096_be_hw_params_fixup;100 link->init = apq8096_init;101 link->ops = &apq8096_ops;102 }103 }104}105 106static int apq8096_platform_probe(struct platform_device *pdev)107{108 struct snd_soc_card *card;109 struct device *dev = &pdev->dev;110 int ret;111 112 card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);113 if (!card)114 return -ENOMEM;115 116 card->driver_name = "apq8096";117 card->dev = dev;118 card->owner = THIS_MODULE;119 dev_set_drvdata(dev, card);120 ret = qcom_snd_parse_of(card);121 if (ret)122 return ret;123 124 apq8096_add_be_ops(card);125 return devm_snd_soc_register_card(dev, card);126}127 128static const struct of_device_id msm_snd_apq8096_dt_match[] = {129 {.compatible = "qcom,apq8096-sndcard"},130 {}131};132 133MODULE_DEVICE_TABLE(of, msm_snd_apq8096_dt_match);134 135static struct platform_driver msm_snd_apq8096_driver = {136 .probe = apq8096_platform_probe,137 .driver = {138 .name = "msm-snd-apq8096",139 .of_match_table = msm_snd_apq8096_dt_match,140 },141};142module_platform_driver(msm_snd_apq8096_driver);143MODULE_AUTHOR("Srinivas Kandagatla <srinivas.kandagatla@linaro.org");144MODULE_DESCRIPTION("APQ8096 ASoC Machine Driver");145MODULE_LICENSE("GPL");146