1 // SPDX-License-Identifier: GPL-2.0
2 // Copyright (c) 2018, Linaro Limited
4 #include <linux/module.h>
5 #include <linux/platform_device.h>
6 #include <linux/of_device.h>
8 #include <sound/soc-dapm.h>
12 #define SLIM_MAX_TX_PORTS 16
13 #define SLIM_MAX_RX_PORTS 16
14 #define WCD9335_DEFAULT_MCLK_RATE 9600000
16 static int apq8096_be_hw_params_fixup(struct snd_soc_pcm_runtime
*rtd
,
17 struct snd_pcm_hw_params
*params
)
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
);
24 rate
->min
= rate
->max
= 48000;
25 channels
->min
= channels
->max
= 2;
30 static int msm_snd_hw_params(struct snd_pcm_substream
*substream
,
31 struct snd_pcm_hw_params
*params
)
33 struct snd_soc_pcm_runtime
*rtd
= substream
->private_data
;
34 struct snd_soc_dai
*codec_dai
= rtd
->codec_dai
;
35 struct snd_soc_dai
*cpu_dai
= rtd
->cpu_dai
;
36 u32 rx_ch
[SLIM_MAX_RX_PORTS
], tx_ch
[SLIM_MAX_TX_PORTS
];
37 u32 rx_ch_cnt
= 0, tx_ch_cnt
= 0;
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
);
45 } else if (ret
== -ENOTSUPP
) {
49 if (substream
->stream
== SNDRV_PCM_STREAM_PLAYBACK
)
50 ret
= snd_soc_dai_set_channel_map(cpu_dai
, 0, NULL
,
53 ret
= snd_soc_dai_set_channel_map(cpu_dai
, tx_ch_cnt
, tx_ch
,
55 if (ret
!= 0 && ret
!= -ENOTSUPP
)
56 pr_err("Failed to set cpu chan map, err:%d\n", ret
);
57 else if (ret
== -ENOTSUPP
)
63 static struct snd_soc_ops apq8096_ops
= {
64 .hw_params
= msm_snd_hw_params
,
67 static int apq8096_init(struct snd_soc_pcm_runtime
*rtd
)
69 struct snd_soc_dai
*codec_dai
= rtd
->codec_dai
;
72 * Codec SLIMBUS configuration
73 * RX1, RX2, RX3, RX4, RX5, RX6, RX7, RX8, RX9, RX10, RX11, RX12, RX13
74 * TX1, TX2, TX3, TX4, TX5, TX6, TX7, TX8, TX9, TX10, TX11, TX12, TX13
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,
83 snd_soc_dai_set_channel_map(codec_dai
, ARRAY_SIZE(tx_ch
),
84 tx_ch
, ARRAY_SIZE(rx_ch
), rx_ch
);
86 snd_soc_dai_set_sysclk(codec_dai
, 0, WCD9335_DEFAULT_MCLK_RATE
,
87 SNDRV_PCM_STREAM_PLAYBACK
);
92 static void apq8096_add_be_ops(struct snd_soc_card
*card
)
94 struct snd_soc_dai_link
*link
;
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
;
106 static int apq8096_platform_probe(struct platform_device
*pdev
)
108 struct snd_soc_card
*card
;
109 struct device
*dev
= &pdev
->dev
;
112 card
= kzalloc(sizeof(*card
), GFP_KERNEL
);
117 dev_set_drvdata(dev
, card
);
118 ret
= qcom_snd_parse_of(card
);
120 dev_err(dev
, "Error parsing OF data\n");
124 apq8096_add_be_ops(card
);
125 ret
= snd_soc_register_card(card
);
127 goto err_card_register
;
132 kfree(card
->dai_link
);
138 static int apq8096_platform_remove(struct platform_device
*pdev
)
140 struct snd_soc_card
*card
= dev_get_drvdata(&pdev
->dev
);
142 snd_soc_unregister_card(card
);
143 kfree(card
->dai_link
);
149 static const struct of_device_id msm_snd_apq8096_dt_match
[] = {
150 {.compatible
= "qcom,apq8096-sndcard"},
154 MODULE_DEVICE_TABLE(of
, msm_snd_apq8096_dt_match
);
156 static struct platform_driver msm_snd_apq8096_driver
= {
157 .probe
= apq8096_platform_probe
,
158 .remove
= apq8096_platform_remove
,
160 .name
= "msm-snd-apq8096",
161 .of_match_table
= msm_snd_apq8096_dt_match
,
164 module_platform_driver(msm_snd_apq8096_driver
);
165 MODULE_AUTHOR("Srinivas Kandagatla <srinivas.kandagatla@linaro.org");
166 MODULE_DESCRIPTION("APQ8096 ASoC Machine Driver");
167 MODULE_LICENSE("GPL v2");