From ac0278bb4c75547466e8eaef5db4949a914079bc Mon Sep 17 00:00:00 2001 From: himta ram Date: Tue, 29 Oct 2019 15:09:49 +0530 Subject: [PATCH] bluetooth: Add bluetooth slimbus slave drivers This snapshot is taken as of msm-4.19 'commit <670a322c> ("bluetooth: Add bluetooth slimbus slave drivers"). Change-Id: Ide1aef9a42cbee862fbd4afc016a89b37269f95f Signed-off-by: himta ram --- drivers/bluetooth/Kconfig | 36 ++ drivers/bluetooth/Makefile | 5 + drivers/bluetooth/btfm_slim.c | 615 ++++++++++++++++++++++++++++ drivers/bluetooth/btfm_slim.h | 169 ++++++++ drivers/bluetooth/btfm_slim_codec.c | 459 +++++++++++++++++++++ drivers/bluetooth/btfm_slim_slave.c | 189 +++++++++ drivers/bluetooth/btfm_slim_slave.h | 170 ++++++++ 7 files changed, 1643 insertions(+) create mode 100644 drivers/bluetooth/btfm_slim.c create mode 100644 drivers/bluetooth/btfm_slim.h create mode 100644 drivers/bluetooth/btfm_slim_codec.c create mode 100644 drivers/bluetooth/btfm_slim_slave.c create mode 100644 drivers/bluetooth/btfm_slim_slave.h diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig index 343aed9db371..543cade8c3d7 100644 --- a/drivers/bluetooth/Kconfig +++ b/drivers/bluetooth/Kconfig @@ -448,4 +448,40 @@ config MSM_BT_POWER Say Y here to compile support for Bluetooth Power driver into the kernel or say M to compile as a module. +config BTFM_SLIM + tristate "MSM Bluetooth/FM Slimbus Driver" + select SLIMBUS + help + This enables BT/FM slimbus driver to get multiple audio channel. + This will make use of slimbus platform driver and slimbus codec + driver to communicate with slimbus machine driver and LPSS which + is Slimbus master. + + Slimbus slave initialization and configuration will be done through + this driver. + Say Y here to compile support for Bluetooth/FM Slimbus driver + into the kernel or say M to compile as a module. + +config BTFM_SLIM_WCN3990 + tristate "MSM Bluetooth/FM WCN3990 Device" + select BTFM_SLIM + help + This enables specific driver handle for WCN3990 device. + It is designed to adapt any future BT/FM device to implement a specific + chip initialization process and control. + + Say Y here to compile support for Bluetooth slimbus driver + into the kernel or say M to compile as a module. + +config BT_SLIM_QCA6390 + tristate "MSM Bluetooth QCA6390 Device" + select BTFM_SLIM + help + This enables specific driver handle for QCA6390 device. + It is designed to adapt any future BT/FM device to implement a specific + chip initialization process and control. + + Say Y here to compile support for Bluetooth slimbus driver + into the kernel or say M to compile as a module. + endmenu diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile index 12a223aa3ec6..20ae567e41e6 100644 --- a/drivers/bluetooth/Makefile +++ b/drivers/bluetooth/Makefile @@ -33,6 +33,11 @@ obj-$(CONFIG_BT_HCIRSI) += btrsi.o obj-$(CONFIG_MSM_BT_POWER) += btpower.o +obj-$(CONFIG_BTFM_SLIM) += btfm_slim.o +obj-$(CONFIG_BTFM_SLIM) += btfm_slim_codec.o +obj-$(CONFIG_BTFM_SLIM_WCN3990) += btfm_slim_slave.o +obj-$(CONFIG_BT_SLIM_QCA6390) += btfm_slim_slave.o + btmrvl-y := btmrvl_main.o btmrvl-$(CONFIG_DEBUG_FS) += btmrvl_debugfs.o diff --git a/drivers/bluetooth/btfm_slim.c b/drivers/bluetooth/btfm_slim.c new file mode 100644 index 000000000000..a69536dd286d --- /dev/null +++ b/drivers/bluetooth/btfm_slim.c @@ -0,0 +1,615 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "btfm_slim.h" +#include "btfm_slim_slave.h" +#include + +int btfm_slim_write(struct btfmslim *btfmslim, + uint16_t reg, int bytes, void *src, uint8_t pgd) +{ + int ret, i; + struct slim_ele_access msg; + int slim_write_tries = SLIM_SLAVE_RW_MAX_TRIES; + + BTFMSLIM_DBG("Write to %s", pgd?"PGD":"IFD"); + msg.start_offset = SLIM_SLAVE_REG_OFFSET + reg; + msg.num_bytes = bytes; + msg.comp = NULL; + + for ( ; slim_write_tries != 0; slim_write_tries--) { + mutex_lock(&btfmslim->xfer_lock); + ret = slim_change_val_element(pgd ? btfmslim->slim_pgd : + &btfmslim->slim_ifd, &msg, src, bytes); + mutex_unlock(&btfmslim->xfer_lock); + if (ret == 0) + break; + usleep_range(5000, 5100); + } + + if (ret) { + BTFMSLIM_ERR("failed (%d)", ret); + return ret; + } + + for (i = 0; i < bytes; i++) + BTFMSLIM_DBG("Write 0x%02x to reg 0x%x", ((uint8_t *)src)[i], + reg + i); + return 0; +} + +int btfm_slim_write_pgd(struct btfmslim *btfmslim, + uint16_t reg, int bytes, void *src) +{ + return btfm_slim_write(btfmslim, reg, bytes, src, PGD); +} + +int btfm_slim_write_inf(struct btfmslim *btfmslim, + uint16_t reg, int bytes, void *src) +{ + return btfm_slim_write(btfmslim, reg, bytes, src, IFD); +} + +int btfm_slim_read(struct btfmslim *btfmslim, unsigned short reg, + int bytes, void *dest, uint8_t pgd) +{ + int ret, i; + struct slim_ele_access msg; + int slim_read_tries = SLIM_SLAVE_RW_MAX_TRIES; + + BTFMSLIM_DBG("Read from %s", pgd?"PGD":"IFD"); + msg.start_offset = SLIM_SLAVE_REG_OFFSET + reg; + msg.num_bytes = bytes; + msg.comp = NULL; + + for ( ; slim_read_tries != 0; slim_read_tries--) { + mutex_lock(&btfmslim->xfer_lock); + ret = slim_request_val_element(pgd ? btfmslim->slim_pgd : + &btfmslim->slim_ifd, &msg, dest, bytes); + mutex_unlock(&btfmslim->xfer_lock); + if (ret == 0) + break; + usleep_range(5000, 5100); + } + + if (ret) + BTFMSLIM_ERR("failed (%d)", ret); + + for (i = 0; i < bytes; i++) + BTFMSLIM_DBG("Read 0x%02x from reg 0x%x", ((uint8_t *)dest)[i], + reg + i); + + return 0; +} + +int btfm_slim_read_pgd(struct btfmslim *btfmslim, + uint16_t reg, int bytes, void *dest) +{ + return btfm_slim_read(btfmslim, reg, bytes, dest, PGD); +} + +int btfm_slim_read_inf(struct btfmslim *btfmslim, + uint16_t reg, int bytes, void *dest) +{ + return btfm_slim_read(btfmslim, reg, bytes, dest, IFD); +} + +int btfm_slim_enable_ch(struct btfmslim *btfmslim, struct btfmslim_ch *ch, + uint8_t rxport, uint32_t rates, uint8_t grp, uint8_t nchan) +{ + int ret, i; + struct slim_ch prop; + struct btfmslim_ch *chan = ch; + uint16_t ch_h[2]; + + if (!btfmslim || !ch) + return -EINVAL; + + BTFMSLIM_DBG("port: %d ch: %d", ch->port, ch->ch); + + /* Define the channel with below parameters */ + prop.prot = ((rates == 44100) || (rates == 88200)) ? + SLIM_PUSH : SLIM_AUTO_ISO; + prop.baser = ((rates == 44100) || (rates == 88200)) ? + SLIM_RATE_11025HZ : SLIM_RATE_4000HZ; + prop.dataf = ((rates == 48000) || (rates == 44100) || + (rates == 88200) || (rates == 96000)) ? + SLIM_CH_DATAF_NOT_DEFINED : SLIM_CH_DATAF_LPCM_AUDIO; + + /* for feedback channel, PCM bit should not be set */ + if (btfm_feedback_ch_setting) { + BTFMSLIM_DBG("port open for feedback ch, not setting PCM bit"); + prop.dataf = SLIM_CH_DATAF_NOT_DEFINED; + /* reset so that next port open sets the data format properly */ + btfm_feedback_ch_setting = 0; + } + prop.auxf = SLIM_CH_AUXF_NOT_APPLICABLE; + prop.ratem = ((rates == 44100) || (rates == 88200)) ? + (rates/11025) : (rates/4000); + prop.sampleszbits = 16; + + ch_h[0] = ch->ch_hdl; + ch_h[1] = (grp) ? (ch+1)->ch_hdl : 0; + + BTFMSLIM_INFO("channel define - prot:%d, dataf:%d, auxf:%d", + prop.prot, prop.dataf, prop.auxf); + BTFMSLIM_INFO("channel define - rates:%d, baser:%d, ratem:%d", + rates, prop.baser, prop.ratem); + + ret = slim_define_ch(btfmslim->slim_pgd, &prop, ch_h, nchan, grp, + &ch->grph); + if (ret < 0) { + BTFMSLIM_ERR("slim_define_ch failed ret[%d]", ret); + goto error; + } + + for (i = 0; i < nchan; i++, ch++) { + /* Enable port through registration setting */ + if (btfmslim->vendor_port_en) { + ret = btfmslim->vendor_port_en(btfmslim, ch->port, + rxport, 1); + if (ret < 0) { + BTFMSLIM_ERR("vendor_port_en failed ret[%d]", + ret); + goto error; + } + } + + if (rxport) { + BTFMSLIM_INFO("slim_connect_sink(port: %d, ch: %d)", + ch->port, ch->ch); + /* Connect Port with channel given by Machine driver*/ + ret = slim_connect_sink(btfmslim->slim_pgd, + &ch->port_hdl, 1, ch->ch_hdl); + if (ret < 0) { + BTFMSLIM_ERR("slim_connect_sink failed ret[%d]", + ret); + goto remove_channel; + } + + } else { + BTFMSLIM_INFO("slim_connect_src(port: %d, ch: %d)", + ch->port, ch->ch); + /* Connect Port with channel given by Machine driver*/ + ret = slim_connect_src(btfmslim->slim_pgd, ch->port_hdl, + ch->ch_hdl); + if (ret < 0) { + BTFMSLIM_ERR("slim_connect_src failed ret[%d]", + ret); + goto remove_channel; + } + } + } + + /* Activate the channel immediately */ + BTFMSLIM_INFO( + "port: %d, ch: %d, grp: %d, ch->grph: 0x%x, ch_hdl: 0x%x", + chan->port, chan->ch, grp, chan->grph, chan->ch_hdl); + ret = slim_control_ch(btfmslim->slim_pgd, (grp ? chan->grph : + chan->ch_hdl), SLIM_CH_ACTIVATE, true); + if (ret < 0) { + BTFMSLIM_ERR("slim_control_ch failed ret[%d]", ret); + goto remove_channel; + } + +error: + return ret; + +remove_channel: + /* Remove the channel immediately*/ + ret = slim_control_ch(btfmslim->slim_pgd, (grp ? ch->grph : ch->ch_hdl), + SLIM_CH_REMOVE, true); + if (ret < 0) + BTFMSLIM_ERR("slim_control_ch failed ret[%d]", ret); + + return ret; +} + +int btfm_slim_disable_ch(struct btfmslim *btfmslim, struct btfmslim_ch *ch, + uint8_t rxport, uint8_t grp, uint8_t nchan) +{ + int ret, i; + + if (!btfmslim || !ch) + return -EINVAL; + + BTFMSLIM_INFO("port:%d, grp: %d, ch->grph:0x%x, ch->ch_hdl:0x%x ", + ch->port, grp, ch->grph, ch->ch_hdl); + + /* For 44.1/88.2 Khz A2DP Rx, disconnect the port first */ + if (rxport && + (btfmslim->sample_rate == 44100 || + btfmslim->sample_rate == 88200)) { + BTFMSLIM_DBG("disconnecting the ports, removing the channel"); + ret = slim_disconnect_ports(btfmslim->slim_pgd, + &ch->port_hdl, 1); + if (ret < 0) { + BTFMSLIM_ERR("slim_disconnect_ports failed ret[%d]", + ret); + } + } + + /* Remove the channel immediately*/ + ret = slim_control_ch(btfmslim->slim_pgd, (grp ? ch->grph : ch->ch_hdl), + SLIM_CH_REMOVE, true); + if (ret < 0) { + BTFMSLIM_ERR("slim_control_ch failed ret[%d]", ret); + if (btfmslim->sample_rate != 44100 && + btfmslim->sample_rate != 88200) { + ret = slim_disconnect_ports(btfmslim->slim_pgd, + &ch->port_hdl, 1); + if (ret < 0) { + BTFMSLIM_ERR("disconnect_ports failed ret[%d]", + ret); + goto error; + } + } + } + + /* Disable port through registration setting */ + for (i = 0; i < nchan; i++, ch++) { + if (btfmslim->vendor_port_en) { + ret = btfmslim->vendor_port_en(btfmslim, ch->port, + rxport, 0); + if (ret < 0) { + BTFMSLIM_ERR("vendor_port_en failed ret[%d]", + ret); + break; + } + } + } +error: + return ret; +} +static int btfm_slim_get_logical_addr(struct slim_device *slim) +{ + int ret = 0; + const unsigned long timeout = jiffies + + msecs_to_jiffies(SLIM_SLAVE_PRESENT_TIMEOUT); + + do { + ret = slim_get_logical_addr(slim, slim->e_addr, + ARRAY_SIZE(slim->e_addr), &slim->laddr); + if (!ret) { + BTFMSLIM_DBG("Assigned l-addr: 0x%x", slim->laddr); + break; + } + /* Give SLIMBUS time to report present and be ready. */ + usleep_range(1000, 1100); + BTFMSLIM_DBG("retyring get logical addr"); + } while (time_before(jiffies, timeout)); + + return ret; +} + +static int btfm_slim_alloc_port(struct btfmslim *btfmslim) +{ + int ret = -EINVAL, i; + int chipset_ver; + struct btfmslim_ch *rx_chs; + struct btfmslim_ch *tx_chs; + + if (!btfmslim) + return ret; + + chipset_ver = get_chipset_version(); + BTFMSLIM_INFO("chipset soc version:%x", chipset_ver); + + rx_chs = btfmslim->rx_chs; + tx_chs = btfmslim->tx_chs; + if ((chipset_ver >= QCA_CHEROKEE_SOC_ID_0300) && + (chipset_ver <= QCA_CHEROKEE_SOC_ID_0320)) { + for (i = 0; (tx_chs->port != BTFM_SLIM_PGD_PORT_LAST) && + (i < BTFM_SLIM_NUM_CODEC_DAIS); i++, tx_chs++) { + if (tx_chs->port == SLAVE_SB_PGD_PORT_TX1_FM) + tx_chs->port = CHRKVER3_SB_PGD_PORT_TX1_FM; + else if (tx_chs->port == SLAVE_SB_PGD_PORT_TX2_FM) + tx_chs->port = CHRKVER3_SB_PGD_PORT_TX2_FM; + BTFMSLIM_INFO("Tx port:%d", tx_chs->port); + } + tx_chs = btfmslim->tx_chs; + } + if (!rx_chs || !tx_chs) + return ret; + + BTFMSLIM_DBG("Rx: id\tname\tport\thdl\tch\tch_hdl"); + for (i = 0 ; (rx_chs->port != BTFM_SLIM_PGD_PORT_LAST) && + (i < BTFM_SLIM_NUM_CODEC_DAIS); i++, rx_chs++) { + + /* Get Rx port handler from slimbus driver based + * on port number + */ + ret = slim_get_slaveport(btfmslim->slim_pgd->laddr, + rx_chs->port, &rx_chs->port_hdl, SLIM_SINK); + if (ret < 0) { + BTFMSLIM_ERR("slave port failure port#%d - ret[%d]", + rx_chs->port, SLIM_SINK); + return ret; + } + BTFMSLIM_DBG(" %d\t%s\t%d\t%x\t%d\t%x", rx_chs->id, + rx_chs->name, rx_chs->port, rx_chs->port_hdl, + rx_chs->ch, rx_chs->ch_hdl); + } + + BTFMSLIM_DBG("Tx: id\tname\tport\thdl\tch\tch_hdl"); + for (i = 0; (tx_chs->port != BTFM_SLIM_PGD_PORT_LAST) && + (i < BTFM_SLIM_NUM_CODEC_DAIS); i++, tx_chs++) { + + /* Get Tx port handler from slimbus driver based + * on port number + */ + ret = slim_get_slaveport(btfmslim->slim_pgd->laddr, + tx_chs->port, &tx_chs->port_hdl, SLIM_SRC); + if (ret < 0) { + BTFMSLIM_ERR("slave port failure port#%d - ret[%d]", + tx_chs->port, SLIM_SRC); + return ret; + } + BTFMSLIM_DBG(" %d\t%s\t%d\t%x\t%d\t%x", tx_chs->id, + tx_chs->name, tx_chs->port, tx_chs->port_hdl, + tx_chs->ch, tx_chs->ch_hdl); + } + return ret; +} + +int btfm_slim_hw_init(struct btfmslim *btfmslim) +{ + int ret; + + BTFMSLIM_DBG(""); + if (!btfmslim) + return -EINVAL; + + if (btfmslim->enabled) { + BTFMSLIM_DBG("Already enabled"); + return 0; + } + mutex_lock(&btfmslim->io_lock); + + /* Assign Logical Address for PGD (Ported Generic Device) + * enumeration address + */ + ret = btfm_slim_get_logical_addr(btfmslim->slim_pgd); + if (ret) { + BTFMSLIM_ERR("failed to get slimbus %s logical address: %d", + btfmslim->slim_pgd->name, ret); + goto error; + } + + /* Assign Logical Address for Ported Generic Device + * enumeration address + */ + ret = btfm_slim_get_logical_addr(&btfmslim->slim_ifd); + if (ret) { + BTFMSLIM_ERR("failed to get slimbus %s logical address: %d", + btfmslim->slim_ifd.name, ret); + goto error; + } + + /* Allocate ports with logical address to get port handler from + * slimbus driver + */ + ret = btfm_slim_alloc_port(btfmslim); + if (ret) + goto error; + + /* Start vendor specific initialization and get port information */ + if (btfmslim->vendor_init) + ret = btfmslim->vendor_init(btfmslim); + + /* Only when all registers read/write successfully, it set to + * enabled status + */ + btfmslim->enabled = 1; +error: + mutex_unlock(&btfmslim->io_lock); + return ret; +} + + +int btfm_slim_hw_deinit(struct btfmslim *btfmslim) +{ + int ret = 0; + + if (!btfmslim) + return -EINVAL; + + if (!btfmslim->enabled) { + BTFMSLIM_DBG("Already disabled"); + return 0; + } + mutex_lock(&btfmslim->io_lock); + btfmslim->enabled = 0; + mutex_unlock(&btfmslim->io_lock); + return ret; +} + +static int btfm_slim_get_dt_info(struct btfmslim *btfmslim) +{ + int ret = 0; + struct slim_device *slim = btfmslim->slim_pgd; + struct slim_device *slim_ifd = &btfmslim->slim_ifd; + struct property *prop; + + if (!slim || !slim_ifd) + return -EINVAL; + + if (slim->dev.of_node) { + BTFMSLIM_DBG("Platform data from device tree (%s)", + slim->name); + ret = of_property_read_string(slim->dev.of_node, + "qcom,btfm-slim-ifd", &slim_ifd->name); + if (ret) { + BTFMSLIM_ERR("Looking up %s property in node %s failed", + "qcom,btfm-slim-ifd", + slim->dev.of_node->full_name); + return -ENODEV; + } + BTFMSLIM_DBG("qcom,btfm-slim-ifd (%s)", slim_ifd->name); + + prop = of_find_property(slim->dev.of_node, + "qcom,btfm-slim-ifd-elemental-addr", NULL); + if (!prop) { + BTFMSLIM_ERR("Looking up %s property in node %s failed", + "qcom,btfm-slim-ifd-elemental-addr", + slim->dev.of_node->full_name); + return -ENODEV; + } else if (prop->length != 6) { + BTFMSLIM_ERR( + "invalid codec slim ifd addr. addr length= %d", + prop->length); + return -ENODEV; + } + memcpy(slim_ifd->e_addr, prop->value, 6); + BTFMSLIM_DBG( + "PGD Enum Addr: %.02x:%.02x:%.02x:%.02x:%.02x: %.02x", + slim->e_addr[0], slim->e_addr[1], slim->e_addr[2], + slim->e_addr[3], slim->e_addr[4], slim->e_addr[5]); + BTFMSLIM_DBG( + "IFD Enum Addr: %.02x:%.02x:%.02x:%.02x:%.02x: %.02x", + slim_ifd->e_addr[0], slim_ifd->e_addr[1], + slim_ifd->e_addr[2], slim_ifd->e_addr[3], + slim_ifd->e_addr[4], slim_ifd->e_addr[5]); + } else { + BTFMSLIM_ERR("Platform data is not valid"); + } + + return ret; +} + +static int btfm_slim_probe(struct slim_device *slim) +{ + int ret = 0; + struct btfmslim *btfm_slim; + + BTFMSLIM_DBG(""); + if (!slim->ctrl) + return -EINVAL; + + /* Allocation btfmslim data pointer */ + btfm_slim = kzalloc(sizeof(struct btfmslim), GFP_KERNEL); + if (btfm_slim == NULL) { + BTFMSLIM_ERR("error, allocation failed"); + return -ENOMEM; + } + /* BTFM Slimbus driver control data configuration */ + btfm_slim->slim_pgd = slim; + + /* Assign vendor specific function */ + btfm_slim->rx_chs = SLIM_SLAVE_RXPORT; + btfm_slim->tx_chs = SLIM_SLAVE_TXPORT; + btfm_slim->vendor_init = SLIM_SLAVE_INIT; + btfm_slim->vendor_port_en = SLIM_SLAVE_PORT_EN; + + /* Created Mutex for slimbus data transfer */ + mutex_init(&btfm_slim->io_lock); + mutex_init(&btfm_slim->xfer_lock); + + /* Get Device tree node for Interface Device enumeration address */ + ret = btfm_slim_get_dt_info(btfm_slim); + if (ret) + goto dealloc; + + /* Add Interface Device for slimbus driver */ + ret = slim_add_device(btfm_slim->slim_pgd->ctrl, &btfm_slim->slim_ifd); + if (ret) { + BTFMSLIM_ERR("error, adding SLIMBUS device failed"); + goto dealloc; + } + + /* Platform driver data allocation */ + slim->dev.platform_data = btfm_slim; + + /* Driver specific data allocation */ + btfm_slim->dev = &slim->dev; + ret = btfm_slim_register_codec(&slim->dev); + if (ret) { + BTFMSLIM_ERR("error, registering slimbus codec failed"); + goto free; + } + ret = bt_register_slimdev(&slim->dev); + if (ret < 0) { + btfm_slim_unregister_codec(&slim->dev); + goto free; + } + return ret; +free: + slim_remove_device(&btfm_slim->slim_ifd); +dealloc: + mutex_destroy(&btfm_slim->io_lock); + mutex_destroy(&btfm_slim->xfer_lock); + kfree(btfm_slim); + return ret; +} +static int btfm_slim_remove(struct slim_device *slim) +{ + struct btfmslim *btfm_slim = slim->dev.platform_data; + + BTFMSLIM_DBG(""); + mutex_destroy(&btfm_slim->io_lock); + mutex_destroy(&btfm_slim->xfer_lock); + snd_soc_unregister_component(&slim->dev); + + BTFMSLIM_DBG("slim_remove_device() - btfm_slim->slim_ifd"); + slim_remove_device(&btfm_slim->slim_ifd); + + kfree(btfm_slim); + + BTFMSLIM_DBG("slim_remove_device() - btfm_slim->slim_pgd"); + slim_remove_device(slim); + return 0; +} + +static const struct slim_device_id btfm_slim_id[] = { + {SLIM_SLAVE_COMPATIBLE_STR, 0}, + {} +}; + +static struct slim_driver btfm_slim_driver = { + .driver = { + .name = "btfmslim-driver", + .owner = THIS_MODULE, + }, + .probe = btfm_slim_probe, + .remove = btfm_slim_remove, + .id_table = btfm_slim_id +}; + +static int __init btfm_slim_init(void) +{ + int ret; + + BTFMSLIM_DBG(""); + ret = slim_driver_register(&btfm_slim_driver); + if (ret) + BTFMSLIM_ERR("Failed to register slimbus driver: %d", ret); + return ret; +} + +static void __exit btfm_slim_exit(void) +{ + BTFMSLIM_DBG(""); + slim_driver_unregister(&btfm_slim_driver); +} + +module_init(btfm_slim_init); +module_exit(btfm_slim_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("BTFM Slimbus Slave driver"); diff --git a/drivers/bluetooth/btfm_slim.h b/drivers/bluetooth/btfm_slim.h new file mode 100644 index 000000000000..9a7e2199e402 --- /dev/null +++ b/drivers/bluetooth/btfm_slim.h @@ -0,0 +1,169 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved. + */ + +#ifndef BTFM_SLIM_H +#define BTFM_SLIM_H +#include + +#define BTFMSLIM_DBG(fmt, arg...) pr_debug("%s: " fmt "\n", __func__, ## arg) +#define BTFMSLIM_INFO(fmt, arg...) pr_info("%s: " fmt "\n", __func__, ## arg) +#define BTFMSLIM_ERR(fmt, arg...) pr_err("%s: " fmt "\n", __func__, ## arg) + +/* Vendor specific defines + * This should redefines in slimbus slave specific header + */ +#define SLIM_SLAVE_COMPATIBLE_STR "btfmslim_slave" +#define SLIM_SLAVE_REG_OFFSET 0x0000 +#define SLIM_SLAVE_RXPORT NULL +#define SLIM_SLAVE_TXPORT NULL +#define SLIM_SLAVE_INIT NULL +#define SLIM_SLAVE_PORT_EN NULL + +/* Misc defines */ +#define SLIM_SLAVE_RW_MAX_TRIES 3 +#define SLIM_SLAVE_PRESENT_TIMEOUT 100 + +#define PGD 1 +#define IFD 0 + + +/* Codec driver defines */ +enum { + BTFM_FM_SLIM_TX = 0, + BTFM_BT_SCO_SLIM_TX, + BTFM_BT_SCO_A2DP_SLIM_RX, + BTFM_BT_SPLIT_A2DP_SLIM_RX, + BTFM_SLIM_NUM_CODEC_DAIS +}; + +/* Slimbus Port defines - This should be redefined in specific device file */ +#define BTFM_SLIM_PGD_PORT_LAST 0xFF + +struct btfmslim_ch { + int id; + char *name; + uint32_t port_hdl; /* slimbus port handler */ + uint16_t port; /* slimbus port number */ + + uint8_t ch; /* slimbus channel number */ + uint16_t ch_hdl; /* slimbus channel handler */ + uint16_t grph; /* slimbus group channel handler */ +}; + +struct btfmslim { + struct device *dev; + struct slim_device *slim_pgd; + struct slim_device slim_ifd; + struct mutex io_lock; + struct mutex xfer_lock; + uint8_t enabled; + + uint32_t num_rx_port; + uint32_t num_tx_port; + uint32_t sample_rate; + + struct btfmslim_ch *rx_chs; + struct btfmslim_ch *tx_chs; + + int (*vendor_init)(struct btfmslim *btfmslim); + int (*vendor_port_en)(struct btfmslim *btfmslim, uint8_t port_num, + uint8_t rxport, uint8_t enable); +}; + +extern int btfm_feedback_ch_setting; + +/** + * btfm_slim_hw_init: Initialize slimbus slave device + * Returns: + * 0: Success + * else: Fail + */ +int btfm_slim_hw_init(struct btfmslim *btfmslim); + +/** + * btfm_slim_hw_deinit: Deinitialize slimbus slave device + * Returns: + * 0: Success + * else: Fail + */ +int btfm_slim_hw_deinit(struct btfmslim *btfmslim); + +/** + * btfm_slim_write: write value to pgd or ifd device + * @btfmslim: slimbus slave device data pointer. + * @reg: slimbus slave register address + * @bytes: length of data + * @src: data pointer to write + * @pgd: selection for device: either PGD or IFD + * Returns: + * -EINVAL + * -ETIMEDOUT + * -ENOMEM + */ +int btfm_slim_write(struct btfmslim *btfmslim, + uint16_t reg, int bytes, void *src, uint8_t pgd); + + + +/** + * btfm_slim_read: read value from pgd or ifd device + * @btfmslim: slimbus slave device data pointer. + * @reg: slimbus slave register address + * @bytes: length of data + * @dest: data pointer to read + * @pgd: selection for device: either PGD or IFD + * Returns: + * -EINVAL + * -ETIMEDOUT + * -ENOMEM + */ +int btfm_slim_read(struct btfmslim *btfmslim, + uint16_t reg, int bytes, void *dest, uint8_t pgd); + + +/** + * btfm_slim_enable_ch: enable channel for slimbus slave port + * @btfmslim: slimbus slave device data pointer. + * @ch: slimbus slave channel pointer + * @rxport: rxport or txport + * Returns: + * -EINVAL + * -ETIMEDOUT + * -ENOMEM + */ +int btfm_slim_enable_ch(struct btfmslim *btfmslim, + struct btfmslim_ch *ch, uint8_t rxport, uint32_t rates, + uint8_t grp, uint8_t nchan); + +/** + * btfm_slim_disable_ch: disable channel for slimbus slave port + * @btfmslim: slimbus slave device data pointer. + * @ch: slimbus slave channel pointer + * @rxport: rxport or txport + * Returns: + * -EINVAL + * -ETIMEDOUT + * -ENOMEM + */ +int btfm_slim_disable_ch(struct btfmslim *btfmslim, + struct btfmslim_ch *ch, uint8_t rxport, uint8_t grp, uint8_t nchan); + +/** + * btfm_slim_register_codec: Register codec driver in slimbus device node + * @dev: device node + * Returns: + * -ENOMEM + * 0 + */ +int btfm_slim_register_codec(struct device *dev); + +/** + * btfm_slim_unregister_codec: Unregister codec driver in slimbus device node + * @dev: device node + * Returns: + * VOID + */ +void btfm_slim_unregister_codec(struct device *dev); +#endif /* BTFM_SLIM_H */ diff --git a/drivers/bluetooth/btfm_slim_codec.c b/drivers/bluetooth/btfm_slim_codec.c new file mode 100644 index 000000000000..4f4cbe738e50 --- /dev/null +++ b/drivers/bluetooth/btfm_slim_codec.c @@ -0,0 +1,459 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "btfm_slim.h" + +static int bt_soc_enable_status; +int btfm_feedback_ch_setting; + +static int btfm_slim_codec_write(struct snd_soc_component *codec, + unsigned int reg, unsigned int value) +{ + return 0; +} + +static unsigned int btfm_slim_codec_read(struct snd_soc_component *codec, + unsigned int reg) +{ + return 0; +} + +static int bt_soc_status_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = bt_soc_enable_status; + return 1; +} + +static int bt_soc_status_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return 1; +} + +static int btfm_get_feedback_ch_setting(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = btfm_feedback_ch_setting; + return 1; +} + +static int btfm_put_feedback_ch_setting(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + btfm_feedback_ch_setting = ucontrol->value.integer.value[0]; + return 1; +} + +static const struct snd_kcontrol_new status_controls[] = { + SOC_SINGLE_EXT("BT SOC status", 0, 0, 1, 0, + bt_soc_status_get, + bt_soc_status_put), + SOC_SINGLE_EXT("BT set feedback channel", 0, 0, 1, 0, + btfm_get_feedback_ch_setting, + btfm_put_feedback_ch_setting) +}; + + +static int btfm_slim_codec_probe(struct snd_soc_component *codec) +{ + snd_soc_add_component_controls(codec, status_controls, + ARRAY_SIZE(status_controls)); + return 0; +} + +static void btfm_slim_codec_remove(struct snd_soc_component *codec) +{ + +} + +static int btfm_slim_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + int ret; + struct btfmslim *btfmslim = dai->dev->platform_data; + + BTFMSLIM_DBG("substream = %s stream = %d dai->name = %s", + substream->name, substream->stream, dai->name); + ret = btfm_slim_hw_init(btfmslim); + return ret; +} + +static void btfm_slim_dai_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + int i; + struct btfmslim *btfmslim = dai->dev->platform_data; + struct btfmslim_ch *ch; + uint8_t rxport, grp = false, nchan = 1; + + BTFMSLIM_DBG("dai->name: %s, dai->id: %d, dai->rate: %d", dai->name, + dai->id, dai->rate); + + switch (dai->id) { + case BTFM_FM_SLIM_TX: + grp = true; nchan = 2; + ch = btfmslim->tx_chs; + rxport = 0; + break; + case BTFM_BT_SCO_SLIM_TX: + ch = btfmslim->tx_chs; + rxport = 0; + break; + case BTFM_BT_SCO_A2DP_SLIM_RX: + case BTFM_BT_SPLIT_A2DP_SLIM_RX: + ch = btfmslim->rx_chs; + rxport = 1; + break; + case BTFM_SLIM_NUM_CODEC_DAIS: + default: + BTFMSLIM_ERR("dai->id is invalid:%d", dai->id); + return; + } + + /* Search for dai->id matched port handler */ + for (i = 0; (i < BTFM_SLIM_NUM_CODEC_DAIS) && + (ch->id != BTFM_SLIM_NUM_CODEC_DAIS) && + (ch->id != dai->id); ch++, i++) + ; + + if ((ch->port == BTFM_SLIM_PGD_PORT_LAST) || + (ch->id == BTFM_SLIM_NUM_CODEC_DAIS)) { + BTFMSLIM_ERR("ch is invalid!!"); + return; + } + + btfm_slim_disable_ch(btfmslim, ch, rxport, grp, nchan); + btfm_slim_hw_deinit(btfmslim); +} + +static int btfm_slim_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + BTFMSLIM_DBG("dai->name = %s DAI-ID %x rate %d num_ch %d", + dai->name, dai->id, params_rate(params), + params_channels(params)); + + return 0; +} + +static int btfm_slim_dai_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + int i, ret = -EINVAL; + struct btfmslim *btfmslim = dai->dev->platform_data; + struct btfmslim_ch *ch; + uint8_t rxport, grp = false, nchan = 1; + + bt_soc_enable_status = 0; + + BTFMSLIM_DBG("dai->name: %s, dai->id: %d, dai->rate: %d", dai->name, + dai->id, dai->rate); + + /* save sample rate */ + btfmslim->sample_rate = dai->rate; + + switch (dai->id) { + case BTFM_FM_SLIM_TX: + grp = true; nchan = 2; + ch = btfmslim->tx_chs; + rxport = 0; + break; + case BTFM_BT_SCO_SLIM_TX: + ch = btfmslim->tx_chs; + rxport = 0; + break; + case BTFM_BT_SCO_A2DP_SLIM_RX: + case BTFM_BT_SPLIT_A2DP_SLIM_RX: + ch = btfmslim->rx_chs; + rxport = 1; + break; + case BTFM_SLIM_NUM_CODEC_DAIS: + default: + BTFMSLIM_ERR("dai->id is invalid:%d", dai->id); + return ret; + } + + /* Search for dai->id matched port handler */ + for (i = 0; (i < BTFM_SLIM_NUM_CODEC_DAIS) && + (ch->id != BTFM_SLIM_NUM_CODEC_DAIS) && + (ch->id != dai->id); ch++, i++) + ; + + if ((ch->port == BTFM_SLIM_PGD_PORT_LAST) || + (ch->id == BTFM_SLIM_NUM_CODEC_DAIS)) { + BTFMSLIM_ERR("ch is invalid!!"); + return ret; + } + + ret = btfm_slim_enable_ch(btfmslim, ch, rxport, dai->rate, grp, nchan); + + /* save the enable channel status */ + if (ret == 0) + bt_soc_enable_status = 1; + return ret; +} + +/* This function will be called once during boot up */ +static int btfm_slim_dai_set_channel_map(struct snd_soc_dai *dai, + unsigned int tx_num, unsigned int *tx_slot, + unsigned int rx_num, unsigned int *rx_slot) +{ + int ret = -EINVAL, i; + struct btfmslim *btfmslim = dai->dev->platform_data; + struct btfmslim_ch *rx_chs; + struct btfmslim_ch *tx_chs; + + BTFMSLIM_DBG(""); + + if (!btfmslim) + return ret; + + rx_chs = btfmslim->rx_chs; + tx_chs = btfmslim->tx_chs; + + if (!rx_chs || !tx_chs) + return ret; + + BTFMSLIM_DBG("Rx: id\tname\tport\thdl\tch\tch_hdl"); + for (i = 0; (rx_chs->port != BTFM_SLIM_PGD_PORT_LAST) && (i < rx_num); + i++, rx_chs++) { + /* Set Rx Channel number from machine driver and + * get channel handler from slimbus driver + */ + rx_chs->ch = *(uint8_t *)(rx_slot + i); + ret = slim_query_ch(btfmslim->slim_pgd, rx_chs->ch, + &rx_chs->ch_hdl); + if (ret < 0) { + BTFMSLIM_ERR("slim_query_ch failure ch#%d - ret[%d]", + rx_chs->ch, ret); + goto error; + } + BTFMSLIM_DBG(" %d\t%s\t%d\t%x\t%d\t%x", rx_chs->id, + rx_chs->name, rx_chs->port, rx_chs->port_hdl, + rx_chs->ch, rx_chs->ch_hdl); + } + + BTFMSLIM_DBG("Tx: id\tname\tport\thdl\tch\tch_hdl"); + for (i = 0; (tx_chs->port != BTFM_SLIM_PGD_PORT_LAST) && (i < tx_num); + i++, tx_chs++) { + /* Set Tx Channel number from machine driver and + * get channel handler from slimbus driver + */ + tx_chs->ch = *(uint8_t *)(tx_slot + i); + ret = slim_query_ch(btfmslim->slim_pgd, tx_chs->ch, + &tx_chs->ch_hdl); + if (ret < 0) { + BTFMSLIM_ERR("slim_query_ch failure ch#%d - ret[%d]", + tx_chs->ch, ret); + goto error; + } + BTFMSLIM_DBG(" %d\t%s\t%d\t%x\t%d\t%x", tx_chs->id, + tx_chs->name, tx_chs->port, tx_chs->port_hdl, + tx_chs->ch, tx_chs->ch_hdl); + } + +error: + return ret; +} + +static int btfm_slim_dai_get_channel_map(struct snd_soc_dai *dai, + unsigned int *tx_num, unsigned int *tx_slot, + unsigned int *rx_num, unsigned int *rx_slot) +{ + int i, ret = -EINVAL, *slot = NULL, j = 0, num = 1; + struct btfmslim *btfmslim = dai->dev->platform_data; + struct btfmslim_ch *ch = NULL; + + if (!btfmslim) + return ret; + + switch (dai->id) { + case BTFM_FM_SLIM_TX: + num = 2; + case BTFM_BT_SCO_SLIM_TX: + if (!tx_slot || !tx_num) { + BTFMSLIM_ERR("Invalid tx_slot %p or tx_num %p", + tx_slot, tx_num); + return -EINVAL; + } + ch = btfmslim->tx_chs; + if (!ch) + return -EINVAL; + slot = tx_slot; + *rx_slot = 0; + *tx_num = num; + *rx_num = 0; + break; + case BTFM_BT_SCO_A2DP_SLIM_RX: + case BTFM_BT_SPLIT_A2DP_SLIM_RX: + if (!rx_slot || !rx_num) { + BTFMSLIM_ERR("Invalid rx_slot %p or rx_num %p", + rx_slot, rx_num); + return -EINVAL; + } + ch = btfmslim->rx_chs; + if (!ch) + return -EINVAL; + slot = rx_slot; + *tx_slot = 0; + *tx_num = 0; + *rx_num = num; + break; + default: + BTFMSLIM_ERR("Unsupported DAI %d", dai->id); + return -EINVAL; + } + + do { + if (!ch) + return -EINVAL; + for (i = 0; (i < BTFM_SLIM_NUM_CODEC_DAIS) && (ch->id != + BTFM_SLIM_NUM_CODEC_DAIS) && (ch->id != dai->id); + ch++, i++) + ; + + if (ch->id == BTFM_SLIM_NUM_CODEC_DAIS || + i == BTFM_SLIM_NUM_CODEC_DAIS) { + BTFMSLIM_ERR( + "No channel has been allocated for dai (%d)", + dai->id); + return -EINVAL; + } + if (!slot) + return -EINVAL; + *(slot + j) = ch->ch; + BTFMSLIM_DBG("id:%d, port:%d, ch:%d, slot: %d", ch->id, + ch->port, ch->ch, *(slot + j)); + + /* In case it has mulitiple channels */ + if (++j < num) + ch++; + } while (j < num); + + return 0; +} + +static struct snd_soc_dai_ops btfmslim_dai_ops = { + .startup = btfm_slim_dai_startup, + .shutdown = btfm_slim_dai_shutdown, + .hw_params = btfm_slim_dai_hw_params, + .prepare = btfm_slim_dai_prepare, + .set_channel_map = btfm_slim_dai_set_channel_map, + .get_channel_map = btfm_slim_dai_get_channel_map, +}; + +static struct snd_soc_dai_driver btfmslim_dai[] = { + { /* FM Audio data multiple channel : FM -> qdsp */ + .name = "btfm_fm_slim_tx", + .id = BTFM_FM_SLIM_TX, + .capture = { + .stream_name = "FM TX Capture", + .rates = SNDRV_PCM_RATE_48000, /* 48 KHz */ + .formats = SNDRV_PCM_FMTBIT_S16_LE, /* 16 bits */ + .rate_max = 48000, + .rate_min = 48000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &btfmslim_dai_ops, + }, + { /* Bluetooth SCO voice uplink: bt -> modem */ + .name = "btfm_bt_sco_slim_tx", + .id = BTFM_BT_SCO_SLIM_TX, + .capture = { + .stream_name = "SCO TX Capture", + /* 8 KHz or 16 KHz */ + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 + | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 + | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, /* 16 bits */ + .rate_max = 96000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 1, + }, + .ops = &btfmslim_dai_ops, + }, + { /* Bluetooth SCO voice downlink: modem -> bt or A2DP Playback */ + .name = "btfm_bt_sco_a2dp_slim_rx", + .id = BTFM_BT_SCO_A2DP_SLIM_RX, + .playback = { + .stream_name = "SCO A2DP RX Playback", + /* 8/16/44.1/48/88.2/96 Khz */ + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 + | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 + | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, /* 16 bits */ + .rate_max = 96000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 1, + }, + .ops = &btfmslim_dai_ops, + }, + { /* Bluetooth Split A2DP data: qdsp -> bt */ + .name = "btfm_bt_split_a2dp_slim_rx", + .id = BTFM_BT_SPLIT_A2DP_SLIM_RX, + .playback = { + .stream_name = "SPLIT A2DP Playback", + .rates = SNDRV_PCM_RATE_48000, /* 48 KHz */ + .formats = SNDRV_PCM_FMTBIT_S16_LE, /* 16 bits */ + .rate_max = 48000, + .rate_min = 48000, + .channels_min = 1, + .channels_max = 1, + }, + .ops = &btfmslim_dai_ops, + }, +}; + +static const struct snd_soc_component_driver btfmslim_codec = { + .probe = btfm_slim_codec_probe, + .remove = btfm_slim_codec_remove, + .read = btfm_slim_codec_read, + .write = btfm_slim_codec_write, +}; + +int btfm_slim_register_codec(struct device *dev) +{ + int ret = 0; + + BTFMSLIM_DBG(""); + /* Register Codec driver */ + ret = snd_soc_register_component(dev, &btfmslim_codec, + btfmslim_dai, ARRAY_SIZE(btfmslim_dai)); + + if (ret) + BTFMSLIM_ERR("failed to register codec (%d)", ret); + + return ret; +} + +void btfm_slim_unregister_codec(struct device *dev) +{ + BTFMSLIM_DBG(""); + /* Unregister Codec driver */ + snd_soc_unregister_component(dev); +} + +MODULE_DESCRIPTION("BTFM Slimbus Codec driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/bluetooth/btfm_slim_slave.c b/drivers/bluetooth/btfm_slim_slave.c new file mode 100644 index 000000000000..f02e31950093 --- /dev/null +++ b/drivers/bluetooth/btfm_slim_slave.c @@ -0,0 +1,189 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved. + */ + +#include +#include "btfm_slim.h" +#include "btfm_slim_slave.h" + +/* SLAVE (WCN3990/QCA6390) Port assignment */ +struct btfmslim_ch slave_rxport[] = { + {.id = BTFM_BT_SCO_A2DP_SLIM_RX, .name = "SCO_A2P_Rx", + .port = SLAVE_SB_PGD_PORT_RX_SCO}, + {.id = BTFM_BT_SPLIT_A2DP_SLIM_RX, .name = "A2P_Rx", + .port = SLAVE_SB_PGD_PORT_RX_A2P}, + {.id = BTFM_SLIM_NUM_CODEC_DAIS, .name = "", + .port = BTFM_SLIM_PGD_PORT_LAST}, +}; + +struct btfmslim_ch slave_txport[] = { + {.id = BTFM_BT_SCO_SLIM_TX, .name = "SCO_Tx", + .port = SLAVE_SB_PGD_PORT_TX_SCO}, + {.id = BTFM_FM_SLIM_TX, .name = "FM_Tx1", + .port = SLAVE_SB_PGD_PORT_TX1_FM}, + {.id = BTFM_FM_SLIM_TX, .name = "FM_Tx2", + .port = SLAVE_SB_PGD_PORT_TX2_FM}, + {.id = BTFM_SLIM_NUM_CODEC_DAIS, .name = "", + .port = BTFM_SLIM_PGD_PORT_LAST}, +}; + +/* Function description */ +int btfm_slim_slave_hw_init(struct btfmslim *btfmslim) +{ + int ret = 0; + uint8_t reg_val; + uint16_t reg; + + BTFMSLIM_DBG(""); + + if (!btfmslim) + return -EINVAL; + + /* Get SB_SLAVE_HW_REV_MSB value*/ + reg = SLAVE_SB_SLAVE_HW_REV_MSB; + ret = btfm_slim_read(btfmslim, reg, 1, ®_val, IFD); + if (ret) { + BTFMSLIM_ERR("failed to read (%d) reg 0x%x", ret, reg); + goto error; + } + BTFMSLIM_DBG("Major Rev: 0x%x, Minor Rev: 0x%x", + (reg_val & 0xF0) >> 4, (reg_val & 0x0F)); + + /* Get SB_SLAVE_HW_REV_LSB value*/ + reg = SLAVE_SB_SLAVE_HW_REV_LSB; + ret = btfm_slim_read(btfmslim, reg, 1, ®_val, IFD); + if (ret) { + BTFMSLIM_ERR("failed to read (%d) reg 0x%x", ret, reg); + goto error; + } + BTFMSLIM_DBG("Step Rev: 0x%x", reg_val); + +error: + return ret; +} + +static inline int is_fm_port(uint8_t port_num) +{ + if (port_num == SLAVE_SB_PGD_PORT_TX1_FM || + port_num == CHRKVER3_SB_PGD_PORT_TX1_FM || + port_num == CHRKVER3_SB_PGD_PORT_TX2_FM || + port_num == SLAVE_SB_PGD_PORT_TX2_FM) + return 1; + else + return 0; +} + +int btfm_slim_slave_enable_port(struct btfmslim *btfmslim, uint8_t port_num, + uint8_t rxport, uint8_t enable) +{ + int ret = 0; + uint8_t reg_val = 0, en; + uint8_t rxport_num = 0; + uint16_t reg; + + BTFMSLIM_DBG("port(%d) enable(%d)", port_num, enable); + if (rxport) { + BTFMSLIM_DBG("sample rate is %d", btfmslim->sample_rate); + if (enable && + btfmslim->sample_rate != 44100 && + btfmslim->sample_rate != 88200) { + BTFMSLIM_DBG("setting multichannel bit"); + /* For SCO Rx, A2DP Rx other than 44.1 and 88.2Khz */ + if (port_num < 24) { + rxport_num = port_num - 16; + reg_val = 0x01 << rxport_num; + reg = SLAVE_SB_PGD_RX_PORTn_MULTI_CHNL_0( + rxport_num); + } else { + rxport_num = port_num - 24; + reg_val = 0x01 << rxport_num; + reg = SLAVE_SB_PGD_RX_PORTn_MULTI_CHNL_1( + rxport_num); + } + + BTFMSLIM_DBG("writing reg_val (%d) to reg(%x)", + reg_val, reg); + ret = btfm_slim_write(btfmslim, reg, 1, ®_val, IFD); + if (ret) { + BTFMSLIM_ERR("failed to write (%d) reg 0x%x", + ret, reg); + goto error; + } + } + /* Port enable */ + reg = SLAVE_SB_PGD_PORT_RX_CFGN(port_num - 0x10); + goto enable_disable_rxport; + } + if (!enable) + goto enable_disable_txport; + + /* txport */ + /* Multiple Channel Setting */ + if (is_fm_port(port_num)) { + if (port_num == CHRKVER3_SB_PGD_PORT_TX1_FM) + reg_val = (0x1 << CHRKVER3_SB_PGD_PORT_TX1_FM); + else if (port_num == CHRKVER3_SB_PGD_PORT_TX2_FM) + reg_val = (0x1 << CHRKVER3_SB_PGD_PORT_TX2_FM); + else + reg_val = (0x1 << SLAVE_SB_PGD_PORT_TX1_FM) | + (0x1 << SLAVE_SB_PGD_PORT_TX2_FM); + reg = SLAVE_SB_PGD_TX_PORTn_MULTI_CHNL_0(port_num); + BTFMSLIM_INFO("writing reg_val (%d) to reg(%x)", reg_val, reg); + ret = btfm_slim_write(btfmslim, reg, 1, ®_val, IFD); + if (ret) { + BTFMSLIM_ERR("failed to write (%d) reg 0x%x", ret, reg); + goto error; + } + } else if (port_num == SLAVE_SB_PGD_PORT_TX_SCO) { + /* SCO Tx */ + reg_val = 0x1 << SLAVE_SB_PGD_PORT_TX_SCO; + reg = SLAVE_SB_PGD_TX_PORTn_MULTI_CHNL_0(port_num); + BTFMSLIM_DBG("writing reg_val (%d) to reg(%x)", + reg_val, reg); + ret = btfm_slim_write(btfmslim, reg, 1, ®_val, IFD); + if (ret) { + BTFMSLIM_ERR("failed to write (%d) reg 0x%x", + ret, reg); + goto error; + } + } + + /* Enable Tx port hw auto recovery for underrun or overrun error */ + reg_val = (SLAVE_ENABLE_OVERRUN_AUTO_RECOVERY | + SLAVE_ENABLE_UNDERRUN_AUTO_RECOVERY); + reg = SLAVE_SB_PGD_PORT_TX_OR_UR_CFGN(port_num); + ret = btfm_slim_write(btfmslim, reg, 1, ®_val, IFD); + if (ret) { + BTFMSLIM_ERR("failed to write (%d) reg 0x%x", ret, reg); + goto error; + } + +enable_disable_txport: + /* Port enable */ + reg = SLAVE_SB_PGD_PORT_TX_CFGN(port_num); + +enable_disable_rxport: + if (enable) + en = SLAVE_SB_PGD_PORT_ENABLE; + else + en = SLAVE_SB_PGD_PORT_DISABLE; + + if (is_fm_port(port_num)) + reg_val = en | SLAVE_SB_PGD_PORT_WM_L8; + else if (port_num == SLAVE_SB_PGD_PORT_TX_SCO) + reg_val = enable ? en | SLAVE_SB_PGD_PORT_WM_L1 : en; + else + reg_val = enable ? en | SLAVE_SB_PGD_PORT_WM_LB : en; + + if (enable && port_num == SLAVE_SB_PGD_PORT_TX_SCO) + BTFMSLIM_INFO("programming SCO Tx with reg_val %d to reg 0x%x", + reg_val, reg); + + ret = btfm_slim_write(btfmslim, reg, 1, ®_val, IFD); + if (ret) + BTFMSLIM_ERR("failed to write (%d) reg 0x%x", ret, reg); + +error: + return ret; +} diff --git a/drivers/bluetooth/btfm_slim_slave.h b/drivers/bluetooth/btfm_slim_slave.h new file mode 100644 index 000000000000..26d9c1d4daa3 --- /dev/null +++ b/drivers/bluetooth/btfm_slim_slave.h @@ -0,0 +1,170 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved. + */ + +#ifndef BTFM_SLIM_SLAVE_H +#define BTFM_SLIM_SLAVE_H +#include "btfm_slim.h" + +/* Registers Address */ +#define SLAVE_SB_COMP_TEST 0x00000000 +#define SLAVE_SB_SLAVE_HW_REV_MSB 0x00000001 +#define SLAVE_SB_SLAVE_HW_REV_LSB 0x00000002 +#define SLAVE_SB_DEBUG_FEATURES 0x00000005 +#define SLAVE_SB_INTF_INT_EN 0x00000010 +#define SLAVE_SB_INTF_INT_STATUS 0x00000011 +#define SLAVE_SB_INTF_INT_CLR 0x00000012 +#define SLAVE_SB_FRM_CFG 0x00000013 +#define SLAVE_SB_FRM_STATUS 0x00000014 +#define SLAVE_SB_FRM_INT_EN 0x00000015 +#define SLAVE_SB_FRM_INT_STATUS 0x00000016 +#define SLAVE_SB_FRM_INT_CLR 0x00000017 +#define SLAVE_SB_FRM_WAKEUP 0x00000018 +#define SLAVE_SB_FRM_CLKCTL_DONE 0x00000019 +#define SLAVE_SB_FRM_IE_STATUS 0x0000001A +#define SLAVE_SB_FRM_VE_STATUS 0x0000001B +#define SLAVE_SB_PGD_TX_CFG_STATUS 0x00000020 +#define SLAVE_SB_PGD_RX_CFG_STATUS 0x00000021 +#define SLAVE_SB_PGD_DEV_INT_EN 0x00000022 +#define SLAVE_SB_PGD_DEV_INT_STATUS 0x00000023 +#define SLAVE_SB_PGD_DEV_INT_CLR 0x00000024 +#define SLAVE_SB_PGD_PORT_INT_EN_RX_0 0x00000030 +#define SLAVE_SB_PGD_PORT_INT_EN_RX_1 0x00000031 +#define SLAVE_SB_PGD_PORT_INT_EN_TX_0 0x00000032 +#define SLAVE_SB_PGD_PORT_INT_EN_TX_1 0x00000033 +#define SLAVE_SB_PGD_PORT_INT_STATUS_RX_0 0x00000034 +#define SLAVE_SB_PGD_PORT_INT_STATUS_RX_1 0x00000035 +#define SLAVE_SB_PGD_PORT_INT_STATUS_TX_0 0x00000036 +#define SLAVE_SB_PGD_PORT_INT_STATUS_TX_1 0x00000037 +#define SLAVE_SB_PGD_PORT_INT_CLR_RX_0 0x00000038 +#define SLAVE_SB_PGD_PORT_INT_CLR_RX_1 0x00000039 +#define SLAVE_SB_PGD_PORT_INT_CLR_TX_0 0x0000003A +#define SLAVE_SB_PGD_PORT_INT_CLR_TX_1 0x0000003B +#define SLAVE_SB_PGD_PORT_RX_CFGN(n) (0x00000040 + n) +#define SLAVE_SB_PGD_PORT_TX_CFGN(n) (0x00000050 + n) +#define SLAVE_SB_PGD_PORT_INT_RX_SOURCEN(n) (0x00000060 + n) +#define SLAVE_SB_PGD_PORT_INT_TX_SOURCEN(n) (0x00000070 + n) +#define SLAVE_SB_PGD_PORT_RX_STATUSN(n) (0x00000080 + n) +#define SLAVE_SB_PGD_PORT_TX_STATUSN(n) (0x00000090 + n) +#define SLAVE_SB_PGD_TX_PORTn_MULTI_CHNL_0(n) (0x00000100 + 0x4*n) +#define SLAVE_SB_PGD_TX_PORTn_MULTI_CHNL_1(n) (0x00000101 + 0x4*n) +#define SLAVE_SB_PGD_RX_PORTn_MULTI_CHNL_0(n) (0x00000180 + 0x4*n) +#define SLAVE_SB_PGD_RX_PORTn_MULTI_CHNL_1(n) (0x00000181 + 0x4*n) +#define SLAVE_SB_PGD_PORT_TX_OR_UR_CFGN(n) (0x000001F0 + n) + +/* Register Bit Setting */ +#define SLAVE_ENABLE_OVERRUN_AUTO_RECOVERY (0x1 << 1) +#define SLAVE_ENABLE_UNDERRUN_AUTO_RECOVERY (0x1 << 0) +#define SLAVE_SB_PGD_PORT_ENABLE (0x1 << 0) +#define SLAVE_SB_PGD_PORT_DISABLE (0x0 << 0) +#define SLAVE_SB_PGD_PORT_WM_L1 (0x1 << 1) +#define SLAVE_SB_PGD_PORT_WM_L2 (0x2 << 1) +#define SLAVE_SB_PGD_PORT_WM_L3 (0x3 << 1) +#define SLAVE_SB_PGD_PORT_WM_L8 (0x8 << 1) +#define SLAVE_SB_PGD_PORT_WM_LB (0xB << 1) + +#define SLAVE_SB_PGD_PORT_RX_NUM 16 +#define SLAVE_SB_PGD_PORT_TX_NUM 16 + +/* PGD Port Map */ +#define SLAVE_SB_PGD_PORT_TX_SCO 0 +#define SLAVE_SB_PGD_PORT_TX1_FM 1 +#define SLAVE_SB_PGD_PORT_TX2_FM 2 +#define CHRKVER3_SB_PGD_PORT_TX1_FM 4 +#define CHRKVER3_SB_PGD_PORT_TX2_FM 5 +#define SLAVE_SB_PGD_PORT_RX_SCO 16 +#define SLAVE_SB_PGD_PORT_RX_A2P 17 + +enum { + QCA_CHEROKEE_SOC_ID_0100 = 0x40010100, + QCA_CHEROKEE_SOC_ID_0200 = 0x40010200, + QCA_CHEROKEE_SOC_ID_0201 = 0x40010201, + QCA_CHEROKEE_SOC_ID_0210 = 0x40010214, + QCA_CHEROKEE_SOC_ID_0211 = 0x40010224, + QCA_CHEROKEE_SOC_ID_0300 = 0x40010300, + QCA_CHEROKEE_SOC_ID_0310 = 0x40010310, + QCA_CHEROKEE_SOC_ID_0320 = 0x40010320, +}; + +enum { + QCA_APACHE_SOC_ID_0005 = 0x40020100, + QCA_APACHE_SOC_ID_0006 = 0x40020110, + QCA_APACHE_SOC_ID_0100 = 0x40020120, + QCA_APACHE_SOC_ID_0101 = 0x40020121, + QCA_APACHE_SOC_ID_0102 = 0x40020122, + QCA_APACHE_SOC_ID_0103 = 0x40020123, + QCA_APACHE_SOC_ID_0110 = 0x40020130, + QCA_APACHE_SOC_ID_0120 = 0x40020140, + QCA_APACHE_SOC_ID_0121 = 0x40020150, +}; + +enum { + QCA_COMANCHE_SOC_ID_0100 = 0x40070100, + QCA_COMANCHE_SOC_ID_0101 = 0x40070101, + QCA_COMANCHE_SOC_ID_0110 = 0x40070110, +}; + +enum { + QCA_HASTINGS_SOC_ID_0100 = 0x400A0100, + QCA_HASTINGS_SOC_ID_0101 = 0x40040101, + QCA_HASTINGS_SOC_ID_0110 = 0x400A0110, + QCA_HASTINGS_SOC_ID_0200 = 0x400A0200, +}; + +/* Function Prototype */ + +/* + * btfm_slim_slave_hw_init: Initialize slave specific slimbus slave device + * @btfmslim: slimbus slave device data pointer. + * Returns: + * 0: Success + * else: Fail + */ +int btfm_slim_slave_hw_init(struct btfmslim *btfmslim); + +/* + * btfm_slim_slave_enable_rxport: Enable slave Rx port by given port number + * @btfmslim: slimbus slave device data pointer. + * @portNum: slimbus slave port number to enable + * @rxport: rxport or txport + * @enable: enable port or disable port + * Returns: + * 0: Success + * else: Fail + */ +int btfm_slim_slave_enable_port(struct btfmslim *btfmslim, uint8_t portNum, + uint8_t rxport, uint8_t enable); + +/* Specific defines for slave slimbus device */ +#define SLAVE_SLIM_REG_OFFSET 0x0800 + +#ifdef SLIM_SLAVE_REG_OFFSET +#undef SLIM_SLAVE_REG_OFFSET +#define SLIM_SLAVE_REG_OFFSET SLAVE_SLIM_REG_OFFSET +#endif + +/* Assign vendor specific function */ +extern struct btfmslim_ch slave_txport[]; +extern struct btfmslim_ch slave_rxport[]; + +#ifdef SLIM_SLAVE_RXPORT +#undef SLIM_SLAVE_RXPORT +#define SLIM_SLAVE_RXPORT (&slave_rxport[0]) +#endif + +#ifdef SLIM_SLAVE_TXPORT +#undef SLIM_SLAVE_TXPORT +#define SLIM_SLAVE_TXPORT (&slave_txport[0]) +#endif + +#ifdef SLIM_SLAVE_INIT +#undef SLIM_SLAVE_INIT +#define SLIM_SLAVE_INIT btfm_slim_slave_hw_init +#endif + +#ifdef SLIM_SLAVE_PORT_EN +#undef SLIM_SLAVE_PORT_EN +#define SLIM_SLAVE_PORT_EN btfm_slim_slave_enable_port +#endif +#endif