From 897ecff7426b1837a9d3ea7e87fff5d870c0ce01 Mon Sep 17 00:00:00 2001 From: Balakrishna Godavarthi Date: Wed, 5 Jul 2023 11:55:04 +0530 Subject: [PATCH] soundwire: Update soundwire driver to support btfmcodec This change add supports in both btfmcodec and soundwire to support btfmcodec driver has interface. Change-Id: I2e77afaea44778147b362175aae33961dcc5042e Signed-off-by: Balakrishna Godavarthi --- bt_modules.bzl | 19 +- btfmcodec/btfm_codec.c | 72 +++- btfmcodec/btfm_codec_hw_interface.c | 11 +- btfmcodec/btfm_codec_interface.c | 128 +++++- btfmcodec/include/btfm_codec.h | 4 + btfmcodec/include/btfm_codec_hw_interface.h | 16 +- btfmcodec/include/btfm_codec_pkt.h | 19 + slimbus/btfm_slim_hw_interface.c | 9 +- soundwire/Makefile | 2 +- soundwire/btfm_swr.c | 6 +- soundwire/btfm_swr.h | 17 - soundwire/btfm_swr_codec.c | 341 --------------- soundwire/btfm_swr_hw_interface.c | 440 ++++++++++++++++++++ soundwire/btfm_swr_hw_interface.h | 35 ++ 14 files changed, 717 insertions(+), 402 deletions(-) delete mode 100644 soundwire/btfm_swr_codec.c create mode 100644 soundwire/btfm_swr_hw_interface.c create mode 100644 soundwire/btfm_swr_hw_interface.h diff --git a/bt_modules.bzl b/bt_modules.bzl index 8918e024ee..6759503284 100644 --- a/bt_modules.bzl +++ b/bt_modules.bzl @@ -1,6 +1,7 @@ PWR_PATH = "pwr" SLIMBUS_PATH = "slimbus" FMRTC_PATH = "rtc6226" +BTFMCODEC_PATH = "btfmcodec" # This dictionary holds all the BT modules included in the bt-kernel bt_modules = {} @@ -75,11 +76,10 @@ register_bt_modules( deps = [":%b_btpower"], ) -# Not enabled/compiling until btfmcodec is enabled register_bt_modules( name = "btfm_slim_codec", path = SLIMBUS_PATH, - # config_opt = "CONFIG_SLIM_BTFM_CODEC", + config_opt = "CONFIG_SLIM_BTFM_CODEC", srcs = [ "btfm_slim.c", "btfm_slim.h", @@ -88,7 +88,20 @@ register_bt_modules( "btfm_slim_hw_interface.c", "btfm_slim_hw_interface.h", ], - deps = [":%b_btpower", ":btfmcodec_headers"], + deps = [":%b_btpower", ":%b_btfmcodec", ":btfmcodec_headers"], +) + +register_bt_modules( + name = "btfmcodec", + path = BTFMCODEC_PATH, + config_opt = "CONFIG_BTFM_CODEC", + srcs = [ + "btfm_codec.c", + "btfm_codec_btadv_interface.c", + "btfm_codec_hw_interface.c", + "btfm_codec_interface.c", + ], + deps = [":btfmcodec_headers"], ) register_bt_modules( diff --git a/btfmcodec/btfm_codec.c b/btfmcodec/btfm_codec.c index a970e59793..7677b22f89 100644 --- a/btfmcodec/btfm_codec.c +++ b/btfmcodec/btfm_codec.c @@ -23,6 +23,7 @@ static dev_t dev_major; struct btfmcodec_data *btfmcodec; struct device_driver driver = {.name = "btfmcodec-driver", .owner = THIS_MODULE}; struct btfmcodec_char_device *btfmcodec_dev; +bool is_cp_supported = true; #define cdev_to_btfmchardev(_cdev) container_of(_cdev, struct btfmcodec_char_device, cdev) #define MIN_PKT_LEN 0x9 @@ -183,6 +184,22 @@ static void btfmcodec_dev_rxwork(struct work_struct *work) status); wake_up_interruptible(&btfmcodec_dev->rsp_wait_q[idx]); break; + case BTM_BTFMCODEC_CODEC_CONFIG_DMA_RSP: + idx = BTM_PKT_TYPE_DMA_CONFIG_RSP; + if (len == BTM_CODEC_CONFIG_DMA_RSP_LEN) { + status = skb->data[1]; + if (status == MSG_SUCCESS) + btfmcodec_dev->status[idx] = BTM_RSP_RECV; + else + btfmcodec_dev->status[idx] = BTM_FAIL_RESP_RECV; + } else { + BTFMCODEC_ERR("wrong packet format with len:%d", len); + btfmcodec_dev->status[idx] = BTM_FAIL_RESP_RECV; + } + BTFMCODEC_INFO("Rx BTM_BTFMCODEC_CODEC_CONFIG_DMA_RSP status:%d", + status); + wake_up_interruptible(&btfmcodec_dev->rsp_wait_q[idx]); + break; case BTM_BTFMCODEC_CTRL_MASTER_SHUTDOWN_RSP: idx = BTM_PKT_TYPE_MASTER_SHUTDOWN_RSP; if (len == BTM_MASTER_CONFIG_RSP_LEN) { @@ -442,6 +459,56 @@ static ssize_t btfmcodec_dev_read(struct file *file, return use; } + +bool isCpSupported(void) +{ + return is_cp_supported; +} + +static long btfmcodec_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct btfmcodec_data *btfmcodec = file->private_data; + struct hwep_data *hwep_info; + + BTFMCODEC_INFO("%s: command %04x", __func__, cmd); + + mutex_lock(&btfmcodec->hwep_drv_lock); + hwep_info = btfmcodec->hwep_info; + if (!hwep_info) { + BTFMCODEC_WARN("%s: HWEP is not registered with btfmcodec", __func__); + BTFMCODEC_WARN("%s: caching required info", __func__); + is_cp_supported = ((int)arg == 1) ? true : false; + mutex_unlock(&btfmcodec->hwep_drv_lock); + return 0; + } + + mutex_unlock(&btfmcodec->hwep_drv_lock); + switch (cmd) { + case BTM_CP_UPDATE: { + if ((int)arg == 1) { + if (!strcmp(hwep_info->driver_name, "btfmslim")) + set_bit(BTADV_AUDIO_MASTER_CONFIG, &hwep_info->flags); + else if (!strcmp(hwep_info->driver_name, "btfmswr_slave")) + set_bit(BTADV_CONFIGURE_DMA, &hwep_info->flags); + BTFMCODEC_INFO("%s: This target support CP hwep %s", + __func__, hwep_info->driver_name); + } else { + clear_bit(BTADV_AUDIO_MASTER_CONFIG, &hwep_info->flags); + clear_bit(BTADV_CONFIGURE_DMA, &hwep_info->flags); + BTFMCODEC_INFO("%s: This target support doesn't CP", __func__); + } + + BTFMCODEC_INFO("%s: mastr %d dma codec %d", __func__, + (int)test_bit(BTADV_AUDIO_MASTER_CONFIG, &hwep_info->flags), + (int)test_bit(BTADV_CONFIGURE_DMA, &hwep_info->flags)); + break; + } default: { + BTFMCODEC_ERR("%s unhandled cmd %04x", __func__, cmd); + } + } + + return 0; +} static const struct file_operations btfmcodec_fops = { .owner = THIS_MODULE, .open = btfmcodec_dev_open, @@ -450,8 +517,8 @@ static const struct file_operations btfmcodec_fops = { .poll = btfmcodec_dev_poll, .read = btfmcodec_dev_read, /* For Now add no hookups for below callbacks */ - .unlocked_ioctl = NULL, - .compat_ioctl = NULL, + .unlocked_ioctl = btfmcodec_ioctl, + .compat_ioctl = btfmcodec_ioctl, }; static ssize_t btfmcodec_attributes_store(struct device *dev, @@ -503,6 +570,7 @@ static int __init btfmcodec_init(void) return -ENOMEM; } + mutex_init(&btfmcodec->hwep_drv_lock); states = &btfmcodec->states; states->current_state = IDLE; states->next_state = IDLE; diff --git a/btfmcodec/btfm_codec_hw_interface.c b/btfmcodec/btfm_codec_hw_interface.c index 091fe190cd..97d4d7d4ca 100644 --- a/btfmcodec/btfm_codec_hw_interface.c +++ b/btfmcodec/btfm_codec_hw_interface.c @@ -6,17 +6,14 @@ #include "btfm_codec_hw_interface.h" #include "btfm_codec_interface.h" -struct mutex hwep_drv_lock; - int btfmcodec_register_hw_ep (struct hwep_data *ep_info) { struct btfmcodec_data *btfmcodec; struct hwep_data *hwep_info; int ret = 0; - // ToDo Check wether we need mutex_init api - mutex_lock(&hwep_drv_lock); btfmcodec = btfm_get_btfmcodec(); + mutex_lock(&btfmcodec->hwep_drv_lock); if (!btfmcodec) { BTFMCODEC_ERR("btfm codec driver it not initialized"); ret = -EPERM; @@ -54,7 +51,7 @@ int btfmcodec_register_hw_ep (struct hwep_data *ep_info) &hwep_info->flags)); ret = btfm_register_codec(hwep_info); end: - mutex_unlock(&hwep_drv_lock); + mutex_unlock(&btfmcodec->hwep_drv_lock); return ret; } @@ -64,8 +61,8 @@ int btfmcodec_unregister_hw_ep (char *driver_name) struct hwep_data *hwep_info; int ret; - mutex_lock(&hwep_drv_lock); btfmcodec = btfm_get_btfmcodec(); + mutex_lock(&btfmcodec->hwep_drv_lock); if (!btfmcodec) { BTFMCODEC_ERR("btfm codec driver it not initialized"); ret = -EPERM; @@ -91,7 +88,7 @@ int btfmcodec_unregister_hw_ep (char *driver_name) goto end; } end: - mutex_unlock(&hwep_drv_lock); + mutex_unlock(&btfmcodec->hwep_drv_lock); return ret; } diff --git a/btfmcodec/btfm_codec_interface.c b/btfmcodec/btfm_codec_interface.c index eea4cf04bd..d8139d482b 100644 --- a/btfmcodec/btfm_codec_interface.c +++ b/btfmcodec/btfm_codec_interface.c @@ -439,7 +439,7 @@ static int btfmcodec_configure_master(struct btfmcodec_data *btfmcodec, uint8_t struct btfmcodec_char_device *btfmcodec_dev = btfmcodec->btfmcodec_dev; struct hwep_data *hwep_info = btfmcodec->hwep_info; struct master_hwep_configurations hwep_configs; - struct btm_master_config_req config_reg; + struct btm_master_config_req config_req; struct hwep_dai_driver *dai_drv = (struct hwep_dai_driver *) btfmcodec_get_dai_drvdata(hwep_info); wait_queue_head_t *rsp_wait_q = @@ -456,28 +456,28 @@ static int btfmcodec_configure_master(struct btfmcodec_data *btfmcodec, uint8_t } BTFMCODEC_INFO("framing packet for %d", id); - config_reg.opcode = BTM_BTFMCODEC_MASTER_CONFIG_REQ; - config_reg.len = BTM_MASTER_CONFIG_REQ_LEN; - config_reg.stream_id = hwep_configs.stream_id; - config_reg.device_id = hwep_configs.device_id; - config_reg.sample_rate = hwep_configs.sample_rate; - config_reg.bit_width = hwep_configs.bit_width; - config_reg.num_channels = hwep_configs.num_channels; - config_reg.channel_num = hwep_configs.chan_num; - config_reg.codec_id = hwep_configs.codectype; + config_req.opcode = BTM_BTFMCODEC_MASTER_CONFIG_REQ; + config_req.len = BTM_MASTER_CONFIG_REQ_LEN; + config_req.stream_id = hwep_configs.stream_id; + config_req.device_id = hwep_configs.device_id; + config_req.sample_rate = hwep_configs.sample_rate; + config_req.bit_width = hwep_configs.bit_width; + config_req.num_channels = hwep_configs.num_channels; + config_req.channel_num = hwep_configs.chan_num; + config_req.codec_id = hwep_configs.codectype; BTFMCODEC_DBG("================================================\n"); - BTFMCODEC_DBG("config_reg.len :%d", config_reg.len); - BTFMCODEC_DBG("config_reg.stream_id :%d", config_reg.stream_id); - BTFMCODEC_DBG("config_reg.device_id :%d", config_reg.device_id); - BTFMCODEC_DBG("config_reg.sample_rate :%d", config_reg.sample_rate); - BTFMCODEC_DBG("config_reg.bit_width :%d", config_reg.bit_width); - BTFMCODEC_DBG("config_reg.num_channels :%d", config_reg.num_channels); - BTFMCODEC_DBG("config_reg.channel_num :%d", config_reg.channel_num); - BTFMCODEC_DBG("config_reg.codec_id :%d", config_reg.codec_id); + BTFMCODEC_DBG("dma_config_req.len :%d", config_req.len); + BTFMCODEC_DBG("dma_config_req.stream_id :%d", config_req.stream_id); + BTFMCODEC_DBG("dma_config_req.device_id :%d", config_req.device_id); + BTFMCODEC_DBG("dma_config_req.sample_rate :%d", config_req.sample_rate); + BTFMCODEC_DBG("dma_config_req.bit_width :%d", config_req.bit_width); + BTFMCODEC_DBG("dma_config_req.num_channels :%d", config_req.num_channels); + BTFMCODEC_DBG("dma_config_req.channel_num :%d", config_req.channel_num); + BTFMCODEC_DBG("dma_config_req.codec_id :%d", config_req.codec_id); BTFMCODEC_DBG("================================================\n"); /* See if we need to protect below with lock */ *status = BTM_WAITING_RSP; - btfmcodec_dev_enqueue_pkt(btfmcodec_dev, &config_reg, (config_reg.len + + btfmcodec_dev_enqueue_pkt(btfmcodec_dev, &config_req, (config_req.len + BTM_HEADER_LEN)); ret = wait_event_interruptible_timeout(*rsp_wait_q, (*status) != BTM_WAITING_RSP, @@ -496,6 +496,73 @@ static int btfmcodec_configure_master(struct btfmcodec_data *btfmcodec, uint8_t return ret; } +static int btfmcodec_configure_dma(struct btfmcodec_data *btfmcodec, uint8_t id) +{ + struct btfmcodec_char_device *btfmcodec_dev = btfmcodec->btfmcodec_dev; + struct hwep_data *hwep_info = btfmcodec->hwep_info; + struct hwep_dma_configurations dma_config; + struct btm_dma_config_req dma_config_req; + struct hwep_dai_driver *dai_drv = (struct hwep_dai_driver *) + btfmcodec_get_dai_drvdata(hwep_info); + wait_queue_head_t *rsp_wait_q = + &btfmcodec_dev->rsp_wait_q[BTM_PKT_TYPE_DMA_CONFIG_RSP]; + uint8_t *status = &btfmcodec_dev->status[BTM_PKT_TYPE_DMA_CONFIG_RSP]; + int ret = 0; + + if (dai_drv && dai_drv->dai_ops && dai_drv->dai_ops->hwep_get_configs) { + dai_drv->dai_ops->hwep_get_configs((void *)btfmcodec->hwep_info, + &dma_config, id); + } else { + BTFMCODEC_ERR("No hwep_get_configs is set by hw ep driver"); + return -1; + } + + BTFMCODEC_INFO("framing packet for %d", id); + dma_config_req.opcode = BTM_BTFMCODEC_CODEC_CONFIG_DMA_REQ; + dma_config_req.len = BTM_CODEC_CONFIG_DMA_REQ_LEN; + dma_config_req.stream_id = dma_config.stream_id; + dma_config_req.sample_rate = dma_config.sample_rate; + dma_config_req.bit_width = dma_config.bit_width; + dma_config_req.num_channels = dma_config.num_channels; + dma_config_req.codec_id = dma_config.codectype; + dma_config_req.lpaif = dma_config.lpaif; + dma_config_req.inf_index = dma_config.inf_index; + dma_config_req.active_channel_mask = dma_config.active_channel_mask; + + BTFMCODEC_DBG("================================================\n"); + BTFMCODEC_DBG("dma_config_req.len :%d", dma_config_req.len); + BTFMCODEC_DBG("dma_config_req.stream_id :%d", dma_config_req.stream_id); + BTFMCODEC_DBG("dma_config_req.sample_rate :%d", dma_config_req.sample_rate); + BTFMCODEC_DBG("dma_config_req.bit_width :%d", dma_config_req.bit_width); + BTFMCODEC_DBG("dma_config_req.num_channels :%d", dma_config_req.num_channels); + BTFMCODEC_DBG("dma_config_req.codec_id :%d", dma_config_req.codec_id); + BTFMCODEC_DBG("dma_config_req.lpaif :%d", dma_config_req.lpaif); + BTFMCODEC_DBG("dma_config_req.inf_index :%d", dma_config_req.inf_index); + BTFMCODEC_DBG("dma_config_req.active_channel_mask :%d", dma_config_req.active_channel_mask); + BTFMCODEC_DBG("================================================\n"); + + *status = BTM_WAITING_RSP; + btfmcodec_dev_enqueue_pkt(btfmcodec_dev, &dma_config_req, (dma_config_req.len + + BTM_HEADER_LEN)); + + ret = wait_event_interruptible_timeout(*rsp_wait_q, + (*status) != BTM_WAITING_RSP, + msecs_to_jiffies(BTM_MASTER_DMA_CONFIG_RSP_TIMEOUT)); + + if (ret == 0) { + BTFMCODEC_ERR("failed to recevie response from BTADV audio Manager"); + ret = -ETIMEDOUT; + } else { + if (*status == BTM_RSP_RECV) + return 0; + else if (*status == BTM_FAIL_RESP_RECV || + *status == BTM_RSP_NOT_RECV_CLIENT_KILLED) + return -1; + } + + return ret; +} + int btfmcodec_hwep_prepare(struct btfmcodec_data *btfmcodec, uint32_t sampling_rate, uint32_t direction, int id) { @@ -508,14 +575,20 @@ int btfmcodec_hwep_prepare(struct btfmcodec_data *btfmcodec, uint32_t sampling_r if (dai_drv && dai_drv->dai_ops && dai_drv->dai_ops->hwep_prepare) { ret = dai_drv->dai_ops->hwep_prepare((void *)hwep_info, sampling_rate, direction, id); + BTFMCODEC_ERR("%s: hwep info %d", __func__, hwep_info->flags); if (ret == 0 && test_bit(BTADV_AUDIO_MASTER_CONFIG, &hwep_info->flags)) { ret = btfmcodec_configure_master(btfmcodec, (uint8_t)id); if (ret < 0) { BTFMCODEC_ERR("failed to configure master error %d", ret); - /* close slave port and reset the state*/ btfmcodec_set_current_state(state, IDLE); - /* we don't need to do shutdown, ASOC is doing it */ -// btfmcodec_hwep_shutdown(btfmcodec, id); + } else { + btfmcodec_set_current_state(state, BT_Connected); + } + } else if (ret == 0 && test_bit(BTADV_CONFIGURE_DMA, &hwep_info->flags)) { + ret = btfmcodec_configure_dma(btfmcodec, (uint8_t)id); + if (ret < 0) { + BTFMCODEC_ERR("failed to configure Codec DMA %d", ret); + btfmcodec_set_current_state(state, IDLE); } else { btfmcodec_set_current_state(state, BT_Connected); } @@ -785,6 +858,17 @@ int btfm_register_codec(struct hwep_data *hwep_info) BTFMCODEC_INFO("btfmcodec_wq_prepare_bearer:%p", btfmcodec_wq_prepare_bearer); BTFMCODEC_INFO("btfmcodec_wq_hwep_shutdown:%p", btfmcodec_wq_hwep_shutdown); + if (isCpSupported()) { + if (!strcmp(hwep_info->driver_name, "btfmslim")) + set_bit(BTADV_AUDIO_MASTER_CONFIG, &hwep_info->flags); + else if (!strcmp(hwep_info->driver_name, "btfmswr_slave")) + set_bit(BTADV_CONFIGURE_DMA, &hwep_info->flags); + + BTFMCODEC_INFO("%s: master %d dma codec %d", __func__, + (int)test_bit(BTADV_AUDIO_MASTER_CONFIG, &hwep_info->flags), + (int)test_bit(BTADV_CONFIGURE_DMA, &hwep_info->flags)); + } + return ret; } diff --git a/btfmcodec/include/btfm_codec.h b/btfmcodec/include/btfm_codec.h index 88c7117997..563e557817 100644 --- a/btfmcodec/include/btfm_codec.h +++ b/btfmcodec/include/btfm_codec.h @@ -33,6 +33,7 @@ static uint8_t log_lvl = BTM_BTFMCODEC_DEFAULT_LOG_LVL; } #define DEVICE_NAME_MAX_LEN 64 +#define BTM_CP_UPDATE 0xbfaf typedef enum btfmcodec_states { /*Default state of kernel proxy driver */ @@ -54,6 +55,7 @@ enum btfm_pkt_type { BTM_PKT_TYPE_BEARER_SWITCH_IND, BTM_PKT_TYPE_HWEP_SHUTDOWN, BTM_PKT_TYPE_HWEP_CONFIG, + BTM_PKT_TYPE_DMA_CONFIG_RSP, BTM_PKT_TYPE_MAX, }; @@ -98,7 +100,9 @@ struct btfmcodec_data { struct hwep_data *hwep_info; struct list_head config_head; struct adsp_notifier notifier; + struct mutex hwep_drv_lock; }; struct btfmcodec_data *btfm_get_btfmcodec(void); +bool isCpSupported(void); #endif /*__LINUX_BTFM_CODEC_H */ diff --git a/btfmcodec/include/btfm_codec_hw_interface.h b/btfmcodec/include/btfm_codec_hw_interface.h index bf8e2e6790..3474a6495b 100644 --- a/btfmcodec/include/btfm_codec_hw_interface.h +++ b/btfmcodec/include/btfm_codec_hw_interface.h @@ -18,6 +18,7 @@ * responsible to configure master. */ #define BTADV_AUDIO_MASTER_CONFIG 0 +#define BTADV_CONFIGURE_DMA 1 #define DEVICE_NAME_MAX_LEN 64 struct hwep_configurations { @@ -41,6 +42,18 @@ struct master_hwep_configurations { uint8_t codectype; uint16_t direction; }; + +struct hwep_dma_configurations { + uint8_t stream_id; + uint32_t sample_rate; + uint8_t bit_width; + uint8_t num_channels; + uint8_t codectype; + uint8_t lpaif; // Low power audio interface + uint8_t inf_index; // interface index + uint8_t active_channel_mask; +}; + struct hwep_comp_drv { int (*hwep_probe) (struct snd_soc_component *); void (*hwep_remove) (struct snd_soc_component *); @@ -58,8 +71,7 @@ struct hwep_dai_ops { unsigned int, unsigned int *); int (*hwep_get_channel_map)(void *, unsigned int *, unsigned int *, unsigned int *, unsigned int *, int); - int (*hwep_get_configs)(void *, struct master_hwep_configurations *, - uint8_t); + int (*hwep_get_configs)(void *a, void *b, uint8_t c); uint8_t *hwep_codectype; }; diff --git a/btfmcodec/include/btfm_codec_pkt.h b/btfmcodec/include/btfm_codec_pkt.h index e6b1f8c67b..ccf7ef4ddb 100644 --- a/btfmcodec/include/btfm_codec_pkt.h +++ b/btfmcodec/include/btfm_codec_pkt.h @@ -38,6 +38,9 @@ struct btm_ctrl_pkt { #define BTM_BTFMCODEC_MASTER_CONFIG_RSP 0x50000003 #define BTM_BTFMCODEC_MASTER_SHUTDOWN_REQ 0x50000004 #define BTM_BTFMCODEC_CTRL_MASTER_SHUTDOWN_RSP 0x50000005 +#define BTM_BTFMCODEC_CODEC_CONFIG_DMA_REQ 0x58000006 +#define BTM_BTFMCODEC_CODEC_CONFIG_DMA_RSP 0x58000007 + #define BTM_BTFMCODEC_BEARER_SWITCH_IND 0x58000001 #define BTM_BTFMCODEC_TRANSPORT_SWITCH_FAILED_IND 0x58000002 #define BTM_BTFMCODEC_ADSP_STATE_IND 0x58000003 @@ -45,14 +48,17 @@ struct btm_ctrl_pkt { #define BTM_MASTER_CONFIG_REQ_LEN 13 #define BTM_MASTER_CONFIG_RSP_TIMEOUT 5000 +#define BTM_MASTER_DMA_CONFIG_RSP_TIMEOUT 5000 #define BTM_HEADER_LEN 8 #define BTM_PREPARE_AUDIO_BEARER_SWITCH_RSP_LEN 2 #define BTM_MASTER_CONFIG_RSP_LEN 2 +#define BTM_CODEC_CONFIG_DMA_RSP_LEN 2 #define BTM_MASTER_SHUTDOWN_REQ_LEN 1 #define BTM_PREPARE_AUDIO_BEARER_SWITCH_REQ_LEN 1 #define BTM_BEARER_SWITCH_IND_LEN 1 #define BTM_LOG_LVL_IND_LEN 1 #define BTM_ADSP_STATE_IND_LEN 4 +#define BTM_CODEC_CONFIG_DMA_REQ_LEN 11 #define BTM_BTFMCODEC_USECASE_START_IND 0x58000008 #define BTM_USECASE_START_IND_LEN 1 @@ -81,6 +87,7 @@ enum btfm_kp_status { MSG_FAILED_TO_SHUTDOWN_HWEP, MSG_ERR_WHILE_SHUTING_DOWN_HWEP, }; + struct btm_master_config_req { btm_opcode opcode; uint32_t len; @@ -93,6 +100,18 @@ struct btm_master_config_req { uint8_t codec_id; }__attribute__((packed)); +struct btm_dma_config_req { + btm_opcode opcode; + uint32_t len; + uint8_t stream_id; + uint32_t sample_rate; + uint8_t bit_width; + uint8_t num_channels; + uint8_t codec_id; + uint8_t lpaif; // Low power audio interface + uint8_t inf_index; // interface index + uint8_t active_channel_mask; +} __packed; struct btm_usecase_start_ind { btm_opcode opcode; diff --git a/slimbus/btfm_slim_hw_interface.c b/slimbus/btfm_slim_hw_interface.c index 0a2eeea973..9886307f2d 100644 --- a/slimbus/btfm_slim_hw_interface.c +++ b/slimbus/btfm_slim_hw_interface.c @@ -397,16 +397,17 @@ static int btfm_slim_dai_get_channel_map(void *dai, return 0; } -int btfm_slim_dai_get_configs (void * dai, - struct master_hwep_configurations *hwep_config, - uint8_t id) +int btfm_slim_dai_get_configs(void *dai, void *config, uint8_t id) { struct hwep_data *hwep_info = (struct hwep_data *)dai; struct btfmslim *btfmslim = dev_get_drvdata(hwep_info->dev); + struct master_hwep_configurations *hwep_config; struct btfmslim_ch *ch = NULL; int i = 0; BTFMSLIM_DBG(""); + + hwep_config = (struct master_hwep_configurations *) config; hwep_config->stream_id = id; hwep_config->device_id = btfmslim->device_id; hwep_config->sample_rate = btfmslim->sample_rate; @@ -436,6 +437,7 @@ int btfm_slim_dai_get_configs (void * dai, return 1; } + static struct hwep_dai_ops btfmslim_hw_dai_ops = { .hwep_startup = btfm_slim_dai_startup, .hwep_shutdown = btfm_slim_dai_shutdown, @@ -519,7 +521,6 @@ int btfm_slim_register_hw_ep(struct btfmslim *btfm_slim) hwep_info->num_dai = 2; hwep_info->num_mixer_ctrl = ARRAY_SIZE(status_controls); hwep_info->mixer_ctrl = status_controls; - set_bit(BTADV_AUDIO_MASTER_CONFIG, &hwep_info->flags); /* Register to hardware endpoint */ ret = btfmcodec_register_hw_ep(hwep_info); if (ret) { diff --git a/soundwire/Makefile b/soundwire/Makefile index 04eb9a25e3..db71d25bba 100644 --- a/soundwire/Makefile +++ b/soundwire/Makefile @@ -1,3 +1,3 @@ ccflags-y += -I$(BT_ROOT)/include -bt_fm_swr-objs := btfm_swr.o btfm_swr_codec.o btfm_swr_slave.o +bt_fm_swr-objs := btfm_swr.o btfm_swr_hw_interface.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 index c40271d5de..3b4d049a16 100644 --- a/soundwire/btfm_swr.c +++ b/soundwire/btfm_swr.c @@ -21,6 +21,7 @@ #include #include "btpower.h" #include "btfm_swr.h" +#include "btfm_swr_hw_interface.h" #include "btfm_swr_slave.h" struct class *btfm_swr_class; @@ -205,7 +206,7 @@ static int btfm_swr_probe(struct swr_device *pdev) pbtfmswr->initialized = false; // register with ALSA - ret = btfm_swr_register_codec(pbtfmswr); + ret = btfm_swr_register_hw_ep(pbtfmswr); if (ret) { BTFMSWR_ERR("registration with ALSA failed, returning"); goto dealloc; @@ -239,8 +240,7 @@ class_err: unregister_chrdev(btfm_swr_major, "btfm_swr"); register_err: - btfm_swr_unregister_codec(pbtfmswr->dev); - + btfm_swr_unregister_hwep(); dealloc: kfree(pbtfmswr); return ret; diff --git a/soundwire/btfm_swr.h b/soundwire/btfm_swr.h index a1e9b7ab86..d28dab04c1 100644 --- a/soundwire/btfm_swr.h +++ b/soundwire/btfm_swr.h @@ -83,21 +83,4 @@ int btfm_swr_enable_port(u8 port_num, u8 ch_count, u32 sample_rate, 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 deleted file mode 100644 index e0fcd363a1..0000000000 --- a/soundwire/btfm_swr_codec.c +++ /dev/null @@ -1,341 +0,0 @@ -// 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_hw_interface.c b/soundwire/btfm_swr_hw_interface.c new file mode 100644 index 0000000000..200c4e7016 --- /dev/null +++ b/soundwire/btfm_swr_hw_interface.c @@ -0,0 +1,440 @@ +// 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" +#include "btfm_swr_hw_interface.h" +#include "btfm_codec_hw_interface.h" + +#define LPAIF_AUD 0x05 + +static int bt_soc_enable_status; +int btfm_feedback_ch_setting; +static uint8_t usecase_codec; + +static int btfm_swr_hwep_write(struct snd_soc_component *codec, + unsigned int reg, unsigned int value) +{ + BTFMSWR_DBG(""); + return 0; +} + +static unsigned int btfm_swr_hwep_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 int btfm_get_codec_type(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + BTFMSWR_DBG("current codec type:%s", codec_text[usecase_codec]); + ucontrol->value.integer.value[0] = usecase_codec; + return 1; +} + +static int btfm_put_codec_type(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + usecase_codec = ucontrol->value.integer.value[0]; + BTFMSWR_DBG("codec type set to:%s", codec_text[usecase_codec]); + return 1; +} + +static 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), + SOC_ENUM_EXT("BT codec type", codec_display, + btfm_get_codec_type, btfm_put_codec_type), +}; + + +static int btfm_swr_hwep_probe(struct snd_soc_component *codec) +{ + BTFMSWR_DBG(""); + return 0; +} + +static void btfm_swr_hwep_remove(struct snd_soc_component *codec) +{ + BTFMSWR_DBG(""); +} + +static int btfm_swr_dai_startup(void *dai) +{ + //struct hwep_data *hwep_info = (struct hwep_data *)dai; + int ret = -1; + + BTFMSWR_DBG(""); + + ret = btfm_swr_hw_init(); + return ret; +} + +static void btfm_swr_dai_shutdown(void *dai, int id) +{ + struct hwep_data *hwep_info = (struct hwep_data *)dai; + struct btfmswr *btfmswr = dev_get_drvdata(hwep_info->dev); + int ret = 0; + u8 port_type; + + BTFMSWR_INFO(""); + + if (btfmswr == NULL) + BTFMSWR_INFO("btfmswr is NULL\n"); + + switch (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", id); + return; + } + + ret = btfm_swr_disable_port(btfmswr->p_dai_port->port_info[id].port, + btfmswr->num_channels, port_type); +} + +static int btfm_swr_dai_hw_params(void *dai, uint32_t bps, + uint32_t direction, uint8_t num_channels) +{ + struct hwep_data *hwep_info = (struct hwep_data *)dai; + struct btfmswr *btfmswr = dev_get_drvdata(hwep_info->dev); + + BTFMSWR_DBG(""); + btfmswr->bps = bps; + btfmswr->direction = direction; + btfmswr->num_channels = num_channels; + + return 0; +} + +void btfm_get_sampling_rate(uint32_t *sampling_rate) +{ + uint8_t codec_types_avb = ARRAY_SIZE(codec_text); + + if (usecase_codec > (codec_types_avb - 1)) { + BTFMSWR_ERR("falling back to use default sampling_rate: %u", + *sampling_rate); + return; + } + + if (*sampling_rate == 44100 || *sampling_rate == 48000) { + if (usecase_codec == LDAC || + usecase_codec == APTX_AD) + *sampling_rate = (*sampling_rate) * 2; + } + + if (usecase_codec == LC3_VOICE || + usecase_codec == APTX_AD_SPEECH || + usecase_codec == LC3 || usecase_codec == APTX_AD_QLEA || + usecase_codec == APTX_AD_R4) { + *sampling_rate = 96000; + } + + BTFMSWR_INFO("current usecase codec type %s and sampling rate:%u khz", + codec_text[usecase_codec], *sampling_rate); +} + +static int btfm_swr_dai_prepare(void *dai, uint32_t sampling_rate, uint32_t direction, int id) +{ + struct hwep_data *hwep_info = (struct hwep_data *)dai; + struct btfmswr *btfmswr = dev_get_drvdata(hwep_info->dev); + int ret = -EINVAL; + u8 port_type; + + bt_soc_enable_status = 0; + BTFMSWR_INFO("dai->id: %d, dai->rate: %d direction: %d", id, sampling_rate, direction); + + btfm_get_sampling_rate(&sampling_rate); + btfmswr->sample_rate = sampling_rate; + + switch (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", id); + return -EINVAL; + } + + ret = btfm_swr_enable_port(btfmswr->p_dai_port->port_info[id].port, + btfmswr->num_channels, sampling_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(void *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(void *dai, + unsigned int *tx_num, unsigned int *tx_slot, + unsigned int *rx_num, unsigned int *rx_slot, int id) +{ + + struct hwep_data *hwep_info = (struct hwep_data *)dai; + struct btfmswr *btfmswr = dev_get_drvdata(hwep_info->dev); + + *rx_slot = 0; + *tx_slot = 0; + *rx_num = 0; + *tx_num = 0; + + switch (id) { + case FMAUDIO_TX: + case BTAUDIO_TX: + case BTAUDIO_A2DP_SINK_TX: + *tx_num = btfmswr->num_channels; + *tx_slot = btfmswr->num_channels == 2 ? TWO_CHANNEL_MASK : ONE_CHANNEL_MASK; + break; + case BTAUDIO_RX: + *rx_num = btfmswr->num_channels; + *rx_slot = btfmswr->num_channels == 2 ? TWO_CHANNEL_MASK : ONE_CHANNEL_MASK; + break; + + default: + BTFMSWR_ERR("Unsupported DAI %d", id); + return -EINVAL; + } + + return 0; +} + +int btfm_swr_dai_get_configs(void *dai, void *config, uint8_t id) +{ + struct hwep_data *hwep_info = (struct hwep_data *)dai; + struct btfmswr *btfmswr = dev_get_drvdata(hwep_info->dev); + struct hwep_dma_configurations *hwep_config; + + BTFMSWR_DBG(""); + hwep_config = (struct hwep_dma_configurations *)config; + + hwep_config->stream_id = id; + hwep_config->sample_rate = btfmswr->sample_rate; + hwep_config->bit_width = (uint8_t)btfmswr->bps; + hwep_config->codectype = usecase_codec; + + hwep_config->num_channels = btfmswr->num_channels; + hwep_config->active_channel_mask = (btfmswr->num_channels == 2 ? + TWO_CHANNEL_MASK : ONE_CHANNEL_MASK); + hwep_config->lpaif = LPAIF_AUD; + hwep_config->inf_index = 1; + return 1; +} + +static struct hwep_dai_ops btfmswr_hw_dai_ops = { + .hwep_startup = btfm_swr_dai_startup, + .hwep_shutdown = btfm_swr_dai_shutdown, + .hwep_hw_params = btfm_swr_dai_hw_params, + .hwep_prepare = btfm_swr_dai_prepare, + .hwep_set_channel_map = btfm_swr_dai_set_channel_map, + .hwep_get_channel_map = btfm_swr_dai_get_channel_map, + .hwep_get_configs = btfm_swr_dai_get_configs, + .hwep_codectype = &usecase_codec, +}; + +static struct hwep_dai_driver btfmswr_dai_driver[] = { + { /* FM Audio data multiple channel : FM -> lpass */ + .dai_name = "btaudio_fm_tx", + .id = FMAUDIO_TX, + .capture = { + .stream_name = "FM SWR 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, + }, + .dai_ops = &btfmswr_hw_dai_ops, + }, + { /* Bluetooth SCO voice uplink: bt -> lpass */ + .dai_name = "btaudio_tx", + .id = BTAUDIO_TX, + .capture = { + .stream_name = "BT Audio SWR Tx Capture", + /* 8 KHz or 16 KHz */ + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 + | SNDRV_PCM_RATE_8000_192000 + | 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, + }, + .dai_ops = &btfmswr_hw_dai_ops, + }, + { /* Bluetooth SCO voice downlink: lpass -> bt or A2DP Playback */ + .dai_name = "btaudio_rx", + .id = BTAUDIO_RX, + .playback = { + .stream_name = "BT Audio SWR Rx Playback", + /* 8/16/44.1/48/88.2/96 Khz */ + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 + | SNDRV_PCM_RATE_8000_192000 + | 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, + }, + .dai_ops = &btfmswr_hw_dai_ops, + }, + { /* Bluetooth A2DP sink: bt -> lpass */ + .dai_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, + }, + .dai_ops = &btfmswr_hw_dai_ops, + } +}; + +static struct hwep_comp_drv btfmswr_hw_driver = { + .hwep_probe = btfm_swr_hwep_probe, + .hwep_remove = btfm_swr_hwep_remove, + .hwep_read = btfm_swr_hwep_read, + .hwep_write = btfm_swr_hwep_write, +}; + +int btfm_swr_register_hw_ep(struct btfmswr *btfm_swr) +{ + struct device *dev = btfm_swr->dev; + struct hwep_data *hwep_info; + int ret = 0; + + BTFMSWR_INFO("Registering with BTFMCODEC HWEP interface\n"); + hwep_info = kzalloc(sizeof(struct hwep_data), GFP_KERNEL); + + if (!hwep_info) { + BTFMSWR_ERR("%s: failed to allocate memory\n", __func__); + ret = -ENOMEM; + goto end; + } + + /* Copy EP device parameters as intercations will be on the same device */ + hwep_info->dev = dev; + strscpy(hwep_info->driver_name, SWR_SLAVE_COMPATIBLE_STR, DEVICE_NAME_MAX_LEN); + hwep_info->drv = &btfmswr_hw_driver; + hwep_info->dai_drv = btfmswr_dai_driver; + hwep_info->num_dai = ARRAY_SIZE(btfmswr_dai_driver); + hwep_info->num_dai = 4; + hwep_info->num_mixer_ctrl = ARRAY_SIZE(status_controls); + hwep_info->mixer_ctrl = status_controls; + /* Register to hardware endpoint */ + ret = btfmcodec_register_hw_ep(hwep_info); + if (ret) { + BTFMSWR_ERR("failed to register with btfmcodec driver hw interface (%d)", ret); + goto end; + } + + BTFMSWR_INFO("Registered succesfull with BTFMCODEC HWEP interface\n"); + return ret; +end: + return ret; +} + +void btfm_swr_unregister_hwep(void) +{ + BTFMSWR_INFO("Unregistered with BTFMCODEC HWEP interface"); + /* Unregister with BTFMCODEC HWEP driver */ + btfmcodec_unregister_hw_ep(SWR_SLAVE_COMPATIBLE_STR); + +} + +MODULE_DESCRIPTION("BTFM SoundWire Codec driver"); +MODULE_LICENSE("GPL v2"); diff --git a/soundwire/btfm_swr_hw_interface.h b/soundwire/btfm_swr_hw_interface.h new file mode 100644 index 0000000000..60b2ff7557 --- /dev/null +++ b/soundwire/btfm_swr_hw_interface.h @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#ifndef __LINUX_BTFM_SWR_HW_INTERFACE_H +#define __LINUX_BTFM_SWR_HW_INTERFACE_H + +int btfm_swr_register_hw_ep(struct btfmswr *a); +void btfm_swr_unregister_hwep(void); + +enum Codec { + SBC = 0, + AAC, + LDAC, + APTX, + APTX_HD, + APTX_AD, + LC3, + APTX_AD_SPEECH, + LC3_VOICE, + APTX_AD_QLEA, + APTX_AD_R4, + NO_CODEC +}; + +static const char * const codec_text[] = {"CODEC_TYPE_SBC", "CODEC_TYPE_AAC", + "CODEC_TYPE_LDAC", "CODEC_TYPE_APTX", + "CODEC_TYPE_APTX_HD", "CODEC_TYPE_APTX_AD", + "CODEC_TYPE_LC3", "CODEC_TYPE_APTX_AD_SPEECH", + "CODEC_TYPE_LC3_VOICE", "CODEC_TYPE_APTX_AD_QLEA", + "CODEC_TYPE_APTX_AD_R4", "CODEC_TYPE_INVALID"}; + +static SOC_ENUM_SINGLE_EXT_DECL(codec_display, codec_text); +#endif /*__LINUX_BTFM_SWR_HW_INTERFACE_H*/