diff --git a/soundwire/Kconfig b/soundwire/Kconfig new file mode 100644 index 0000000000..f01fac6089 --- /dev/null +++ b/soundwire/Kconfig @@ -0,0 +1,12 @@ + +config BTFM_SWR + tristate "MSM Bluetooth/FM SoundWire Device" + depends on MSM_BT_POWER + help + This enables BT/FM soundwire driver to open/close ports. + This will make use of soundwire bus driver and soundwire + master driver to communicate with soundwire slave. + + Say Y here to compile support for Bluetooth/FM soundwire slave driver + into the kernel or say M to compile as a module. + diff --git a/soundwire/Makefile b/soundwire/Makefile new file mode 100644 index 0000000000..04eb9a25e3 --- /dev/null +++ b/soundwire/Makefile @@ -0,0 +1,3 @@ +ccflags-y += -I$(BT_ROOT)/include +bt_fm_swr-objs := btfm_swr.o btfm_swr_codec.o btfm_swr_slave.o +obj-$(CONFIG_BTFM_SWR) += bt_fm_swr.o diff --git a/soundwire/btfm_swr.c b/soundwire/btfm_swr.c new file mode 100644 index 0000000000..c40271d5de --- /dev/null +++ b/soundwire/btfm_swr.c @@ -0,0 +1,288 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "btpower.h" +#include "btfm_swr.h" +#include "btfm_swr_slave.h" + +struct class *btfm_swr_class; +static int btfm_swr_major; +struct btfmswr *pbtfmswr; +static int btfm_num_ports_open; + +#define BT_CMD_SWR_TEST 0xbfac + +static int btfm_swr_probe(struct swr_device *pdev); + +int btfm_get_bt_soc_index(int chipset_ver) +{ + switch (chipset_ver) { + case QCA_GANGES_SOC_ID_0100: + case QCA_GANGES_SOC_ID_0200: + return GANGES; + case QCA_EVROS_SOC_ID_0100: + case QCA_EVROS_SOC_ID_0200: + return EVROS; + default: + BTFMSWR_ERR("no BT SOC id defined, returning EVROS"); + return EVROS; + } +} + +int btfm_swr_hw_init(void) +{ + uint8_t dev_num = 0; + int ret = 0; + int chipset_ver; + + BTFMSWR_DBG(""); + + if (pbtfmswr->initialized) + BTFMSWR_INFO("Already initialized"); + + // get BT chipset version + chipset_ver = btpower_get_chipset_version(); + + // get BT/FM SOC slave port details + pbtfmswr->soc_index = btfm_get_bt_soc_index(chipset_ver); + + BTFMSWR_INFO("chipset soc version:%x, soc index: %x", chipset_ver, + pbtfmswr->soc_index); + + pbtfmswr->p_dai_port = &slave_port[pbtfmswr->soc_index]; + + // get logical address + /* + * Add 5msec delay to provide sufficient time for + * soundwire auto enumeration of slave devices as + * per HW requirement. + */ + usleep_range(5000, 5010); + ret = swr_get_logical_dev_num(pbtfmswr->swr_slave, pbtfmswr->p_dai_port->ea, + &dev_num); + if (ret) { + BTFMSWR_ERR("error while getting logical device number"); + goto err; + } + + pbtfmswr->swr_slave->dev_num = dev_num; + pbtfmswr->initialized = true; + +err: + return ret; +} + +int btfm_swr_enable_port(u8 port_num, u8 ch_count, u32 sample_rate, u8 usecase) +{ + int ret = 0; + u8 port_id[MAX_BT_PORTS]; + u8 num_ch[MAX_BT_PORTS]; + u8 ch_mask[MAX_BT_PORTS]; + u32 ch_rate[MAX_BT_PORTS]; + u8 port_type[MAX_BT_PORTS]; + u8 num_port = 1; + + // master expects port num -1 to be sent + port_id[0] = port_num-1; + num_ch[0] = ch_count; + ch_mask[0] = ch_count == 2 ? TWO_CHANNEL_MASK : ONE_CHANNEL_MASK; + ch_rate[0] = sample_rate; + port_type[0] = usecase; + + BTFMSWR_INFO("enabling port : %d\n", port_num); + ret = swr_connect_port(pbtfmswr->swr_slave, &port_id[0], num_port, + &ch_mask[0], &ch_rate[0], &num_ch[0], + &port_type[0]); + + if (ret < 0) { + BTFMSWR_ERR("swr_connect_port failed, error %d", ret); + return ret; + } + + BTFMSWR_INFO("calling swr_slvdev_datapath_control\n"); + ret = swr_slvdev_datapath_control(pbtfmswr->swr_slave, + pbtfmswr->swr_slave->dev_num, + true); + if (ret < 0) + BTFMSWR_ERR("swr_slvdev_datapath_control failed"); + + if (ret == 0) + btfm_num_ports_open++; + + BTFMSWR_INFO("btfm_num_ports_open: %d", btfm_num_ports_open); + + return ret; +} + +int btfm_swr_disable_port(u8 port_num, u8 ch_count, u8 usecase) +{ + int ret = 0; + u8 port_id[MAX_BT_PORTS]; + u8 ch_mask[MAX_BT_PORTS]; + u8 port_type[MAX_BT_PORTS]; + u8 num_port = 1; + + // master expects port num -1 to be sent + port_id[0] = port_num-1; + ch_mask[0] = ch_count == 2 ? TWO_CHANNEL_MASK : ONE_CHANNEL_MASK; + port_type[0] = usecase; + + BTFMSWR_INFO("disabling port : %d\n", port_num); + ret = swr_disconnect_port(pbtfmswr->swr_slave, &port_id[0], num_port, + &ch_mask[0], &port_type[0]); + + if (ret < 0) + BTFMSWR_ERR("swr_disconnect_port port failed, error %d", ret); + + BTFMSWR_INFO("calling swr_slvdev_datapath_control\n"); + ret = swr_slvdev_datapath_control(pbtfmswr->swr_slave, + pbtfmswr->swr_slave->dev_num, + false); + if (ret < 0) + BTFMSWR_ERR("swr_slvdev_datapath_control failed"); + + if (btfm_num_ports_open > 0) + btfm_num_ports_open--; + + BTFMSWR_INFO("btfm_num_ports_open: %d", btfm_num_ports_open); + + return ret; +} + +static long btfm_swr_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + int ret = 0; + + BTFMSWR_INFO(""); + switch (cmd) { + case BT_CMD_SWR_TEST: + BTFMSWR_INFO("cmd BT_CMD_SLIM_TEST, call btfm_swr_hw_init"); + ret = btfm_swr_hw_init(); + break; + } + return ret; +} + +static const struct file_operations bt_dev_fops = { + .unlocked_ioctl = btfm_swr_ioctl, + .compat_ioctl = btfm_swr_ioctl, +}; + +static int btfm_swr_probe(struct swr_device *pdev) +{ + int ret = 0; + + BTFMSWR_INFO(""); + + pbtfmswr = devm_kzalloc(&pdev->dev, + sizeof(struct btfmswr), GFP_KERNEL); + if (!pbtfmswr) { + BTFMSWR_ERR("memory allocation to driver failed"); + return -ENOMEM; + } + + swr_set_dev_data(pdev, pbtfmswr); + pbtfmswr->swr_slave = pdev; + pbtfmswr->dev = &pdev->dev; + pbtfmswr->initialized = false; + + // register with ALSA + ret = btfm_swr_register_codec(pbtfmswr); + if (ret) { + BTFMSWR_ERR("registration with ALSA failed, returning"); + goto dealloc; + } + + btfm_swr_major = register_chrdev(0, "btfm_swr", &bt_dev_fops); + if (btfm_swr_major < 0) { + BTFMSWR_ERR("%s: failed to allocate char dev\n", __func__); + ret = -1; + goto register_err; + } + + btfm_swr_class = class_create(THIS_MODULE, "btfmswr-dev"); + if (IS_ERR(btfm_swr_class)) { + BTFMSWR_ERR("%s: coudn't create class\n", __func__); + ret = -1; + goto class_err; + } + + if (device_create(btfm_swr_class, NULL, MKDEV(btfm_swr_major, 0), + NULL, "btfmswr") == NULL) { + BTFMSWR_ERR("%s: failed to allocate char dev\n", __func__); + ret = -1; + goto device_err; + } + return ret; + +device_err: + class_destroy(btfm_swr_class); +class_err: + unregister_chrdev(btfm_swr_major, "btfm_swr"); + +register_err: + btfm_swr_unregister_codec(pbtfmswr->dev); + +dealloc: + kfree(pbtfmswr); + return ret; +} + +static const struct swr_device_id btfm_swr_id[] = { + {SWR_SLAVE_COMPATIBLE_STR, 0}, + {} +}; + +static const struct of_device_id btfm_swr_dt_match[] = { + { + .compatible = "qcom,btfmswr_slave", + }, + {} +}; + +static struct swr_driver btfm_swr_driver = { + .driver = { + .name = "btfmswr-driver", + .owner = THIS_MODULE, + .of_match_table = btfm_swr_dt_match, + }, + .probe = btfm_swr_probe, + .id_table = btfm_swr_id, +}; + +static int __init btfm_swr_init(void) +{ + BTFMSWR_INFO(""); + return swr_driver_register(&btfm_swr_driver); +} + +static void __exit btfm_swr_exit(void) +{ + BTFMSWR_INFO(""); + swr_driver_unregister(&btfm_swr_driver); +} + +module_init(btfm_swr_init); +module_exit(btfm_swr_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("BTFM SoundWire Slave driver"); + diff --git a/soundwire/btfm_swr.h b/soundwire/btfm_swr.h new file mode 100644 index 0000000000..a1e9b7ab86 --- /dev/null +++ b/soundwire/btfm_swr.h @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. + */ + + +#ifndef BTFM_SWR_H +#define BTFM_SWR_H + +#include +#include +#include +#include "soc/soundwire.h" + +#define SWR_SLAVE_COMPATIBLE_STR "btfmswr_slave" + + +#define BTFMSWR_DBG(fmt, arg...) pr_debug("%s: " fmt "\n", __func__, ## arg) +#define BTFMSWR_INFO(fmt, arg...) pr_info("%s: " fmt "\n", __func__, ## arg) +#define BTFMSWR_ERR(fmt, arg...) pr_err("%s: " fmt "\n", __func__, ## arg) + +extern struct btfmswr *pbtfmswr; + +// assumption is that we use adjacent channels +#define ONE_CHANNEL_MASK 1 +#define TWO_CHANNEL_MASK 3 + +#define MAX_BT_PORTS 1 + +/* Codec driver defines */ +enum { + FMAUDIO_TX = 0, + BTAUDIO_TX, + BTAUDIO_RX, + BTAUDIO_A2DP_SINK_TX, + BTFM_NUM_CODEC_DAIS +}; + +enum { + EVROS = 0, + GANGES = 1, + MAX_SOC_ID = 0xFF +}; + + +struct btfmswr_dai_port_info { + int dai_id; + char *dai_name; + uint8_t port; +}; + +struct soc_port_mapping { + // enumeration address of BT SOC + u64 ea; + struct btfmswr_dai_port_info port_info[BTFM_NUM_CODEC_DAIS]; +}; + + +struct btfmswr { + struct device *dev; + struct swr_device *swr_slave; + bool initialized; + uint32_t sample_rate; + uint32_t bps; + uint16_t direction; + uint8_t num_channels; + int soc_index; + struct soc_port_mapping *p_dai_port; +}; + +/** + * btfm_swr_hw_init: Initialize soundwire slave device + * Returns: + * 0: Success + * else: Fail + */ +int btfm_swr_hw_init(void); + +int btfm_get_bt_soc_index(int chipset_ver); + +int btfm_swr_enable_port(u8 port_num, u8 ch_count, u32 sample_rate, + u8 port_type); + + +int btfm_swr_disable_port(u8 port_num, u8 ch_count, u8 usecase); + +/** + * btfm_swr_register_codec: Register codec driver with ALSA + * @btfmswr: swr slave device data pointer. + * Returns: + * -ENOMEM + * 0 + */ +int btfm_swr_register_codec(struct btfmswr *btfmswr); + +/** + * btfm_swr_unregister_codec: Unregister codec driver with ALSA + * @dev: device node + * Returns: + * VOID + */ +void btfm_swr_unregister_codec(struct device *dev); +#endif /* BTFM_SWR_H */ diff --git a/soundwire/btfm_swr_codec.c b/soundwire/btfm_swr_codec.c new file mode 100644 index 0000000000..e0fcd363a1 --- /dev/null +++ b/soundwire/btfm_swr_codec.c @@ -0,0 +1,341 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include "btfm_swr.h" + +static int bt_soc_enable_status; +int btfm_feedback_ch_setting; + +static int btfm_swr_codec_write(struct snd_soc_component *codec, + unsigned int reg, unsigned int value) +{ + BTFMSWR_DBG(""); + return 0; +} + +static unsigned int btfm_swr_codec_read(struct snd_soc_component *codec, + unsigned int reg) +{ + BTFMSWR_DBG(""); + return 0; +} + +static int btfm_soc_status_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + BTFMSWR_DBG(""); + ucontrol->value.integer.value[0] = bt_soc_enable_status; + return 1; +} + +static int btfm_soc_status_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + BTFMSWR_DBG(""); + return 1; +} + +static int btfm_get_feedback_ch_setting(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + BTFMSWR_DBG(""); + 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) +{ + BTFMSWR_DBG(""); + 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, + btfm_soc_status_get, + btfm_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_swr_codec_probe(struct snd_soc_component *codec) +{ + BTFMSWR_DBG(""); + snd_soc_add_component_controls(codec, status_controls, + ARRAY_SIZE(status_controls)); + return 0; +} + +static void btfm_swr_codec_remove(struct snd_soc_component *codec) +{ + BTFMSWR_DBG(""); +} + +static int btfm_swr_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + int ret = -1; + + BTFMSWR_INFO("substream = %s stream = %d dai->name = %s", + substream->name, substream->stream, dai->name); + ret = btfm_swr_hw_init(); + return ret; +} + +static void btfm_swr_dai_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + int ret = 0; + u8 port_type; + + BTFMSWR_INFO("dai->name: %s, dai->id: %d, dai->rate: %d", dai->name, + dai->id, dai->rate); + + switch (dai->id) { + case FMAUDIO_TX: + port_type = FM_AUDIO_TX1; + break; + case BTAUDIO_TX: + port_type = BT_AUDIO_TX1; + break; + case BTAUDIO_RX: + port_type = BT_AUDIO_RX1; + break; + case BTAUDIO_A2DP_SINK_TX: + port_type = BT_AUDIO_TX2; + break; + case BTFM_NUM_CODEC_DAIS: + default: + BTFMSWR_ERR("dai->id is invalid:%d", dai->id); + return; + } + + ret = btfm_swr_disable_port(pbtfmswr->p_dai_port->port_info[dai->id].port, + pbtfmswr->num_channels, port_type); + +} + +static int btfm_swr_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + pbtfmswr->bps = params_width(params); + pbtfmswr->direction = substream->stream; + pbtfmswr->num_channels = params_channels(params); + + BTFMSWR_INFO("dai->name = %s dai id %x rate %d bps %d num_ch %d", + dai->name, dai->id, params_rate(params), params_width(params), + params_channels(params)); + return 0; +} + +static int btfm_swr_dai_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + int ret = -EINVAL; + u8 port_type; + + bt_soc_enable_status = 0; + BTFMSWR_INFO("dai->name: %s, dai->id: %d, dai->rate: %d direction: %d", dai->name, + dai->id, dai->rate, pbtfmswr->direction); + + /* save sample rate */ + pbtfmswr->sample_rate = dai->rate; + + switch (dai->id) { + case FMAUDIO_TX: + port_type = FM_AUDIO_TX1; + break; + case BTAUDIO_TX: + port_type = BT_AUDIO_TX1; + break; + case BTAUDIO_RX: + port_type = BT_AUDIO_RX1; + break; + case BTAUDIO_A2DP_SINK_TX: + port_type = BT_AUDIO_TX2; + break; + case BTFM_NUM_CODEC_DAIS: + default: + BTFMSWR_ERR("dai->id is invalid:%d", dai->id); + return -EINVAL; + } + + ret = btfm_swr_enable_port(pbtfmswr->p_dai_port->port_info[dai->id].port, + pbtfmswr->num_channels, dai->rate, port_type); + + /* save the enable channel status */ + if (ret == 0) + bt_soc_enable_status = 1; + + if (ret == -EISCONN) { + BTFMSWR_ERR("channel opened without closing, returning success"); + ret = 0; + } + + return ret; +} + +/* This function will be called once during boot up */ +static int btfm_swr_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) +{ + BTFMSWR_DBG(""); + return 0; +} + +static int btfm_swr_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) +{ + *rx_slot = 0; + *tx_slot = 0; + *rx_num = 0; + *tx_num = 0; + + switch (dai->id) { + case FMAUDIO_TX: + case BTAUDIO_TX: + case BTAUDIO_A2DP_SINK_TX: + *tx_num = pbtfmswr->num_channels; + *tx_slot = pbtfmswr->num_channels == 2 ? TWO_CHANNEL_MASK : + ONE_CHANNEL_MASK; + break; + case BTAUDIO_RX: + *rx_num = pbtfmswr->num_channels; + *rx_slot = pbtfmswr->num_channels == 2 ? TWO_CHANNEL_MASK : + ONE_CHANNEL_MASK; + break; + + default: + BTFMSWR_ERR("Unsupported DAI %d", dai->id); + return -EINVAL; + } + + return 0; +} +static struct snd_soc_dai_ops btfmswr_dai_ops = { + .startup = btfm_swr_dai_startup, + .shutdown = btfm_swr_dai_shutdown, + .hw_params = btfm_swr_dai_hw_params, + .prepare = btfm_swr_dai_prepare, + .set_channel_map = btfm_swr_dai_set_channel_map, + .get_channel_map = btfm_swr_dai_get_channel_map, +}; + +static struct snd_soc_dai_driver btfmswr_dai[] = { + { /* FM Audio data multiple channel : FM -> lpass */ + .name = "btfm_fm_swr_tx", + .id = FMAUDIO_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 = &btfmswr_dai_ops, + }, + { /* Bluetooth SCO voice uplink: bt -> lpass */ + .name = "btfm_bt_sco_swr_tx", + .id = BTAUDIO_TX, + .capture = { + .stream_name = "SCO TX Capture", + /* 8/16/44.1/48/88.2/96/192 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 + | SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, /* 16 bits */ + .rate_max = 192000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 1, + }, + .ops = &btfmswr_dai_ops, + }, + { /* Bluetooth SCO voice downlink: lpass -> bt or A2DP Playback */ + .name = "btfm_bt_sco_a2dp_swr_rx", + .id = BTAUDIO_RX, + .playback = { + .stream_name = "SCO A2DP RX Playback", + /* 8/16/44.1/48/88.2/96/192 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 + | SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, /* 16 bits */ + .rate_max = 192000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 1, + }, + .ops = &btfmswr_dai_ops, + }, + { /* Bluetooth A2DP sink: bt -> lpass */ + .name = "btfm_a2dp_sink_swr_tx", + .id = BTAUDIO_A2DP_SINK_TX, + .capture = { + .stream_name = "A2DP sink TX Capture", + /* 8/16/44.1/48/88.2/96/192 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 + | SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, /* 16 bits */ + .rate_max = 192000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 1, + }, + .ops = &btfmswr_dai_ops, + }, +}; + +static const struct snd_soc_component_driver btfmswr_codec = { + .probe = btfm_swr_codec_probe, + .remove = btfm_swr_codec_remove, + .read = btfm_swr_codec_read, + .write = btfm_swr_codec_write, +}; + +int btfm_swr_register_codec(struct btfmswr *btfm_swr) +{ + int ret = 0; + struct device *dev = btfm_swr->dev; + + BTFMSWR_DBG(""); + dev_err(dev, "\n"); + + /* Register Codec driver */ + ret = snd_soc_register_component(dev, &btfmswr_codec, + btfmswr_dai, ARRAY_SIZE(btfmswr_dai)); + if (ret) + BTFMSWR_ERR("failed to register codec (%d)", ret); + return ret; +} + +void btfm_swr_unregister_codec(struct device *dev) +{ + BTFMSWR_DBG(""); + /* Unregister Codec driver */ + snd_soc_unregister_component(dev); +} + + +MODULE_DESCRIPTION("BTFM SoundWire Codec driver"); +MODULE_LICENSE("GPL v2"); diff --git a/soundwire/btfm_swr_slave.c b/soundwire/btfm_swr_slave.c new file mode 100644 index 0000000000..f5e99bf092 --- /dev/null +++ b/soundwire/btfm_swr_slave.c @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. + */ + + +#include "btfm_swr.h" +#include "btfm_swr_slave.h" + +struct soc_port_mapping slave_port[] = { + // Evros + { + .ea = EVROS_EA, + .port_info[0].dai_id = FMAUDIO_TX, + .port_info[0].port = 5, + + .port_info[1].dai_id = BTAUDIO_TX, + .port_info[1].port = 3, + + .port_info[2].dai_id = BTAUDIO_RX, + .port_info[2].port = 1, + + .port_info[3].dai_id = BTAUDIO_A2DP_SINK_TX, + .port_info[3].port = 4, + }, + + // Ganges + { + .ea = GANGES_EA, + // FM is not supported on Ganges. populate with invalid port number + .port_info[0].dai_id = FMAUDIO_TX, + .port_info[0].port = BTFM_INVALID_PORT, + + .port_info[1].dai_id = BTAUDIO_TX, + .port_info[1].port = 4, + + .port_info[2].dai_id = BTAUDIO_RX, + .port_info[2].port = 1, + + .port_info[3].dai_id = BTAUDIO_A2DP_SINK_TX, + .port_info[3].port = 5, + }, +}; + diff --git a/soundwire/btfm_swr_slave.h b/soundwire/btfm_swr_slave.h new file mode 100644 index 0000000000..ab07cdddd4 --- /dev/null +++ b/soundwire/btfm_swr_slave.h @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. + */ + + +#ifndef BTFM_SWR_SLAVE_H +#define BTFM_SWR_SLAVE_H + +#include "btfm_swr.h" + +/* Registers Address */ + +/* 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 BTFM_INVALID_PORT 0xFF + +extern struct soc_port_mapping slave_port[]; + +enum { + QCA_GANGES_SOC_ID_0100 = 0x40210100, + QCA_GANGES_SOC_ID_0200 = 0x40210200, +}; + + +enum { + QCA_EVROS_SOC_ID_0100 = 0x40200100, + QCA_EVROS_SOC_ID_0200 = 0x40200200, +}; + + +enum { + EVROS_EA = 0x0108170220, + GANGES_EA = 0x0208170220, +}; + +/* Specific defines for slave slimbus device */ +#define SLAVE_SWR_REG_OFFSET 0x0800 + +#endif