blob: 64d7ac758487204a1ff2409332f7f13c9e132082 [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 */
Sungjun Parkea258e42017-10-18 19:08:25 -0700133 prop.prot = SLIM_AUTO_ISO;
134 prop.baser = ((rates == 44100) || (rates == 88200)) ?
135 SLIM_RATE_11025HZ : SLIM_RATE_4000HZ;
136 prop.dataf = ((rates == 48000) || (rates == 44100) ||
137 (rates == 88200) || (rates == 96000)) ?
138 SLIM_CH_DATAF_NOT_DEFINED : SLIM_CH_DATAF_LPCM_AUDIO;
Mahesh Kumar Sharma41a4d382017-01-17 17:00:51 -0800139 prop.auxf = SLIM_CH_AUXF_NOT_APPLICABLE;
Sungjun Parkea258e42017-10-18 19:08:25 -0700140 prop.ratem = ((rates == 44100) || (rates == 88200)) ?
141 (rates/11025) : (rates/4000);
Mahesh Kumar Sharma41a4d382017-01-17 17:00:51 -0800142 prop.sampleszbits = 16;
143
144 ch_h[0] = ch->ch_hdl;
145 ch_h[1] = (grp) ? (ch+1)->ch_hdl : 0;
146
Sungjun Parkea258e42017-10-18 19:08:25 -0700147 BTFMSLIM_INFO("channel define - prot:%d, dataf:%d, auxf:%d",
148 prop.prot, prop.dataf, prop.auxf);
149 BTFMSLIM_INFO("channel define - rates:%d, baser:%d, ratem:%d",
150 rates, prop.baser, prop.ratem);
151
Mahesh Kumar Sharma41a4d382017-01-17 17:00:51 -0800152 ret = slim_define_ch(btfmslim->slim_pgd, &prop, ch_h, nchan, grp,
153 &ch->grph);
154 if (ret < 0) {
155 BTFMSLIM_ERR("slim_define_ch failed ret[%d]", ret);
156 goto error;
157 }
158
159 for (i = 0; i < nchan; i++, ch++) {
160 /* Enable port through registration setting */
161 if (btfmslim->vendor_port_en) {
162 ret = btfmslim->vendor_port_en(btfmslim, ch->port,
163 rxport, 1);
164 if (ret < 0) {
165 BTFMSLIM_ERR("vendor_port_en failed ret[%d]",
166 ret);
167 goto error;
168 }
169 }
170
171 if (rxport) {
172 BTFMSLIM_INFO("slim_connect_sink(port: %d, ch: %d)",
173 ch->port, ch->ch);
174 /* Connect Port with channel given by Machine driver*/
175 ret = slim_connect_sink(btfmslim->slim_pgd,
176 &ch->port_hdl, 1, ch->ch_hdl);
177 if (ret < 0) {
178 BTFMSLIM_ERR("slim_connect_sink failed ret[%d]",
179 ret);
180 goto remove_channel;
181 }
182
183 } else {
184 BTFMSLIM_INFO("slim_connect_src(port: %d, ch: %d)",
185 ch->port, ch->ch);
186 /* Connect Port with channel given by Machine driver*/
187 ret = slim_connect_src(btfmslim->slim_pgd, ch->port_hdl,
188 ch->ch_hdl);
189 if (ret < 0) {
190 BTFMSLIM_ERR("slim_connect_src failed ret[%d]",
191 ret);
192 goto remove_channel;
193 }
194 }
195 }
196
197 /* Activate the channel immediately */
198 BTFMSLIM_INFO(
199 "port: %d, ch: %d, grp: %d, ch->grph: 0x%x, ch_hdl: 0x%x",
200 chan->port, chan->ch, grp, chan->grph, chan->ch_hdl);
201 ret = slim_control_ch(btfmslim->slim_pgd, (grp ? chan->grph :
202 chan->ch_hdl), SLIM_CH_ACTIVATE, true);
203 if (ret < 0) {
204 BTFMSLIM_ERR("slim_control_ch failed ret[%d]", ret);
205 goto remove_channel;
206 }
207
208error:
209 return ret;
210
211remove_channel:
212 /* Remove the channel immediately*/
213 ret = slim_control_ch(btfmslim->slim_pgd, (grp ? ch->grph : ch->ch_hdl),
214 SLIM_CH_REMOVE, true);
215 if (ret < 0)
216 BTFMSLIM_ERR("slim_control_ch failed ret[%d]", ret);
217
218 return ret;
219}
220
221int btfm_slim_disable_ch(struct btfmslim *btfmslim, struct btfmslim_ch *ch,
222 uint8_t rxport, uint8_t grp, uint8_t nchan)
223{
224 int ret, i;
225
226 if (!btfmslim || !ch)
227 return -EINVAL;
228
229 BTFMSLIM_INFO("port:%d, grp: %d, ch->grph:0x%x, ch->ch_hdl:0x%x ",
230 ch->port, grp, ch->grph, ch->ch_hdl);
231 /* Remove the channel immediately*/
232 ret = slim_control_ch(btfmslim->slim_pgd, (grp ? ch->grph : ch->ch_hdl),
233 SLIM_CH_REMOVE, true);
234 if (ret < 0) {
235 BTFMSLIM_ERR("slim_control_ch failed ret[%d]", ret);
236 ret = slim_disconnect_ports(btfmslim->slim_pgd,
237 &ch->port_hdl, 1);
238 if (ret < 0) {
239 BTFMSLIM_ERR("slim_disconnect_ports failed ret[%d]",
240 ret);
241 goto error;
242 }
243 }
244
245 /* Disable port through registration setting */
246 for (i = 0; i < nchan; i++, ch++) {
247 if (btfmslim->vendor_port_en) {
248 ret = btfmslim->vendor_port_en(btfmslim, ch->port,
249 rxport, 0);
250 if (ret < 0) {
251 BTFMSLIM_ERR("vendor_port_en failed ret[%d]",
252 ret);
253 break;
254 }
255 }
256 }
257error:
258 return ret;
259}
260static int btfm_slim_get_logical_addr(struct slim_device *slim)
261{
262 int ret = 0;
263 const unsigned long timeout = jiffies +
264 msecs_to_jiffies(SLIM_SLAVE_PRESENT_TIMEOUT);
265
266 do {
267 ret = slim_get_logical_addr(slim, slim->e_addr,
268 ARRAY_SIZE(slim->e_addr), &slim->laddr);
269 if (!ret) {
270 BTFMSLIM_DBG("Assigned l-addr: 0x%x", slim->laddr);
271 break;
272 }
273 /* Give SLIMBUS time to report present and be ready. */
274 usleep_range(1000, 1100);
275 BTFMSLIM_DBG("retyring get logical addr");
276 } while (time_before(jiffies, timeout));
277
278 return ret;
279}
280
281static int btfm_slim_alloc_port(struct btfmslim *btfmslim)
282{
283 int ret = -EINVAL, i;
284 struct btfmslim_ch *rx_chs;
285 struct btfmslim_ch *tx_chs;
286
287 if (!btfmslim)
288 return ret;
289
290 rx_chs = btfmslim->rx_chs;
291 tx_chs = btfmslim->tx_chs;
292
293 if (!rx_chs || !tx_chs)
294 return ret;
295
296 BTFMSLIM_DBG("Rx: id\tname\tport\thdl\tch\tch_hdl");
297 for (i = 0 ; (rx_chs->port != BTFM_SLIM_PGD_PORT_LAST) &&
298 (i < BTFM_SLIM_NUM_CODEC_DAIS); i++, rx_chs++) {
299
300 /* Get Rx port handler from slimbus driver based
301 * on port number
302 */
303 ret = slim_get_slaveport(btfmslim->slim_pgd->laddr,
304 rx_chs->port, &rx_chs->port_hdl, SLIM_SINK);
305 if (ret < 0) {
306 BTFMSLIM_ERR("slave port failure port#%d - ret[%d]",
307 rx_chs->port, SLIM_SINK);
308 return ret;
309 }
310 BTFMSLIM_DBG(" %d\t%s\t%d\t%x\t%d\t%x", rx_chs->id,
311 rx_chs->name, rx_chs->port, rx_chs->port_hdl,
312 rx_chs->ch, rx_chs->ch_hdl);
313 }
314
315 BTFMSLIM_DBG("Tx: id\tname\tport\thdl\tch\tch_hdl");
316 for (i = 0; (tx_chs->port != BTFM_SLIM_PGD_PORT_LAST) &&
317 (i < BTFM_SLIM_NUM_CODEC_DAIS); i++, tx_chs++) {
318
319 /* Get Tx port handler from slimbus driver based
320 * on port number
321 */
322 ret = slim_get_slaveport(btfmslim->slim_pgd->laddr,
323 tx_chs->port, &tx_chs->port_hdl, SLIM_SRC);
324 if (ret < 0) {
325 BTFMSLIM_ERR("slave port failure port#%d - ret[%d]",
326 tx_chs->port, SLIM_SRC);
327 return ret;
328 }
329 BTFMSLIM_DBG(" %d\t%s\t%d\t%x\t%d\t%x", tx_chs->id,
330 tx_chs->name, tx_chs->port, tx_chs->port_hdl,
331 tx_chs->ch, tx_chs->ch_hdl);
332 }
333 return ret;
334}
335
336int btfm_slim_hw_init(struct btfmslim *btfmslim)
337{
338 int ret;
339
340 BTFMSLIM_DBG("");
341 if (!btfmslim)
342 return -EINVAL;
343
344 if (btfmslim->enabled) {
345 BTFMSLIM_DBG("Already enabled");
346 return 0;
347 }
348 mutex_lock(&btfmslim->io_lock);
349
350 /* Assign Logical Address for PGD (Ported Generic Device)
351 * enumeration address
352 */
353 ret = btfm_slim_get_logical_addr(btfmslim->slim_pgd);
354 if (ret) {
355 BTFMSLIM_ERR("failed to get slimbus %s logical address: %d",
356 btfmslim->slim_pgd->name, ret);
357 goto error;
358 }
359
360 /* Assign Logical Address for Ported Generic Device
361 * enumeration address
362 */
363 ret = btfm_slim_get_logical_addr(&btfmslim->slim_ifd);
364 if (ret) {
365 BTFMSLIM_ERR("failed to get slimbus %s logical address: %d",
366 btfmslim->slim_ifd.name, ret);
367 goto error;
368 }
369
370 /* Allocate ports with logical address to get port handler from
371 * slimbus driver
372 */
373 ret = btfm_slim_alloc_port(btfmslim);
374 if (ret)
375 goto error;
376
377 /* Start vendor specific initialization and get port information */
378 if (btfmslim->vendor_init)
379 ret = btfmslim->vendor_init(btfmslim);
380
381 /* Only when all registers read/write successfully, it set to
382 * enabled status
383 */
384 btfmslim->enabled = 1;
385error:
386 mutex_unlock(&btfmslim->io_lock);
387 return ret;
388}
389
390
391int btfm_slim_hw_deinit(struct btfmslim *btfmslim)
392{
393 int ret = 0;
394
395 if (!btfmslim)
396 return -EINVAL;
397
398 if (!btfmslim->enabled) {
399 BTFMSLIM_DBG("Already disabled");
400 return 0;
401 }
402 mutex_lock(&btfmslim->io_lock);
403 btfmslim->enabled = 0;
404 mutex_unlock(&btfmslim->io_lock);
405 return ret;
406}
407
408static int btfm_slim_get_dt_info(struct btfmslim *btfmslim)
409{
410 int ret = 0;
411 struct slim_device *slim = btfmslim->slim_pgd;
412 struct slim_device *slim_ifd = &btfmslim->slim_ifd;
413 struct property *prop;
414
415 if (!slim || !slim_ifd)
416 return -EINVAL;
417
418 if (slim->dev.of_node) {
419 BTFMSLIM_DBG("Platform data from device tree (%s)",
420 slim->name);
421 ret = of_property_read_string(slim->dev.of_node,
422 "qcom,btfm-slim-ifd", &slim_ifd->name);
423 if (ret) {
424 BTFMSLIM_ERR("Looking up %s property in node %s failed",
425 "qcom,btfm-slim-ifd",
426 slim->dev.of_node->full_name);
427 return -ENODEV;
428 }
429 BTFMSLIM_DBG("qcom,btfm-slim-ifd (%s)", slim_ifd->name);
430
431 prop = of_find_property(slim->dev.of_node,
432 "qcom,btfm-slim-ifd-elemental-addr", NULL);
433 if (!prop) {
434 BTFMSLIM_ERR("Looking up %s property in node %s failed",
435 "qcom,btfm-slim-ifd-elemental-addr",
436 slim->dev.of_node->full_name);
437 return -ENODEV;
438 } else if (prop->length != 6) {
439 BTFMSLIM_ERR(
440 "invalid codec slim ifd addr. addr length= %d",
441 prop->length);
442 return -ENODEV;
443 }
444 memcpy(slim_ifd->e_addr, prop->value, 6);
445 BTFMSLIM_DBG(
446 "PGD Enum Addr: %.02x:%.02x:%.02x:%.02x:%.02x: %.02x",
447 slim->e_addr[0], slim->e_addr[1], slim->e_addr[2],
448 slim->e_addr[3], slim->e_addr[4], slim->e_addr[5]);
449 BTFMSLIM_DBG(
450 "IFD Enum Addr: %.02x:%.02x:%.02x:%.02x:%.02x: %.02x",
451 slim_ifd->e_addr[0], slim_ifd->e_addr[1],
452 slim_ifd->e_addr[2], slim_ifd->e_addr[3],
453 slim_ifd->e_addr[4], slim_ifd->e_addr[5]);
454 } else {
455 BTFMSLIM_ERR("Platform data is not valid");
456 }
457
458 return ret;
459}
460
461static int btfm_slim_probe(struct slim_device *slim)
462{
463 int ret = 0;
464 struct btfmslim *btfm_slim;
465
466 BTFMSLIM_DBG("");
467 if (!slim->ctrl)
468 return -EINVAL;
469
470 /* Allocation btfmslim data pointer */
471 btfm_slim = kzalloc(sizeof(struct btfmslim), GFP_KERNEL);
472 if (btfm_slim == NULL) {
473 BTFMSLIM_ERR("error, allocation failed");
474 return -ENOMEM;
475 }
476 /* BTFM Slimbus driver control data configuration */
477 btfm_slim->slim_pgd = slim;
478
479 /* Assign vendor specific function */
480 btfm_slim->rx_chs = SLIM_SLAVE_RXPORT;
481 btfm_slim->tx_chs = SLIM_SLAVE_TXPORT;
482 btfm_slim->vendor_init = SLIM_SLAVE_INIT;
483 btfm_slim->vendor_port_en = SLIM_SLAVE_PORT_EN;
484
485 /* Created Mutex for slimbus data transfer */
486 mutex_init(&btfm_slim->io_lock);
487 mutex_init(&btfm_slim->xfer_lock);
488
489 /* Get Device tree node for Interface Device enumeration address */
490 ret = btfm_slim_get_dt_info(btfm_slim);
491 if (ret)
492 goto dealloc;
493
494 /* Add Interface Device for slimbus driver */
495 ret = slim_add_device(btfm_slim->slim_pgd->ctrl, &btfm_slim->slim_ifd);
496 if (ret) {
497 BTFMSLIM_ERR("error, adding SLIMBUS device failed");
498 goto dealloc;
499 }
500
501 /* Platform driver data allocation */
502 slim->dev.platform_data = btfm_slim;
503
504 /* Driver specific data allocation */
505 btfm_slim->dev = &slim->dev;
506 ret = btfm_slim_register_codec(&slim->dev);
Satish kumar sugasibcf528a2017-07-27 13:37:22 -0700507 if (ret) {
508 BTFMSLIM_ERR("error, registering slimbus codec failed");
509 goto free;
510 }
Mahesh Kumar Sharma41a4d382017-01-17 17:00:51 -0800511 ret = bt_register_slimdev(&slim->dev);
Satish kumar sugasibcf528a2017-07-27 13:37:22 -0700512 if (ret < 0) {
513 btfm_slim_unregister_codec(&slim->dev);
514 goto free;
515 }
Mahesh Kumar Sharma41a4d382017-01-17 17:00:51 -0800516 return ret;
Satish kumar sugasibcf528a2017-07-27 13:37:22 -0700517free:
518 slim_remove_device(&btfm_slim->slim_ifd);
Mahesh Kumar Sharma41a4d382017-01-17 17:00:51 -0800519dealloc:
520 mutex_destroy(&btfm_slim->io_lock);
521 mutex_destroy(&btfm_slim->xfer_lock);
522 kfree(btfm_slim);
523 return ret;
524}
525static int btfm_slim_remove(struct slim_device *slim)
526{
527 struct btfmslim *btfm_slim = slim->dev.platform_data;
528
529 BTFMSLIM_DBG("");
530 mutex_destroy(&btfm_slim->io_lock);
531 mutex_destroy(&btfm_slim->xfer_lock);
532 snd_soc_unregister_codec(&slim->dev);
533
534 BTFMSLIM_DBG("slim_remove_device() - btfm_slim->slim_ifd");
535 slim_remove_device(&btfm_slim->slim_ifd);
536
537 kfree(btfm_slim);
538
539 BTFMSLIM_DBG("slim_remove_device() - btfm_slim->slim_pgd");
540 slim_remove_device(slim);
541 return 0;
542}
543
544static const struct slim_device_id btfm_slim_id[] = {
545 {SLIM_SLAVE_COMPATIBLE_STR, 0},
546 {}
547};
548
549static struct slim_driver btfm_slim_driver = {
550 .driver = {
551 .name = "btfmslim-driver",
552 .owner = THIS_MODULE,
553 },
554 .probe = btfm_slim_probe,
555 .remove = btfm_slim_remove,
556 .id_table = btfm_slim_id
557};
558
559static int __init btfm_slim_init(void)
560{
561 int ret;
562
563 BTFMSLIM_DBG("");
564 ret = slim_driver_register(&btfm_slim_driver);
565 if (ret)
566 BTFMSLIM_ERR("Failed to register slimbus driver: %d", ret);
567 return ret;
568}
569
570static void __exit btfm_slim_exit(void)
571{
572 BTFMSLIM_DBG("");
573 slim_driver_unregister(&btfm_slim_driver);
574}
575
576module_init(btfm_slim_init);
577module_exit(btfm_slim_exit);
578
579MODULE_LICENSE("GPL v2");
580MODULE_DESCRIPTION("BTFM Slimbus Slave driver");