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:
@@ -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(
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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 */
|
||||
|
@@ -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;
|
||||
};
|
||||
|
||||
|
@@ -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;
|
||||
|
@@ -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) {
|
||||
|
@@ -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
|
||||
|
@@ -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;
|
||||
|
@@ -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 */
|
||||
|
@@ -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");
|
440
soundwire/btfm_swr_hw_interface.c
Normal file
440
soundwire/btfm_swr_hw_interface.c
Normal 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");
|
35
soundwire/btfm_swr_hw_interface.h
Normal file
35
soundwire/btfm_swr_hw_interface.h
Normal 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*/
|
Reference in New Issue
Block a user