blob: 6792e045c7f0499259c08449dcbcd20ab6cc94d6 [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/ratelimit.h>
20#include <linux/slab.h>
21#include <sound/pcm.h>
22#include <sound/pcm_params.h>
23#include <sound/soc.h>
24#include <sound/soc-dapm.h>
25#include <sound/tlv.h>
26#include <btfm_slim.h>
27#include <btfm_slim_wcn3990.h>
28#include <linux/bluetooth-power.h>
29
30int btfm_slim_write(struct btfmslim *btfmslim,
31 uint16_t reg, int bytes, void *src, uint8_t pgd)
32{
33 int ret, i;
34 struct slim_ele_access msg;
35 int slim_write_tries = SLIM_SLAVE_RW_MAX_TRIES;
36
37 BTFMSLIM_DBG("Write to %s", pgd?"PGD":"IFD");
38 msg.start_offset = SLIM_SLAVE_REG_OFFSET + reg;
39 msg.num_bytes = bytes;
40 msg.comp = NULL;
41
42 for ( ; slim_write_tries != 0; slim_write_tries--) {
43 mutex_lock(&btfmslim->xfer_lock);
44 ret = slim_change_val_element(pgd ? btfmslim->slim_pgd :
45 &btfmslim->slim_ifd, &msg, src, bytes);
46 mutex_unlock(&btfmslim->xfer_lock);
47 if (ret == 0)
48 break;
49 usleep_range(5000, 5100);
50 }
51
52 if (ret) {
53 BTFMSLIM_ERR("failed (%d)", ret);
54 return ret;
55 }
56
57 for (i = 0; i < bytes; i++)
58 BTFMSLIM_DBG("Write 0x%02x to reg 0x%x", ((uint8_t *)src)[i],
59 reg + i);
60 return 0;
61}
62
63int btfm_slim_write_pgd(struct btfmslim *btfmslim,
64 uint16_t reg, int bytes, void *src)
65{
66 return btfm_slim_write(btfmslim, reg, bytes, src, PGD);
67}
68
69int btfm_slim_write_inf(struct btfmslim *btfmslim,
70 uint16_t reg, int bytes, void *src)
71{
72 return btfm_slim_write(btfmslim, reg, bytes, src, IFD);
73}
74
75int btfm_slim_read(struct btfmslim *btfmslim, unsigned short reg,
76 int bytes, void *dest, uint8_t pgd)
77{
78 int ret, i;
79 struct slim_ele_access msg;
80 int slim_read_tries = SLIM_SLAVE_RW_MAX_TRIES;
81
82 BTFMSLIM_DBG("Read from %s", pgd?"PGD":"IFD");
83 msg.start_offset = SLIM_SLAVE_REG_OFFSET + reg;
84 msg.num_bytes = bytes;
85 msg.comp = NULL;
86
87 for ( ; slim_read_tries != 0; slim_read_tries--) {
88 mutex_lock(&btfmslim->xfer_lock);
89 ret = slim_request_val_element(pgd ? btfmslim->slim_pgd :
90 &btfmslim->slim_ifd, &msg, dest, bytes);
91 mutex_unlock(&btfmslim->xfer_lock);
92 if (ret == 0)
93 break;
94 usleep_range(5000, 5100);
95 }
96
97 if (ret)
98 BTFMSLIM_ERR("failed (%d)", ret);
99
100 for (i = 0; i < bytes; i++)
101 BTFMSLIM_DBG("Read 0x%02x from reg 0x%x", ((uint8_t *)dest)[i],
102 reg + i);
103
104 return 0;
105}
106
107int btfm_slim_read_pgd(struct btfmslim *btfmslim,
108 uint16_t reg, int bytes, void *dest)
109{
110 return btfm_slim_read(btfmslim, reg, bytes, dest, PGD);
111}
112
113int btfm_slim_read_inf(struct btfmslim *btfmslim,
114 uint16_t reg, int bytes, void *dest)
115{
116 return btfm_slim_read(btfmslim, reg, bytes, dest, IFD);
117}
118
119int btfm_slim_enable_ch(struct btfmslim *btfmslim, struct btfmslim_ch *ch,
120 uint8_t rxport, uint32_t rates, uint8_t grp, uint8_t nchan)
121{
122 int ret, i;
123 struct slim_ch prop;
124 struct btfmslim_ch *chan = ch;
125 uint16_t ch_h[2];
126
127 if (!btfmslim || !ch)
128 return -EINVAL;
129
Rupesh Tatiya3815c792017-02-17 12:58:01 +0530130 BTFMSLIM_DBG("port: %d ch: %d", ch->port, ch->ch);
Mahesh Kumar Sharma41a4d382017-01-17 17:00:51 -0800131
132 /* Define the channel with below parameters */
Satish Kodishala530e0b62017-12-06 16:28:09 +0530133 prop.prot = ((rates == 44100) || (rates == 88200)) ?
134 SLIM_PUSH : SLIM_AUTO_ISO;
Sungjun Parkea258e42017-10-18 19:08:25 -0700135 prop.baser = ((rates == 44100) || (rates == 88200)) ?
136 SLIM_RATE_11025HZ : SLIM_RATE_4000HZ;
137 prop.dataf = ((rates == 48000) || (rates == 44100) ||
138 (rates == 88200) || (rates == 96000)) ?
139 SLIM_CH_DATAF_NOT_DEFINED : SLIM_CH_DATAF_LPCM_AUDIO;
Mahesh Kumar Sharma41a4d382017-01-17 17:00:51 -0800140 prop.auxf = SLIM_CH_AUXF_NOT_APPLICABLE;
Sungjun Parkea258e42017-10-18 19:08:25 -0700141 prop.ratem = ((rates == 44100) || (rates == 88200)) ?
142 (rates/11025) : (rates/4000);
Mahesh Kumar Sharma41a4d382017-01-17 17:00:51 -0800143 prop.sampleszbits = 16;
144
145 ch_h[0] = ch->ch_hdl;
146 ch_h[1] = (grp) ? (ch+1)->ch_hdl : 0;
147
Sungjun Parkea258e42017-10-18 19:08:25 -0700148 BTFMSLIM_INFO("channel define - prot:%d, dataf:%d, auxf:%d",
149 prop.prot, prop.dataf, prop.auxf);
150 BTFMSLIM_INFO("channel define - rates:%d, baser:%d, ratem:%d",
151 rates, prop.baser, prop.ratem);
152
Mahesh Kumar Sharma41a4d382017-01-17 17:00:51 -0800153 ret = slim_define_ch(btfmslim->slim_pgd, &prop, ch_h, nchan, grp,
154 &ch->grph);
155 if (ret < 0) {
156 BTFMSLIM_ERR("slim_define_ch failed ret[%d]", ret);
157 goto error;
158 }
159
160 for (i = 0; i < nchan; i++, ch++) {
161 /* Enable port through registration setting */
162 if (btfmslim->vendor_port_en) {
163 ret = btfmslim->vendor_port_en(btfmslim, ch->port,
164 rxport, 1);
165 if (ret < 0) {
166 BTFMSLIM_ERR("vendor_port_en failed ret[%d]",
167 ret);
168 goto error;
169 }
170 }
171
172 if (rxport) {
173 BTFMSLIM_INFO("slim_connect_sink(port: %d, ch: %d)",
174 ch->port, ch->ch);
175 /* Connect Port with channel given by Machine driver*/
176 ret = slim_connect_sink(btfmslim->slim_pgd,
177 &ch->port_hdl, 1, ch->ch_hdl);
178 if (ret < 0) {
179 BTFMSLIM_ERR("slim_connect_sink failed ret[%d]",
180 ret);
181 goto remove_channel;
182 }
183
184 } else {
185 BTFMSLIM_INFO("slim_connect_src(port: %d, ch: %d)",
186 ch->port, ch->ch);
187 /* Connect Port with channel given by Machine driver*/
188 ret = slim_connect_src(btfmslim->slim_pgd, ch->port_hdl,
189 ch->ch_hdl);
190 if (ret < 0) {
191 BTFMSLIM_ERR("slim_connect_src failed ret[%d]",
192 ret);
193 goto remove_channel;
194 }
195 }
196 }
197
198 /* Activate the channel immediately */
199 BTFMSLIM_INFO(
200 "port: %d, ch: %d, grp: %d, ch->grph: 0x%x, ch_hdl: 0x%x",
201 chan->port, chan->ch, grp, chan->grph, chan->ch_hdl);
202 ret = slim_control_ch(btfmslim->slim_pgd, (grp ? chan->grph :
203 chan->ch_hdl), SLIM_CH_ACTIVATE, true);
204 if (ret < 0) {
205 BTFMSLIM_ERR("slim_control_ch failed ret[%d]", ret);
206 goto remove_channel;
207 }
208
209error:
210 return ret;
211
212remove_channel:
213 /* Remove the channel immediately*/
214 ret = slim_control_ch(btfmslim->slim_pgd, (grp ? ch->grph : ch->ch_hdl),
215 SLIM_CH_REMOVE, true);
216 if (ret < 0)
217 BTFMSLIM_ERR("slim_control_ch failed ret[%d]", ret);
218
219 return ret;
220}
221
222int btfm_slim_disable_ch(struct btfmslim *btfmslim, struct btfmslim_ch *ch,
223 uint8_t rxport, uint8_t grp, uint8_t nchan)
224{
225 int ret, i;
226
227 if (!btfmslim || !ch)
228 return -EINVAL;
229
230 BTFMSLIM_INFO("port:%d, grp: %d, ch->grph:0x%x, ch->ch_hdl:0x%x ",
231 ch->port, grp, ch->grph, ch->ch_hdl);
Satish Kodishala530e0b62017-12-06 16:28:09 +0530232
233 /* For 44.1/88.2 Khz A2DP Rx, disconnect the port first */
234 if (rxport &&
235 (btfmslim->sample_rate == 44100 ||
236 btfmslim->sample_rate == 88200)) {
237 BTFMSLIM_DBG("disconnecting the ports, removing the channel");
238 ret = slim_disconnect_ports(btfmslim->slim_pgd,
239 &ch->port_hdl, 1);
240 if (ret < 0) {
241 BTFMSLIM_ERR("slim_disconnect_ports failed ret[%d]",
242 ret);
243 }
244 }
245
Mahesh Kumar Sharma41a4d382017-01-17 17:00:51 -0800246 /* Remove the channel immediately*/
247 ret = slim_control_ch(btfmslim->slim_pgd, (grp ? ch->grph : ch->ch_hdl),
248 SLIM_CH_REMOVE, true);
249 if (ret < 0) {
250 BTFMSLIM_ERR("slim_control_ch failed ret[%d]", ret);
Satish Kodishala530e0b62017-12-06 16:28:09 +0530251 if (btfmslim->sample_rate != 44100 &&
252 btfmslim->sample_rate != 88200) {
253 ret = slim_disconnect_ports(btfmslim->slim_pgd,
254 &ch->port_hdl, 1);
255 if (ret < 0) {
256 BTFMSLIM_ERR("disconnect_ports failed ret[%d]",
257 ret);
258 goto error;
259 }
Mahesh Kumar Sharma41a4d382017-01-17 17:00:51 -0800260 }
261 }
262
263 /* Disable port through registration setting */
264 for (i = 0; i < nchan; i++, ch++) {
265 if (btfmslim->vendor_port_en) {
266 ret = btfmslim->vendor_port_en(btfmslim, ch->port,
267 rxport, 0);
268 if (ret < 0) {
269 BTFMSLIM_ERR("vendor_port_en failed ret[%d]",
270 ret);
271 break;
272 }
273 }
274 }
275error:
276 return ret;
277}
278static int btfm_slim_get_logical_addr(struct slim_device *slim)
279{
280 int ret = 0;
281 const unsigned long timeout = jiffies +
282 msecs_to_jiffies(SLIM_SLAVE_PRESENT_TIMEOUT);
283
284 do {
285 ret = slim_get_logical_addr(slim, slim->e_addr,
286 ARRAY_SIZE(slim->e_addr), &slim->laddr);
287 if (!ret) {
288 BTFMSLIM_DBG("Assigned l-addr: 0x%x", slim->laddr);
289 break;
290 }
291 /* Give SLIMBUS time to report present and be ready. */
292 usleep_range(1000, 1100);
293 BTFMSLIM_DBG("retyring get logical addr");
294 } while (time_before(jiffies, timeout));
295
296 return ret;
297}
298
299static int btfm_slim_alloc_port(struct btfmslim *btfmslim)
300{
301 int ret = -EINVAL, i;
302 struct btfmslim_ch *rx_chs;
303 struct btfmslim_ch *tx_chs;
304
305 if (!btfmslim)
306 return ret;
307
308 rx_chs = btfmslim->rx_chs;
309 tx_chs = btfmslim->tx_chs;
310
311 if (!rx_chs || !tx_chs)
312 return ret;
313
314 BTFMSLIM_DBG("Rx: id\tname\tport\thdl\tch\tch_hdl");
315 for (i = 0 ; (rx_chs->port != BTFM_SLIM_PGD_PORT_LAST) &&
316 (i < BTFM_SLIM_NUM_CODEC_DAIS); i++, rx_chs++) {
317
318 /* Get Rx port handler from slimbus driver based
319 * on port number
320 */
321 ret = slim_get_slaveport(btfmslim->slim_pgd->laddr,
322 rx_chs->port, &rx_chs->port_hdl, SLIM_SINK);
323 if (ret < 0) {
324 BTFMSLIM_ERR("slave port failure port#%d - ret[%d]",
325 rx_chs->port, SLIM_SINK);
326 return ret;
327 }
328 BTFMSLIM_DBG(" %d\t%s\t%d\t%x\t%d\t%x", rx_chs->id,
329 rx_chs->name, rx_chs->port, rx_chs->port_hdl,
330 rx_chs->ch, rx_chs->ch_hdl);
331 }
332
333 BTFMSLIM_DBG("Tx: id\tname\tport\thdl\tch\tch_hdl");
334 for (i = 0; (tx_chs->port != BTFM_SLIM_PGD_PORT_LAST) &&
335 (i < BTFM_SLIM_NUM_CODEC_DAIS); i++, tx_chs++) {
336
337 /* Get Tx port handler from slimbus driver based
338 * on port number
339 */
340 ret = slim_get_slaveport(btfmslim->slim_pgd->laddr,
341 tx_chs->port, &tx_chs->port_hdl, SLIM_SRC);
342 if (ret < 0) {
343 BTFMSLIM_ERR("slave port failure port#%d - ret[%d]",
344 tx_chs->port, SLIM_SRC);
345 return ret;
346 }
347 BTFMSLIM_DBG(" %d\t%s\t%d\t%x\t%d\t%x", tx_chs->id,
348 tx_chs->name, tx_chs->port, tx_chs->port_hdl,
349 tx_chs->ch, tx_chs->ch_hdl);
350 }
351 return ret;
352}
353
354int btfm_slim_hw_init(struct btfmslim *btfmslim)
355{
356 int ret;
357
358 BTFMSLIM_DBG("");
359 if (!btfmslim)
360 return -EINVAL;
361
362 if (btfmslim->enabled) {
363 BTFMSLIM_DBG("Already enabled");
364 return 0;
365 }
366 mutex_lock(&btfmslim->io_lock);
367
368 /* Assign Logical Address for PGD (Ported Generic Device)
369 * enumeration address
370 */
371 ret = btfm_slim_get_logical_addr(btfmslim->slim_pgd);
372 if (ret) {
373 BTFMSLIM_ERR("failed to get slimbus %s logical address: %d",
374 btfmslim->slim_pgd->name, ret);
375 goto error;
376 }
377
378 /* Assign Logical Address for Ported Generic Device
379 * enumeration address
380 */
381 ret = btfm_slim_get_logical_addr(&btfmslim->slim_ifd);
382 if (ret) {
383 BTFMSLIM_ERR("failed to get slimbus %s logical address: %d",
384 btfmslim->slim_ifd.name, ret);
385 goto error;
386 }
387
388 /* Allocate ports with logical address to get port handler from
389 * slimbus driver
390 */
391 ret = btfm_slim_alloc_port(btfmslim);
392 if (ret)
393 goto error;
394
395 /* Start vendor specific initialization and get port information */
396 if (btfmslim->vendor_init)
397 ret = btfmslim->vendor_init(btfmslim);
398
399 /* Only when all registers read/write successfully, it set to
400 * enabled status
401 */
402 btfmslim->enabled = 1;
403error:
404 mutex_unlock(&btfmslim->io_lock);
405 return ret;
406}
407
408
409int btfm_slim_hw_deinit(struct btfmslim *btfmslim)
410{
411 int ret = 0;
412
413 if (!btfmslim)
414 return -EINVAL;
415
416 if (!btfmslim->enabled) {
417 BTFMSLIM_DBG("Already disabled");
418 return 0;
419 }
420 mutex_lock(&btfmslim->io_lock);
421 btfmslim->enabled = 0;
422 mutex_unlock(&btfmslim->io_lock);
423 return ret;
424}
425
426static int btfm_slim_get_dt_info(struct btfmslim *btfmslim)
427{
428 int ret = 0;
429 struct slim_device *slim = btfmslim->slim_pgd;
430 struct slim_device *slim_ifd = &btfmslim->slim_ifd;
431 struct property *prop;
432
433 if (!slim || !slim_ifd)
434 return -EINVAL;
435
436 if (slim->dev.of_node) {
437 BTFMSLIM_DBG("Platform data from device tree (%s)",
438 slim->name);
439 ret = of_property_read_string(slim->dev.of_node,
440 "qcom,btfm-slim-ifd", &slim_ifd->name);
441 if (ret) {
442 BTFMSLIM_ERR("Looking up %s property in node %s failed",
443 "qcom,btfm-slim-ifd",
444 slim->dev.of_node->full_name);
445 return -ENODEV;
446 }
447 BTFMSLIM_DBG("qcom,btfm-slim-ifd (%s)", slim_ifd->name);
448
449 prop = of_find_property(slim->dev.of_node,
450 "qcom,btfm-slim-ifd-elemental-addr", NULL);
451 if (!prop) {
452 BTFMSLIM_ERR("Looking up %s property in node %s failed",
453 "qcom,btfm-slim-ifd-elemental-addr",
454 slim->dev.of_node->full_name);
455 return -ENODEV;
456 } else if (prop->length != 6) {
457 BTFMSLIM_ERR(
458 "invalid codec slim ifd addr. addr length= %d",
459 prop->length);
460 return -ENODEV;
461 }
462 memcpy(slim_ifd->e_addr, prop->value, 6);
463 BTFMSLIM_DBG(
464 "PGD Enum Addr: %.02x:%.02x:%.02x:%.02x:%.02x: %.02x",
465 slim->e_addr[0], slim->e_addr[1], slim->e_addr[2],
466 slim->e_addr[3], slim->e_addr[4], slim->e_addr[5]);
467 BTFMSLIM_DBG(
468 "IFD Enum Addr: %.02x:%.02x:%.02x:%.02x:%.02x: %.02x",
469 slim_ifd->e_addr[0], slim_ifd->e_addr[1],
470 slim_ifd->e_addr[2], slim_ifd->e_addr[3],
471 slim_ifd->e_addr[4], slim_ifd->e_addr[5]);
472 } else {
473 BTFMSLIM_ERR("Platform data is not valid");
474 }
475
476 return ret;
477}
478
479static int btfm_slim_probe(struct slim_device *slim)
480{
481 int ret = 0;
482 struct btfmslim *btfm_slim;
483
484 BTFMSLIM_DBG("");
485 if (!slim->ctrl)
486 return -EINVAL;
487
488 /* Allocation btfmslim data pointer */
489 btfm_slim = kzalloc(sizeof(struct btfmslim), GFP_KERNEL);
490 if (btfm_slim == NULL) {
491 BTFMSLIM_ERR("error, allocation failed");
492 return -ENOMEM;
493 }
494 /* BTFM Slimbus driver control data configuration */
495 btfm_slim->slim_pgd = slim;
496
497 /* Assign vendor specific function */
498 btfm_slim->rx_chs = SLIM_SLAVE_RXPORT;
499 btfm_slim->tx_chs = SLIM_SLAVE_TXPORT;
500 btfm_slim->vendor_init = SLIM_SLAVE_INIT;
501 btfm_slim->vendor_port_en = SLIM_SLAVE_PORT_EN;
502
503 /* Created Mutex for slimbus data transfer */
504 mutex_init(&btfm_slim->io_lock);
505 mutex_init(&btfm_slim->xfer_lock);
506
507 /* Get Device tree node for Interface Device enumeration address */
508 ret = btfm_slim_get_dt_info(btfm_slim);
509 if (ret)
510 goto dealloc;
511
512 /* Add Interface Device for slimbus driver */
513 ret = slim_add_device(btfm_slim->slim_pgd->ctrl, &btfm_slim->slim_ifd);
514 if (ret) {
515 BTFMSLIM_ERR("error, adding SLIMBUS device failed");
516 goto dealloc;
517 }
518
519 /* Platform driver data allocation */
520 slim->dev.platform_data = btfm_slim;
521
522 /* Driver specific data allocation */
523 btfm_slim->dev = &slim->dev;
524 ret = btfm_slim_register_codec(&slim->dev);
Satish kumar sugasibcf528a2017-07-27 13:37:22 -0700525 if (ret) {
526 BTFMSLIM_ERR("error, registering slimbus codec failed");
527 goto free;
528 }
Mahesh Kumar Sharma41a4d382017-01-17 17:00:51 -0800529 ret = bt_register_slimdev(&slim->dev);
Satish kumar sugasibcf528a2017-07-27 13:37:22 -0700530 if (ret < 0) {
531 btfm_slim_unregister_codec(&slim->dev);
532 goto free;
533 }
Mahesh Kumar Sharma41a4d382017-01-17 17:00:51 -0800534 return ret;
Satish kumar sugasibcf528a2017-07-27 13:37:22 -0700535free:
536 slim_remove_device(&btfm_slim->slim_ifd);
Mahesh Kumar Sharma41a4d382017-01-17 17:00:51 -0800537dealloc:
538 mutex_destroy(&btfm_slim->io_lock);
539 mutex_destroy(&btfm_slim->xfer_lock);
540 kfree(btfm_slim);
541 return ret;
542}
543static int btfm_slim_remove(struct slim_device *slim)
544{
545 struct btfmslim *btfm_slim = slim->dev.platform_data;
546
547 BTFMSLIM_DBG("");
548 mutex_destroy(&btfm_slim->io_lock);
549 mutex_destroy(&btfm_slim->xfer_lock);
550 snd_soc_unregister_codec(&slim->dev);
551
552 BTFMSLIM_DBG("slim_remove_device() - btfm_slim->slim_ifd");
553 slim_remove_device(&btfm_slim->slim_ifd);
554
555 kfree(btfm_slim);
556
557 BTFMSLIM_DBG("slim_remove_device() - btfm_slim->slim_pgd");
558 slim_remove_device(slim);
559 return 0;
560}
561
562static const struct slim_device_id btfm_slim_id[] = {
563 {SLIM_SLAVE_COMPATIBLE_STR, 0},
564 {}
565};
566
567static struct slim_driver btfm_slim_driver = {
568 .driver = {
569 .name = "btfmslim-driver",
570 .owner = THIS_MODULE,
571 },
572 .probe = btfm_slim_probe,
573 .remove = btfm_slim_remove,
574 .id_table = btfm_slim_id
575};
576
577static int __init btfm_slim_init(void)
578{
579 int ret;
580
581 BTFMSLIM_DBG("");
582 ret = slim_driver_register(&btfm_slim_driver);
583 if (ret)
584 BTFMSLIM_ERR("Failed to register slimbus driver: %d", ret);
585 return ret;
586}
587
588static void __exit btfm_slim_exit(void)
589{
590 BTFMSLIM_DBG("");
591 slim_driver_unregister(&btfm_slim_driver);
592}
593
594module_init(btfm_slim_init);
595module_exit(btfm_slim_exit);
596
597MODULE_LICENSE("GPL v2");
598MODULE_DESCRIPTION("BTFM Slimbus Slave driver");