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 <quic_bgodavar@quicinc.com>
This commit is contained in:
Balakrishna Godavarthi
2023-07-05 11:55:04 +05:30
parent 7bd1a061bb
commit 897ecff742
14 changed files with 717 additions and 402 deletions

View File

@@ -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(

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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 */

View File

@@ -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;
};

View File

@@ -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;

View File

@@ -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) {

View File

@@ -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

View File

@@ -21,6 +21,7 @@
#include <sound/tlv.h>
#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;

View File

@@ -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 */

View File

@@ -1,341 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#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");

View File

@@ -0,0 +1,440 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#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");

View File

@@ -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*/