blob: 53388ed2e8caf44475e2fbf6a54de647c2a09f2e [file] [log] [blame]
Mahesh Kumar Sharma41a4d382017-01-17 17:00:51 -08001/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12#include <linux/init.h>
13#include <linux/kernel.h>
14#include <linux/module.h>
15#include <linux/of_gpio.h>
16#include <linux/delay.h>
17#include <linux/gpio.h>
18#include <linux/debugfs.h>
19#include <linux/slimbus/slimbus.h>
20#include <linux/ratelimit.h>
21#include <linux/slab.h>
22#include <sound/pcm.h>
23#include <sound/pcm_params.h>
24#include <sound/soc.h>
25#include <sound/soc-dapm.h>
26#include <sound/tlv.h>
27#include <btfm_slim.h>
28
Satish Kodishala8e4e9622017-05-18 19:19:30 +053029static int bt_soc_enable_status;
30
31
Mahesh Kumar Sharma41a4d382017-01-17 17:00:51 -080032static int btfm_slim_codec_write(struct snd_soc_codec *codec, unsigned int reg,
33 unsigned int value)
34{
35 return 0;
36}
37
38static unsigned int btfm_slim_codec_read(struct snd_soc_codec *codec,
39 unsigned int reg)
40{
41 return 0;
42}
43
Satish Kodishala8e4e9622017-05-18 19:19:30 +053044static int bt_soc_status_get(struct snd_kcontrol *kcontrol,
45 struct snd_ctl_elem_value *ucontrol)
46{
47 ucontrol->value.integer.value[0] = bt_soc_enable_status;
48 return 1;
49}
50
51static int bt_soc_status_put(struct snd_kcontrol *kcontrol,
52 struct snd_ctl_elem_value *ucontrol)
53{
54 return 1;
55}
56
57static const struct snd_kcontrol_new status_controls[] = {
58 SOC_SINGLE_EXT("BT SOC status", 0, 0, 1, 0,
59 bt_soc_status_get,
60 bt_soc_status_put)
61
62};
63
64
Mahesh Kumar Sharma41a4d382017-01-17 17:00:51 -080065static int btfm_slim_codec_probe(struct snd_soc_codec *codec)
66{
Satish Kodishala8e4e9622017-05-18 19:19:30 +053067 snd_soc_add_codec_controls(codec, status_controls,
68 ARRAY_SIZE(status_controls));
Mahesh Kumar Sharma41a4d382017-01-17 17:00:51 -080069 return 0;
70}
71
72static int btfm_slim_codec_remove(struct snd_soc_codec *codec)
73{
74 return 0;
75}
76
77static int btfm_slim_dai_startup(struct snd_pcm_substream *substream,
78 struct snd_soc_dai *dai)
79{
80 int ret;
81 struct btfmslim *btfmslim = dai->dev->platform_data;
82
Sungjun Park9b33e942017-02-23 12:50:03 -080083 BTFMSLIM_DBG("substream = %s stream = %d dai->name = %s",
Rupesh Tatiya3815c792017-02-17 12:58:01 +053084 substream->name, substream->stream, dai->name);
Mahesh Kumar Sharma41a4d382017-01-17 17:00:51 -080085 ret = btfm_slim_hw_init(btfmslim);
86 return ret;
87}
88
89static void btfm_slim_dai_shutdown(struct snd_pcm_substream *substream,
90 struct snd_soc_dai *dai)
91{
Sungjun Park9b33e942017-02-23 12:50:03 -080092 int i;
Mahesh Kumar Sharma41a4d382017-01-17 17:00:51 -080093 struct btfmslim *btfmslim = dai->dev->platform_data;
Sungjun Park9b33e942017-02-23 12:50:03 -080094 struct btfmslim_ch *ch;
95 uint8_t rxport, grp = false, nchan = 1;
Mahesh Kumar Sharma41a4d382017-01-17 17:00:51 -080096
Sungjun Park9b33e942017-02-23 12:50:03 -080097 BTFMSLIM_DBG("dai->name: %s, dai->id: %d, dai->rate: %d", dai->name,
98 dai->id, dai->rate);
99
100 switch (dai->id) {
101 case BTFM_FM_SLIM_TX:
102 grp = true; nchan = 2;
103 ch = btfmslim->tx_chs;
104 rxport = 0;
105 break;
106 case BTFM_BT_SCO_SLIM_TX:
107 ch = btfmslim->tx_chs;
108 rxport = 0;
109 break;
110 case BTFM_BT_SCO_A2DP_SLIM_RX:
111 case BTFM_BT_SPLIT_A2DP_SLIM_RX:
112 ch = btfmslim->rx_chs;
113 rxport = 1;
114 break;
115 case BTFM_SLIM_NUM_CODEC_DAIS:
116 default:
117 BTFMSLIM_ERR("dai->id is invalid:%d", dai->id);
118 return;
119 }
120
121 /* Search for dai->id matched port handler */
122 for (i = 0; (i < BTFM_SLIM_NUM_CODEC_DAIS) &&
123 (ch->id != BTFM_SLIM_NUM_CODEC_DAIS) &&
124 (ch->id != dai->id); ch++, i++)
125 ;
126
127 if ((ch->port == BTFM_SLIM_PGD_PORT_LAST) ||
128 (ch->id == BTFM_SLIM_NUM_CODEC_DAIS)) {
129 BTFMSLIM_ERR("ch is invalid!!");
130 return;
131 }
132
133 btfm_slim_disable_ch(btfmslim, ch, rxport, grp, nchan);
Mahesh Kumar Sharma41a4d382017-01-17 17:00:51 -0800134 btfm_slim_hw_deinit(btfmslim);
135}
136
137static int btfm_slim_dai_hw_params(struct snd_pcm_substream *substream,
138 struct snd_pcm_hw_params *params,
139 struct snd_soc_dai *dai)
140{
Sungjun Park9b33e942017-02-23 12:50:03 -0800141 BTFMSLIM_DBG("dai->name = %s DAI-ID %x rate %d num_ch %d",
Mahesh Kumar Sharma41a4d382017-01-17 17:00:51 -0800142 dai->name, dai->id, params_rate(params),
143 params_channels(params));
144
145 return 0;
146}
147
Sungjun Park9b33e942017-02-23 12:50:03 -0800148static int btfm_slim_dai_prepare(struct snd_pcm_substream *substream,
Mahesh Kumar Sharma41a4d382017-01-17 17:00:51 -0800149 struct snd_soc_dai *dai)
150{
151 int i, ret = -EINVAL;
152 struct btfmslim *btfmslim = dai->dev->platform_data;
153 struct btfmslim_ch *ch;
154 uint8_t rxport, grp = false, nchan = 1;
Satish Kodishala8e4e9622017-05-18 19:19:30 +0530155 bt_soc_enable_status = 0;
Mahesh Kumar Sharma41a4d382017-01-17 17:00:51 -0800156
Sungjun Park9b33e942017-02-23 12:50:03 -0800157 BTFMSLIM_DBG("dai->name: %s, dai->id: %d, dai->rate: %d", dai->name,
Mahesh Kumar Sharma41a4d382017-01-17 17:00:51 -0800158 dai->id, dai->rate);
159
Satish Kodishala93194f82017-05-18 20:11:20 +0530160 /* save sample rate */
161 btfmslim->sample_rate = dai->rate;
162
Mahesh Kumar Sharma41a4d382017-01-17 17:00:51 -0800163 switch (dai->id) {
164 case BTFM_FM_SLIM_TX:
165 grp = true; nchan = 2;
166 ch = btfmslim->tx_chs;
167 rxport = 0;
168 break;
169 case BTFM_BT_SCO_SLIM_TX:
170 ch = btfmslim->tx_chs;
171 rxport = 0;
172 break;
173 case BTFM_BT_SCO_A2DP_SLIM_RX:
174 case BTFM_BT_SPLIT_A2DP_SLIM_RX:
175 ch = btfmslim->rx_chs;
176 rxport = 1;
177 break;
178 case BTFM_SLIM_NUM_CODEC_DAIS:
179 default:
180 BTFMSLIM_ERR("dai->id is invalid:%d", dai->id);
181 return ret;
182 }
183
184 /* Search for dai->id matched port handler */
185 for (i = 0; (i < BTFM_SLIM_NUM_CODEC_DAIS) &&
186 (ch->id != BTFM_SLIM_NUM_CODEC_DAIS) &&
187 (ch->id != dai->id); ch++, i++)
188 ;
189
190 if ((ch->port == BTFM_SLIM_PGD_PORT_LAST) ||
191 (ch->id == BTFM_SLIM_NUM_CODEC_DAIS)) {
192 BTFMSLIM_ERR("ch is invalid!!");
193 return ret;
194 }
195
196 ret = btfm_slim_enable_ch(btfmslim, ch, rxport, dai->rate, grp, nchan);
Satish Kodishala8e4e9622017-05-18 19:19:30 +0530197
198 /* save the enable channel status */
199 if (ret == 0)
200 bt_soc_enable_status = 1;
Mahesh Kumar Sharma41a4d382017-01-17 17:00:51 -0800201 return ret;
202}
203
Mahesh Kumar Sharma41a4d382017-01-17 17:00:51 -0800204/* This function will be called once during boot up */
205static int btfm_slim_dai_set_channel_map(struct snd_soc_dai *dai,
206 unsigned int tx_num, unsigned int *tx_slot,
207 unsigned int rx_num, unsigned int *rx_slot)
208{
209 int ret = -EINVAL, i;
210 struct btfmslim *btfmslim = dai->dev->platform_data;
211 struct btfmslim_ch *rx_chs;
212 struct btfmslim_ch *tx_chs;
213
214 BTFMSLIM_DBG("");
215
216 if (!btfmslim)
217 return ret;
218
219 rx_chs = btfmslim->rx_chs;
220 tx_chs = btfmslim->tx_chs;
221
222 if (!rx_chs || !tx_chs)
223 return ret;
224
225 BTFMSLIM_DBG("Rx: id\tname\tport\thdl\tch\tch_hdl");
226 for (i = 0; (rx_chs->port != BTFM_SLIM_PGD_PORT_LAST) && (i < rx_num);
227 i++, rx_chs++) {
228 /* Set Rx Channel number from machine driver and
229 * get channel handler from slimbus driver
230 */
231 rx_chs->ch = *(uint8_t *)(rx_slot + i);
232 ret = slim_query_ch(btfmslim->slim_pgd, rx_chs->ch,
233 &rx_chs->ch_hdl);
234 if (ret < 0) {
235 BTFMSLIM_ERR("slim_query_ch failure ch#%d - ret[%d]",
236 rx_chs->ch, ret);
237 goto error;
238 }
239 BTFMSLIM_DBG(" %d\t%s\t%d\t%x\t%d\t%x", rx_chs->id,
240 rx_chs->name, rx_chs->port, rx_chs->port_hdl,
241 rx_chs->ch, rx_chs->ch_hdl);
242 }
243
244 BTFMSLIM_DBG("Tx: id\tname\tport\thdl\tch\tch_hdl");
245 for (i = 0; (tx_chs->port != BTFM_SLIM_PGD_PORT_LAST) && (i < tx_num);
246 i++, tx_chs++) {
247 /* Set Tx Channel number from machine driver and
248 * get channel handler from slimbus driver
249 */
250 tx_chs->ch = *(uint8_t *)(tx_slot + i);
251 ret = slim_query_ch(btfmslim->slim_pgd, tx_chs->ch,
252 &tx_chs->ch_hdl);
253 if (ret < 0) {
254 BTFMSLIM_ERR("slim_query_ch failure ch#%d - ret[%d]",
255 tx_chs->ch, ret);
256 goto error;
257 }
258 BTFMSLIM_DBG(" %d\t%s\t%d\t%x\t%d\t%x", tx_chs->id,
259 tx_chs->name, tx_chs->port, tx_chs->port_hdl,
260 tx_chs->ch, tx_chs->ch_hdl);
261 }
262
263error:
264 return ret;
265}
266
267static int btfm_slim_dai_get_channel_map(struct snd_soc_dai *dai,
268 unsigned int *tx_num, unsigned int *tx_slot,
269 unsigned int *rx_num, unsigned int *rx_slot)
270{
271 int i, ret = -EINVAL, *slot = NULL, j = 0, num = 1;
272 struct btfmslim *btfmslim = dai->dev->platform_data;
273 struct btfmslim_ch *ch = NULL;
274
275 if (!btfmslim)
276 return ret;
277
278 switch (dai->id) {
279 case BTFM_FM_SLIM_TX:
280 num = 2;
281 case BTFM_BT_SCO_SLIM_TX:
282 if (!tx_slot || !tx_num) {
283 BTFMSLIM_ERR("Invalid tx_slot %p or tx_num %p",
284 tx_slot, tx_num);
285 return -EINVAL;
286 }
287 ch = btfmslim->tx_chs;
288 if (!ch)
289 return -EINVAL;
290 slot = tx_slot;
291 *rx_slot = 0;
292 *tx_num = num;
293 *rx_num = 0;
294 break;
295 case BTFM_BT_SCO_A2DP_SLIM_RX:
296 case BTFM_BT_SPLIT_A2DP_SLIM_RX:
297 if (!rx_slot || !rx_num) {
298 BTFMSLIM_ERR("Invalid rx_slot %p or rx_num %p",
299 rx_slot, rx_num);
300 return -EINVAL;
301 }
302 ch = btfmslim->rx_chs;
303 if (!ch)
304 return -EINVAL;
305 slot = rx_slot;
306 *tx_slot = 0;
307 *tx_num = 0;
308 *rx_num = num;
309 break;
Rupesh Tatiya03a5d4f2017-05-08 10:36:19 +0530310 default:
311 BTFMSLIM_ERR("Unsupported DAI %d", dai->id);
312 return -EINVAL;
Mahesh Kumar Sharma41a4d382017-01-17 17:00:51 -0800313 }
314
315 do {
316 if (!ch)
317 return -EINVAL;
318 for (i = 0; (i < BTFM_SLIM_NUM_CODEC_DAIS) && (ch->id !=
319 BTFM_SLIM_NUM_CODEC_DAIS) && (ch->id != dai->id);
320 ch++, i++)
321 ;
322
323 if (ch->id == BTFM_SLIM_NUM_CODEC_DAIS ||
324 i == BTFM_SLIM_NUM_CODEC_DAIS) {
325 BTFMSLIM_ERR(
326 "No channel has been allocated for dai (%d)",
327 dai->id);
328 return -EINVAL;
329 }
330 if (!slot)
331 return -EINVAL;
332 *(slot + j) = ch->ch;
333 BTFMSLIM_DBG("id:%d, port:%d, ch:%d, slot: %d", ch->id,
334 ch->port, ch->ch, *(slot + j));
335
336 /* In case it has mulitiple channels */
337 if (++j < num)
338 ch++;
339 } while (j < num);
340
341 return 0;
342}
343
344static struct snd_soc_dai_ops btfmslim_dai_ops = {
345 .startup = btfm_slim_dai_startup,
346 .shutdown = btfm_slim_dai_shutdown,
347 .hw_params = btfm_slim_dai_hw_params,
348 .prepare = btfm_slim_dai_prepare,
Mahesh Kumar Sharma41a4d382017-01-17 17:00:51 -0800349 .set_channel_map = btfm_slim_dai_set_channel_map,
350 .get_channel_map = btfm_slim_dai_get_channel_map,
351};
352
353static struct snd_soc_dai_driver btfmslim_dai[] = {
354 { /* FM Audio data multiple channel : FM -> qdsp */
355 .name = "btfm_fm_slim_tx",
356 .id = BTFM_FM_SLIM_TX,
357 .capture = {
358 .stream_name = "FM TX Capture",
359 .rates = SNDRV_PCM_RATE_48000, /* 48 KHz */
360 .formats = SNDRV_PCM_FMTBIT_S16_LE, /* 16 bits */
361 .rate_max = 48000,
362 .rate_min = 48000,
363 .channels_min = 1,
364 .channels_max = 2,
365 },
366 .ops = &btfmslim_dai_ops,
367 },
368 { /* Bluetooth SCO voice uplink: bt -> modem */
369 .name = "btfm_bt_sco_slim_tx",
370 .id = BTFM_BT_SCO_SLIM_TX,
371 .capture = {
372 .stream_name = "SCO TX Capture",
373 /* 8 KHz or 16 KHz */
374 .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
375 .formats = SNDRV_PCM_FMTBIT_S16_LE, /* 16 bits */
376 .rate_max = 16000,
377 .rate_min = 8000,
378 .channels_min = 1,
379 .channels_max = 1,
380 },
381 .ops = &btfmslim_dai_ops,
382 },
383 { /* Bluetooth SCO voice downlink: modem -> bt or A2DP Playback */
384 .name = "btfm_bt_sco_a2dp_slim_rx",
385 .id = BTFM_BT_SCO_A2DP_SLIM_RX,
386 .playback = {
387 .stream_name = "SCO A2DP RX Playback",
Sungjun Parkea258e42017-10-18 19:08:25 -0700388 /* 8/16/44.1/48/88.2/96 Khz */
Mahesh Kumar Sharma41a4d382017-01-17 17:00:51 -0800389 .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000
Sungjun Parkea258e42017-10-18 19:08:25 -0700390 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000
391 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000,
Mahesh Kumar Sharma41a4d382017-01-17 17:00:51 -0800392 .formats = SNDRV_PCM_FMTBIT_S16_LE, /* 16 bits */
Sungjun Parkea258e42017-10-18 19:08:25 -0700393 .rate_max = 96000,
Mahesh Kumar Sharma41a4d382017-01-17 17:00:51 -0800394 .rate_min = 8000,
395 .channels_min = 1,
396 .channels_max = 1,
397 },
398 .ops = &btfmslim_dai_ops,
399 },
400 { /* Bluetooth Split A2DP data: qdsp -> bt */
401 .name = "btfm_bt_split_a2dp_slim_rx",
402 .id = BTFM_BT_SPLIT_A2DP_SLIM_RX,
403 .playback = {
404 .stream_name = "SPLIT A2DP Playback",
405 .rates = SNDRV_PCM_RATE_48000, /* 48 KHz */
406 .formats = SNDRV_PCM_FMTBIT_S16_LE, /* 16 bits */
407 .rate_max = 48000,
408 .rate_min = 48000,
409 .channels_min = 1,
410 .channels_max = 1,
411 },
412 .ops = &btfmslim_dai_ops,
413 },
414};
415
416static struct snd_soc_codec_driver btfmslim_codec = {
417 .probe = btfm_slim_codec_probe,
418 .remove = btfm_slim_codec_remove,
Rupesh Tatiya3815c792017-02-17 12:58:01 +0530419 .read = btfm_slim_codec_read,
Mahesh Kumar Sharma41a4d382017-01-17 17:00:51 -0800420 .write = btfm_slim_codec_write,
421};
422
423int btfm_slim_register_codec(struct device *dev)
424{
425 int ret = 0;
426
427 BTFMSLIM_DBG("");
428 /* Register Codec driver */
429 ret = snd_soc_register_codec(dev, &btfmslim_codec,
430 btfmslim_dai, ARRAY_SIZE(btfmslim_dai));
431
432 if (ret)
433 BTFMSLIM_ERR("failed to register codec (%d)", ret);
434
435 return ret;
436}
437
Satish kumar sugasibcf528a2017-07-27 13:37:22 -0700438void btfm_slim_unregister_codec(struct device *dev)
439{
440 BTFMSLIM_DBG("");
441 /* Unregister Codec driver */
442 snd_soc_unregister_codec(dev);
443}
444
Mahesh Kumar Sharma41a4d382017-01-17 17:00:51 -0800445MODULE_DESCRIPTION("BTFM Slimbus Codec driver");
446MODULE_LICENSE("GPL v2");