btfmcodec: Add btadv audio support

This change adds below support for BT advance audio
* Add support for codec type mixer control
* Update slim driver to read master id, channel number.
* Update slim driver to support HW EP.
* Add support for transport switch based on the request

CRs-Fixed: 3298745
Change-Id: Ica349cb6f3615f4dc51bbc3070c90d43eeba1cdd
This commit is contained in:
Balakrishna Godavarthi
2023-01-02 12:06:09 +05:30
parent 30209d05c3
commit 4a2649332e
13 changed files with 1276 additions and 156 deletions

View File

@@ -1,4 +1,4 @@
ccflags-y += -I$(BT_ROOT)/include ccflags-y += -I$(BT_ROOT)/include
ccflags-y += -I$(BT_ROOT)/btfmcodec/include ccflags-y += -I$(BT_ROOT)/btfmcodec/include
btfmcodec-objs := btfm_codec.o btfm_codec_hw_interface.o btfm_codec_interface.o btfmcodec-objs := btfm_codec.o btfm_codec_hw_interface.o btfm_codec_interface.o btfm_codec_btadv_interface.o
obj-$(CONFIG_BTFM_CODEC) += btfmcodec.o obj-$(CONFIG_BTFM_CODEC) += btfmcodec.o

View File

@@ -24,7 +24,7 @@ struct btfmcodec_data *btfmcodec;
struct device_driver driver = {.name = "btfmcodec-driver", .owner = THIS_MODULE}; struct device_driver driver = {.name = "btfmcodec-driver", .owner = THIS_MODULE};
struct btfmcodec_char_device *btfmcodec_dev; struct btfmcodec_char_device *btfmcodec_dev;
#define cdev_to_btfmchardev(_cdev) container_of(_cdev, struct btfmcodec_char_device, cdev) #define cdev_to_btfmchardev(_cdev) container_of(_cdev, struct btfmcodec_char_device, cdev)
#define MIN_PKT_LEN 0x3 #define MIN_PKT_LEN 0x9
char *coverttostring(enum btfmcodec_states state) { char *coverttostring(enum btfmcodec_states state) {
switch (state) { switch (state) {
@@ -63,15 +63,15 @@ static int btfmcodec_dev_open(struct inode *inode, struct file *file)
{ {
struct btfmcodec_char_device *btfmcodec_dev = cdev_to_btfmchardev(inode->i_cdev); struct btfmcodec_char_device *btfmcodec_dev = cdev_to_btfmchardev(inode->i_cdev);
struct btfmcodec_data *btfmcodec = (struct btfmcodec_data *)btfmcodec_dev->btfmcodec; struct btfmcodec_data *btfmcodec = (struct btfmcodec_data *)btfmcodec_dev->btfmcodec;
int active_clients = refcount_read(&btfmcodec_dev->active_clients); unsigned int active_clients = refcount_read(&btfmcodec_dev->active_clients);
btfmcodec->states.current_state = IDLE; /* Just a temp*/ btfmcodec->states.current_state = IDLE; /* Just a temp*/
BTFMCODEC_INFO("File mode :%u", file->f_mode); btfmcodec->states.next_state = IDLE;
BTFMCODEC_INFO("for %s by %s:%d active_clients[%d]\n", BTFMCODEC_INFO("for %s by %s:%d active_clients[%d]\n",
btfmcodec_dev->dev_name, current->comm, btfmcodec_dev->dev_name, current->comm,
task_pid_nr(current), refcount_read(&btfmcodec_dev->active_clients)); task_pid_nr(current), refcount_read(&btfmcodec_dev->active_clients));
/* Don't allow a new client if already one is active. */ /* Don't allow a new client if already one is active. */
if (active_clients > 0) { if (active_clients > 1) {
BTFMCODEC_WARN("%s: Not honoring open as other client is active", __func__); BTFMCODEC_WARN("%s: Not honoring open as other client is active", __func__);
return EACCES; return EACCES;
} }
@@ -95,12 +95,12 @@ static int btfmcodec_dev_release(struct inode *inode, struct file *file)
struct btfmcodec_char_device *btfmcodec_dev = cdev_to_btfmchardev(inode->i_cdev); struct btfmcodec_char_device *btfmcodec_dev = cdev_to_btfmchardev(inode->i_cdev);
unsigned long flags; unsigned long flags;
BTFMCODEC_INFO("for %s by %s:%d active_clients[%d]\n", BTFMCODEC_INFO("for %s by %s:%d active_clients[%u]\n",
btfmcodec_dev->dev_name, current->comm, btfmcodec_dev->dev_name, current->comm,
task_pid_nr(current), refcount_read(&btfmcodec_dev->active_clients)); task_pid_nr(current), refcount_read(&btfmcodec_dev->active_clients));
refcount_dec(&btfmcodec_dev->active_clients); refcount_dec(&btfmcodec_dev->active_clients);
if (refcount_read(&btfmcodec_dev->active_clients) == 0) { if (refcount_read(&btfmcodec_dev->active_clients) == 1) {
spin_lock_irqsave(&btfmcodec_dev->tx_queue_lock, flags); spin_lock_irqsave(&btfmcodec_dev->tx_queue_lock, flags);
skb_queue_purge(&btfmcodec_dev->txq); skb_queue_purge(&btfmcodec_dev->txq);
/* Wakeup the device if waiting for the data */ /* Wakeup the device if waiting for the data */
@@ -110,42 +110,93 @@ static int btfmcodec_dev_release(struct inode *inode, struct file *file)
skb_queue_purge(&btfmcodec_dev->rxq); skb_queue_purge(&btfmcodec_dev->rxq);
} }
btfmcodec->states.current_state = IDLE;
btfmcodec->states.next_state = IDLE;
return 0; return 0;
} }
btm_opcode get_opcode (struct sk_buff *skb) btm_opcode STREAM_TO_UINT32 (struct sk_buff *skb)
{ {
return ((skb->data[0]<< 8) | skb->data[1]); return (skb->data[0] | (skb->data[1] << 8) |
(skb->data[2] << 16) | (skb->data[3] << 24));
} }
static void btfmcodec_dev_rxwork(struct work_struct *work) static void btfmcodec_dev_rxwork(struct work_struct *work)
{ {
struct btfmcodec_char_device *btfmcodec_dev = container_of(work, struct btfmcodec_char_device, rx_work); struct btfmcodec_char_device *btfmcodec_dev = container_of(work, struct btfmcodec_char_device, rx_work);
struct sk_buff *skb; struct sk_buff *skb;
struct btm_req *req_pkt; uint32_t len;
uint8_t status;
int idx;
BTFMCODEC_DBG("start"); BTFMCODEC_DBG("start");
while ((skb = skb_dequeue(&btfmcodec_dev->rxq))) { while ((skb = skb_dequeue(&btfmcodec_dev->rxq))) {
btm_opcode opcode = get_opcode(skb); btm_opcode opcode = STREAM_TO_UINT32(skb);
// skb_pull(skb, sizeof(btm_opcode)); skb_pull(skb, sizeof(btm_opcode));
len = STREAM_TO_UINT32(skb);
skb_pull(skb, sizeof(len));
switch (opcode) { switch (opcode) {
case BTM_BTFMCODEC_PREPARE_AUDIO_BEARER_SWITCH_REQ: case BTM_BTFMCODEC_PREPARE_AUDIO_BEARER_SWITCH_REQ:
req_pkt = (void *)skb->data; idx = BTM_PKT_TYPE_PREPARE_REQ;
req_pkt->opcode = opcode; BTFMCODEC_DBG("BTM_BTFMCODEC_PREPARE_AUDIO_BEARER_SWITCH_REQ");
BTFMCODEC_DBG("BTM_BTFMCODEC_PREPARE_AUDIO_BEARER_SWITCH_REQ opcode %4x and len %d", req_pkt->opcode, req_pkt->len); if (len == BTM_PREPARE_AUDIO_BEARER_SWITCH_REQ_LEN) {
btfmcodec_dev_enqueue_pkt(btfmcodec_dev, skb->data, skb->len); btfmcodec_dev->status[idx] = skb->data[0];
BTFMCODEC_INFO("prepare wq_prepare_bearer:%p", btfmcodec_dev->wq_prepare_bearer);
queue_work(btfmcodec_dev->workqueue, &btfmcodec_dev->wq_prepare_bearer);
} else {
BTFMCODEC_ERR("wrong packet format with len:%d", len);
}
break; break;
case BTM_BTFMCODEC_MASTER_CONFIG_RSP: case BTM_BTFMCODEC_MASTER_CONFIG_RSP:
BTFMCODEC_DBG("BTM_BTFMCODEC_MASTER_CONFIG_RSP"); idx = BTM_PKT_TYPE_MASTER_CONFIG_RSP;
if (len == BTM_MASTER_CONFIG_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_MASTER_CONFIG_RSP status:%d",
status);
wake_up_interruptible(&btfmcodec_dev->rsp_wait_q[idx]);
break; break;
case BTM_BTFMCODEC_CTRL_MASTER_SHUTDOWN_RSP: case BTM_BTFMCODEC_CTRL_MASTER_SHUTDOWN_RSP:
BTFMCODEC_DBG("BTM_BTFMCODEC_CTRL_MASTER_SHUTDOWN_RSP"); idx = BTM_PKT_TYPE_MASTER_SHUTDOWN_RSP;
if (len == BTM_MASTER_CONFIG_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_CTRL_MASTER_SHUTDOWN_RSP status:%d",
status);
wake_up_interruptible(&btfmcodec_dev->rsp_wait_q[idx]);
break; break;
case BTM_BTFMCODEC_BEARER_SWITCH_IND: case BTM_BTFMCODEC_BEARER_SWITCH_IND:
BTFMCODEC_DBG("BTM_BTFMCODEC_BEARER_SWITCH_IND"); idx = BTM_PKT_TYPE_BEARER_SWITCH_IND;
if (len == BTM_BEARER_SWITCH_IND_LEN) {
status = skb->data[0];
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_BEARER_SWITCH_IND status:%d",
status);
wake_up_interruptible(&btfmcodec_dev->rsp_wait_q[idx]);
break; break;
default: default:
BTFMCODEC_ERR("wrong opcode:%04x", opcode); BTFMCODEC_ERR("wrong opcode:%08x", opcode);
} }
kfree_skb(skb); kfree_skb(skb);
} }
@@ -172,7 +223,7 @@ static ssize_t btfmcodec_dev_write(struct file *file,
void *kbuf; void *kbuf;
int ret = 0; int ret = 0;
if (!btfmcodec || !btfmcodec->btfmcodec_dev || refcount_read(&btfmcodec->btfmcodec_dev->active_clients) == 0) { if (!btfmcodec || !btfmcodec->btfmcodec_dev || refcount_read(&btfmcodec->btfmcodec_dev->active_clients) == 1) {
BTFMCODEC_INFO("%s: %d\n", current->comm, task_pid_nr(current)); BTFMCODEC_INFO("%s: %d\n", current->comm, task_pid_nr(current));
return -EINVAL; return -EINVAL;
} else { } else {
@@ -219,16 +270,22 @@ static ssize_t btfmcodec_dev_write(struct file *file,
free_kbuf: free_kbuf:
mutex_unlock(&btfmcodec_dev->lock); mutex_unlock(&btfmcodec_dev->lock);
BTFMCODEC_DBG("finish to %s ret %d\n", btfmcodec_dev->dev_name, ret); BTFMCODEC_DBG("finish to %s ret %d\n", btfmcodec_dev->dev_name, ret);
return ret; return ret < 0 ? ret : count;
} }
int btfmcodec_dev_enqueue_pkt(struct btfmcodec_char_device *btfmcodec_dev, uint8_t *buf, int len) int btfmcodec_dev_enqueue_pkt(struct btfmcodec_char_device *btfmcodec_dev, void *buf, int len)
{ {
struct sk_buff *skb; struct sk_buff *skb;
unsigned long flags; unsigned long flags;
uint8_t *cmd = buf;
BTFMCODEC_DBG("start"); BTFMCODEC_DBG("start");
spin_lock_irqsave(&btfmcodec_dev->tx_queue_lock, flags); spin_lock_irqsave(&btfmcodec_dev->tx_queue_lock, flags);
if (refcount_read(&btfmcodec_dev->active_clients) == 1) {
BTFMCODEC_WARN("no active clients discarding the packet");
spin_unlock_irqrestore(&btfmcodec_dev->tx_queue_lock, flags);
return -EINVAL;
}
skb = alloc_skb(len, GFP_ATOMIC); skb = alloc_skb(len, GFP_ATOMIC);
if (!skb) { if (!skb) {
BTFMCODEC_ERR("failed to allocate memory"); BTFMCODEC_ERR("failed to allocate memory");
@@ -236,7 +293,7 @@ int btfmcodec_dev_enqueue_pkt(struct btfmcodec_char_device *btfmcodec_dev, uint8
return -ENOMEM; return -ENOMEM;
} }
skb_put_data(skb, buf, len); skb_put_data(skb, cmd, len);
skb_queue_tail(&btfmcodec_dev->txq, skb); skb_queue_tail(&btfmcodec_dev->txq, skb);
wake_up_interruptible(&btfmcodec_dev->readq); wake_up_interruptible(&btfmcodec_dev->readq);
spin_unlock_irqrestore(&btfmcodec_dev->tx_queue_lock, flags); spin_unlock_irqrestore(&btfmcodec_dev->tx_queue_lock, flags);
@@ -261,7 +318,7 @@ static __poll_t btfmcodec_dev_poll(struct file *file, poll_table *wait)
unsigned long flags; unsigned long flags;
BTFMCODEC_DBG("start"); BTFMCODEC_DBG("start");
if (!btfmcodec || !btfmcodec->btfmcodec_dev || refcount_read(&btfmcodec->btfmcodec_dev->active_clients) == 0) { if (!btfmcodec || !btfmcodec->btfmcodec_dev || refcount_read(&btfmcodec->btfmcodec_dev->active_clients) == 1) {
BTFMCODEC_INFO("%s: %d\n", current->comm, task_pid_nr(current)); BTFMCODEC_INFO("%s: %d\n", current->comm, task_pid_nr(current));
return -EINVAL; return -EINVAL;
} else { } else {
@@ -273,7 +330,7 @@ static __poll_t btfmcodec_dev_poll(struct file *file, poll_table *wait)
mutex_lock(&btfmcodec_dev->lock); mutex_lock(&btfmcodec_dev->lock);
/* recheck if the client has released by the driver */ /* recheck if the client has released by the driver */
if (refcount_read(&btfmcodec_dev->active_clients) == 0) { if (refcount_read(&btfmcodec_dev->active_clients) == 1) {
BTFMCODEC_WARN("port has been closed alreadt"); BTFMCODEC_WARN("port has been closed alreadt");
mutex_unlock(&btfmcodec_dev->lock); mutex_unlock(&btfmcodec_dev->lock);
return POLLHUP; return POLLHUP;
@@ -312,7 +369,7 @@ static ssize_t btfmcodec_dev_read(struct file *file,
int use; int use;
BTFMCODEC_DBG("start"); BTFMCODEC_DBG("start");
if (!btfmcodec || !btfmcodec->btfmcodec_dev || refcount_read(&btfmcodec->btfmcodec_dev->active_clients) == 0) { if (!btfmcodec || !btfmcodec->btfmcodec_dev || refcount_read(&btfmcodec->btfmcodec_dev->active_clients) == 1) {
BTFMCODEC_INFO("%s: %d\n", current->comm, task_pid_nr(current)); BTFMCODEC_INFO("%s: %d\n", current->comm, task_pid_nr(current));
return -EINVAL; return -EINVAL;
} else { } else {
@@ -333,7 +390,7 @@ static ssize_t btfmcodec_dev_read(struct file *file,
return -ERESTARTSYS; return -ERESTARTSYS;
/* We lost the client while waiting */ /* We lost the client while waiting */
if (!refcount_read(&btfmcodec->btfmcodec_dev->active_clients)) if (refcount_read(&btfmcodec->btfmcodec_dev->active_clients) == 1)
return -ENETRESET; return -ENETRESET;
spin_lock_irqsave(&btfmcodec_dev->tx_queue_lock, flags); spin_lock_irqsave(&btfmcodec_dev->tx_queue_lock, flags);
@@ -391,7 +448,7 @@ static ssize_t btfmcodec_attributes_show(struct device *dev,
struct device_attribute *attr, struct device_attribute *attr,
char *buf) char *buf)
{ {
// struct btfmcodec_char_device *btfmcodec_dev = dev_to_btfmcodec(dev); // struct btfmcodec_get_current_transport *btfmcodec_dev = dev_to_btfmcodec(dev);
return 0; return 0;
} }
@@ -409,7 +466,7 @@ static int __init btfmcodec_init(void)
struct btfmcodec_state_machine *states; struct btfmcodec_state_machine *states;
struct btfmcodec_char_device *btfmcodec_dev; struct btfmcodec_char_device *btfmcodec_dev;
struct device *dev; struct device *dev;
int ret; int ret, i;
BTFMCODEC_INFO("starting up the module"); BTFMCODEC_INFO("starting up the module");
btfmcodec = kzalloc(sizeof(struct btfmcodec_data), GFP_KERNEL); btfmcodec = kzalloc(sizeof(struct btfmcodec_data), GFP_KERNEL);
@@ -457,7 +514,7 @@ static int __init btfmcodec_init(void)
// ToDo Rethink of having btfmcodec alone instead of btfmcodec // ToDo Rethink of having btfmcodec alone instead of btfmcodec
btfmcodec->btfmcodec_dev = btfmcodec_dev; btfmcodec->btfmcodec_dev = btfmcodec_dev;
refcount_set(&btfmcodec_dev->active_clients, 0); refcount_set(&btfmcodec_dev->active_clients, 1);
mutex_init(&btfmcodec_dev->lock); mutex_init(&btfmcodec_dev->lock);
strlcpy(btfmcodec_dev->dev_name, "btfmcodec_dev", DEVICE_NAME_MAX_LEN); strlcpy(btfmcodec_dev->dev_name, "btfmcodec_dev", DEVICE_NAME_MAX_LEN);
device_initialize(dev); device_initialize(dev);
@@ -499,6 +556,17 @@ static int __init btfmcodec_init(void)
init_waitqueue_head(&btfmcodec_dev->readq); init_waitqueue_head(&btfmcodec_dev->readq);
spin_lock_init(&btfmcodec_dev->tx_queue_lock); spin_lock_init(&btfmcodec_dev->tx_queue_lock);
skb_queue_head_init(&btfmcodec_dev->txq); skb_queue_head_init(&btfmcodec_dev->txq);
INIT_LIST_HEAD(&btfmcodec->config_head);
for (i = 0; i < BTM_PKT_TYPE_MAX; i++) {
init_waitqueue_head(&btfmcodec_dev->rsp_wait_q[i]);
}
mutex_init(&states->state_machine_lock);
btfmcodec_dev->workqueue = alloc_ordered_workqueue("btfmcodec_wq", 0);
if (!btfmcodec_dev->workqueue) {
BTFMCODEC_ERR("btfmcodec_dev Workqueue not initialized properly");
ret = -ENOMEM;
goto free_device;
}
return ret; return ret;
free_device: free_device:

View File

@@ -0,0 +1,314 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 202333 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include "btfm_codec.h"
#include "btfm_codec_pkt.h"
#include "btfm_codec_btadv_interface.h"
void btfmcodec_initiate_hwep_shutdown(struct btfmcodec_char_device *btfmcodec_dev)
{
wait_queue_head_t *rsp_wait_q =
&btfmcodec_dev->rsp_wait_q[BTM_PKT_TYPE_HWEP_SHUTDOWN];
int ret;
uint8_t *status = &btfmcodec_dev->status[BTM_PKT_TYPE_HWEP_SHUTDOWN];
*status = BTM_WAITING_RSP;
BTFMCODEC_INFO("queuing work to shutdown");
schedule_work(&btfmcodec_dev->wq_hwep_shutdown);
BTFMCODEC_INFO("waiting here for shutdown");
ret = wait_event_interruptible_timeout(*rsp_wait_q,
*status != BTM_WAITING_RSP,
msecs_to_jiffies(BTM_MASTER_CONFIG_RSP_TIMEOUT));
/* Rethink of having a new packet to notify transport switch error */
if (ret == 0) {
BTFMCODEC_ERR("failed to recevie to complete hwep_shutdown");
flush_work(&btfmcodec_dev->wq_hwep_shutdown);
} else {
if (*status == BTM_RSP_RECV) {
BTFMCODEC_ERR("sucessfully closed hwep");
return;
} else if (*status == BTM_FAIL_RESP_RECV) {
BTFMCODEC_ERR("Failed to close hwep");
return;
}
}
}
void btfmcodec_move_to_next_state(struct btfmcodec_state_machine *state)
{
mutex_lock(&state->state_machine_lock);
if (state->current_state == BT_Connecting ||
state->current_state == BTADV_AUDIO_Connecting) {
state->current_state += 1;
BTFMCODEC_INFO("moving from %s to %s",
coverttostring(state->current_state -1 ),
coverttostring(state->current_state));
} else {
BTFMCODEC_ERR("State machine might have gone bad. reseting to default");
state->current_state = IDLE;
}
state->prev_state = IDLE;
mutex_unlock(&state->state_machine_lock);
}
void btfmcodec_revert_current_state(struct btfmcodec_state_machine *state)
{
mutex_lock(&state->state_machine_lock);
BTFMCODEC_INFO("reverting from %s to %s", coverttostring(state->current_state),
coverttostring(state->prev_state));
state->current_state = state->prev_state;
state->prev_state = IDLE;
mutex_unlock(&state->state_machine_lock);
}
void btfmcodec_set_current_state(struct btfmcodec_state_machine *state,
btfmcodec_state current_state)
{
mutex_lock(&state->state_machine_lock);
BTFMCODEC_INFO("moving from %s to %s", coverttostring(state->current_state),
coverttostring(current_state));
state->prev_state = state->current_state;
state->current_state = current_state;
mutex_unlock(&state->state_machine_lock);
}
btfmcodec_state btfmcodec_get_current_transport(struct
btfmcodec_state_machine *state)
{
btfmcodec_state current_state;
mutex_lock(&state->state_machine_lock);
current_state = state->current_state;
mutex_unlock(&state->state_machine_lock);
return current_state;
}
int btfmcodec_frame_transport_switch_ind_pkt(struct btfmcodec_char_device *btfmcodec_dev,
uint8_t active_transport,
uint8_t status)
{
struct btm_ctrl_pkt rsp;
rsp.opcode = BTM_BTFMCODEC_TRANSPORT_SWITCH_FAILED_IND;
rsp.len = BTM_PREPARE_AUDIO_BEARER_SWITCH_RSP_LEN;
rsp.active_transport = active_transport;
rsp.status = status;
return btfmcodec_dev_enqueue_pkt(btfmcodec_dev, &rsp, (rsp.len +
BTM_HEADER_LEN));
}
int btfmcodec_frame_prepare_bearer_rsp_pkt(struct btfmcodec_char_device *btfmcodec_dev,
uint8_t active_transport,
uint8_t status)
{
struct btm_ctrl_pkt rsp;
rsp.opcode = BTM_BTFMCODEC_PREPARE_AUDIO_BEARER_SWITCH_RSP;
rsp.len =BTM_PREPARE_AUDIO_BEARER_SWITCH_RSP_LEN;
rsp.active_transport = active_transport;
rsp.status = status;
return btfmcodec_dev_enqueue_pkt(btfmcodec_dev, &rsp, (rsp.len +
BTM_HEADER_LEN));
}
int btfmcodec_wait_for_bearer_ind(struct btfmcodec_char_device *btfmcodec_dev)
{
wait_queue_head_t *rsp_wait_q =
&btfmcodec_dev->rsp_wait_q[BTM_PKT_TYPE_BEARER_SWITCH_IND];
int ret;
uint8_t *status = &btfmcodec_dev->status[BTM_PKT_TYPE_BEARER_SWITCH_IND];
*status = BTM_WAITING_RSP;
ret = wait_event_interruptible_timeout(*rsp_wait_q,
*status != BTM_WAITING_RSP,
msecs_to_jiffies(BTM_MASTER_CONFIG_RSP_TIMEOUT));
if (ret == 0) {
BTFMCODEC_ERR("failed to recevie BTM_BEARER_SWITCH_IND");
ret = -MSG_INTERNAL_TIMEOUT;
} else {
if (*status == BTM_RSP_RECV) {
ret = 0;
} else if (*status == BTM_FAIL_RESP_RECV) {
BTFMCODEC_ERR("Rx BTM_BEARER_SWITCH_IND with failure status");
ret = -1;
}
}
return ret;
}
int btfmcodec_initiate_hwep_configuration(struct btfmcodec_char_device *btfmcodec_dev)
{
wait_queue_head_t *rsp_wait_q =
&btfmcodec_dev->rsp_wait_q[BTM_PKT_TYPE_HWEP_CONFIG];
uint8_t *status = &btfmcodec_dev->status[BTM_PKT_TYPE_HWEP_CONFIG];
int ret;
schedule_work(&btfmcodec_dev->wq_hwep_configure);
ret = wait_event_interruptible_timeout(*rsp_wait_q,
*status != BTM_WAITING_RSP,
msecs_to_jiffies(BTM_MASTER_CONFIG_RSP_TIMEOUT));
if (ret == 0) {
BTFMCODEC_ERR("failed to recevie complete hwep configure");
flush_work(&btfmcodec_dev->wq_hwep_configure);
ret = -1;
} else {
if (*status == BTM_RSP_RECV) {
ret = 0;
} else if (*status == BTM_FAIL_RESP_RECV) {
BTFMCODEC_ERR("Failed to close hwep moving back to previous state");
ret = -1;
}
}
return ret;
}
void btfmcodec_configure_hwep(struct btfmcodec_char_device *btfmcodec_dev)
{
struct btfmcodec_data *btfmcodec = (struct btfmcodec_data *)btfmcodec_dev->btfmcodec;
struct btfmcodec_state_machine *state = &btfmcodec->states;
int ret;
uint8_t status = MSG_SUCCESS;
ret = btfmcodec_initiate_hwep_configuration(btfmcodec_dev);
if (ret < 0) {
status = MSG_FAILED_TO_CONFIGURE_HWEP;
/* Move back to BTADV_AUDIO_Connected from BT_Connecting */
btfmcodec_revert_current_state(state);
}
ret = btfmcodec_frame_transport_switch_ind_pkt(btfmcodec_dev,
btfmcodec_get_current_transport(state), status);
if (status != MSG_SUCCESS)
return;
if (ret < 0) {
ret = btfmcodec_wait_for_bearer_ind(btfmcodec_dev);
if (ret < 0) {
/* Move back to BTADV_AUDIO_Connected for failure cases*/
BTFMCODEC_ERR("moving back to previous state");
btfmcodec_revert_current_state(state);
/* close HWEP */
btfmcodec_initiate_hwep_shutdown(btfmcodec_dev);
if (ret == -MSG_INTERNAL_TIMEOUT) {
btfmcodec_frame_transport_switch_ind_pkt(btfmcodec_dev, BT,
MSG_INTERNAL_TIMEOUT);
}
} else {
/* move from BT_Connecting to BT_Connected */
btfmcodec_move_to_next_state(state);
}
} else {
/* add code to handle packet errors */
}
}
void btfmcodec_prepare_bearer(struct btfmcodec_char_device *btfmcodec_dev,
enum transport_type new_transport)
{
struct btfmcodec_data *btfmcodec = (struct btfmcodec_data *)btfmcodec_dev->btfmcodec;
struct btfmcodec_state_machine *state = &btfmcodec->states;
btfmcodec_state current_state;
int ret = -1;
if (new_transport > (ARRAY_SIZE(transport_type_text))) {
btfmcodec_frame_prepare_bearer_rsp_pkt(btfmcodec_dev,
(uint8_t) btfmcodec_get_current_transport(state),
MSG_WRONG_TRANSPORT_TYPE);
return;
}
BTFMCODEC_INFO("Rx to switch from transport type %s to %s",
coverttostring(btfmcodec_get_current_transport(state)),
transport_type_text[new_transport - 1]);
current_state = btfmcodec_get_current_transport(state);
if (new_transport == BT) {
/* If BT is already active. send +ve ack to BTADV Audio Manager */
if (current_state == BT_Connected ||
current_state == BT_Connecting) {
btfmcodec_frame_prepare_bearer_rsp_pkt(btfmcodec_dev,
(uint8_t)current_state, MSG_SUCCESS);
return;
} else if (current_state == BTADV_AUDIO_Connected ||
current_state == BTADV_AUDIO_Connecting||
current_state == IDLE) {
if (btfmcodec_is_valid_cache_avb(btfmcodec)) {
BTFMCODEC_INFO("detected BTADV audio Gaming usecase to BT usecase");
btfmcodec_set_current_state(state, BT_Connecting);
btfmcodec_configure_hwep(btfmcodec_dev);
} else {
if (current_state != IDLE)
BTFMCODEC_INFO("detected BTADV Audio lossless to IDLE");
BTFMCODEC_INFO("moving to IDLE as no config available");
btfmcodec_set_current_state(state, IDLE);
btfmcodec_frame_prepare_bearer_rsp_pkt(btfmcodec_dev,
btfmcodec_get_current_transport(state),
MSG_SUCCESS);
/* No need wait for bearer switch indications as BTFMCODEC
* driver doesn't have configs to configure
*/
}
}
} else if(new_transport == BTADV) {
/* If BTADV audio is already active. send +ve ack to BTADV audio Manager */
if (current_state == BTADV_AUDIO_Connecting ||
current_state == BTADV_AUDIO_Connected) {
btfmcodec_frame_prepare_bearer_rsp_pkt(btfmcodec_dev,
(uint8_t)current_state, MSG_SUCCESS);
return;
} else {
btfmcodec_set_current_state(state, BTADV_AUDIO_Connecting);
if (btfmcodec_is_valid_cache_avb(btfmcodec)) {
BTFMCODEC_INFO("detected BT to BTADV audio Gaming usecase");
} else {
BTFMCODEC_INFO("detected IDLE to BTADV audio lossless usecase");
}
ret = btfmcodec_frame_prepare_bearer_rsp_pkt(btfmcodec_dev,
BTADV_AUDIO_Connecting, MSG_SUCCESS);
if (ret < 0)
return;
/* Wait here to get Bearer switch indication */
ret = btfmcodec_wait_for_bearer_ind(btfmcodec_dev);
if (ret < 0) {
BTFMCODEC_ERR("moving back to previous state");
btfmcodec_revert_current_state(state);
if (ret == -MSG_INTERNAL_TIMEOUT) {
btfmcodec_frame_transport_switch_ind_pkt(
btfmcodec_dev, BTADV,
MSG_INTERNAL_TIMEOUT);
}
} else {
btfmcodec_move_to_next_state(state);
}
if (ret < 0)
return;
if (btfmcodec_is_valid_cache_avb(btfmcodec)) {
BTFMCODEC_INFO("Initiating BT port close...");
btfmcodec_initiate_hwep_shutdown(btfmcodec_dev);
}
}
}
}
void btfmcodec_wq_prepare_bearer(struct work_struct *work)
{
struct btfmcodec_char_device *btfmcodec_dev = container_of(work,
struct btfmcodec_char_device,
wq_prepare_bearer);
int idx = BTM_PKT_TYPE_PREPARE_REQ;
BTFMCODEC_INFO(": with new transport:%d", btfmcodec_dev->status[idx]);
btfmcodec_prepare_bearer(btfmcodec_dev, btfmcodec_dev->status[idx]);
}

View File

@@ -6,23 +6,116 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include "btfm_codec.h" #include "btfm_codec.h"
#include "btfm_codec_interface.h" #include "btfm_codec_interface.h"
#include "btfm_codec_pkt.h"
#include "btfm_codec_btadv_interface.h"
static struct snd_soc_dai_driver *btfmcodec_dai_info; static struct snd_soc_dai_driver *btfmcodec_dai_info;
uint32_t bits_per_second;
static int btfm_codec_get_mixer_control(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *codec = kcontrol->private_data;
struct btfmcodec_data *btfmcodec = snd_soc_component_get_drvdata(codec);
struct hwep_data *hwepinfo = btfmcodec->hwep_info;
struct btfmcodec_state_machine states = btfmcodec->states;
struct snd_kcontrol_new *mixer_ctrl = hwepinfo->mixer_ctrl;
struct snd_ctl_elem_id id = kcontrol->id;
int num_mixer_ctrl = hwepinfo->num_mixer_ctrl;
int i = 0;
BTFMCODEC_DBG("");
if (states.current_state != IDLE) {
BTFMCODEC_WARN("Received probe when state is :%s",
coverttostring(states.current_state));
} else {
for (; i < num_mixer_ctrl ; i++) {
BTFMCODEC_DBG("checking mixer_ctrl:%s and current mixer:%s",
id.name, mixer_ctrl[i].name);
if (!strncmp(id.name, mixer_ctrl[i].name, 64)) {
BTFMCODEC_DBG("Matched");
mixer_ctrl[i].get(kcontrol, ucontrol);
break;
}
}
if (num_mixer_ctrl == i)
return 0;
}
return 1;
}
static int btfmcodec_put_mixer_control(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *codec = kcontrol->private_data;
struct btfmcodec_data *btfmcodec = snd_soc_component_get_drvdata(codec);
struct hwep_data *hwepinfo = btfmcodec->hwep_info;
struct btfmcodec_state_machine states = btfmcodec->states;
struct snd_kcontrol_new *mixer_ctrl = hwepinfo->mixer_ctrl;
struct snd_ctl_elem_id id = kcontrol->id;
int num_mixer_ctrl = hwepinfo->num_mixer_ctrl;
int i = 0;
BTFMCODEC_DBG("");
if (states.current_state != IDLE) {
BTFMCODEC_WARN("Received probe when state is :%s",
coverttostring(states.current_state));
} else {
for (; i < num_mixer_ctrl ; i++) {
BTFMCODEC_DBG("checking mixer_ctrl:%s and current mixer:%s",
id.name, mixer_ctrl[i].name);
if (!strncmp(id.name, mixer_ctrl[i].name, 64)) {
BTFMCODEC_DBG("Matched");
mixer_ctrl[i].put(kcontrol, ucontrol);
break;
}
}
if (num_mixer_ctrl == i)
return 0;
}
return 1;
}
static int btfmcodec_codec_probe(struct snd_soc_component *codec) static int btfmcodec_codec_probe(struct snd_soc_component *codec)
{ {
struct btfmcodec_data *btfmcodec = snd_soc_component_get_drvdata(codec); struct btfmcodec_data *btfmcodec = snd_soc_component_get_drvdata(codec);
struct btfmcodec_state_machine states = btfmcodec->states; struct btfmcodec_state_machine *state = &btfmcodec->states;
struct hwep_data *hwep_info = btfmcodec->hwep_info; struct hwep_data *hwep_info = btfmcodec->hwep_info;
int num_mixer_ctrl = hwep_info->num_mixer_ctrl;
BTFMCODEC_DBG(""); BTFMCODEC_DBG("");
// ToDo: check weather probe has to allowed when state if different // ToDo: check weather probe has to allowed when state if different
if (states.current_state != IDLE) { if (btfmcodec_get_current_transport(state)!= IDLE) {
BTFMCODEC_WARN("Received probe when state is :%s", coverttostring(states.current_state)); BTFMCODEC_WARN("Received probe when state is :%s",
coverttostring(btfmcodec_get_current_transport(state)));
} else if (hwep_info->drv && hwep_info->drv->hwep_probe) { } else if (hwep_info->drv && hwep_info->drv->hwep_probe) {
return hwep_info->drv->hwep_probe(codec); hwep_info->drv->hwep_probe(codec);
/* Register mixer control */
if (hwep_info->mixer_ctrl && num_mixer_ctrl >= 1) {
struct snd_kcontrol_new *mixer_ctrl;
int i = 0;
mixer_ctrl = (struct snd_kcontrol_new *)
kzalloc(num_mixer_ctrl *
sizeof(struct snd_kcontrol_new), GFP_KERNEL);
if (!mixer_ctrl) {
BTFMCODEC_ERR("failed to register mixer controls");
goto end;
}
BTFMCODEC_INFO("Registering %d mixer controls", num_mixer_ctrl);
memcpy(mixer_ctrl, hwep_info->mixer_ctrl, num_mixer_ctrl * sizeof(struct snd_kcontrol_new));
for (; i< num_mixer_ctrl; i++) {
BTFMCODEC_INFO("name of control:%s", mixer_ctrl[i].name);
mixer_ctrl[i].get = btfm_codec_get_mixer_control;
mixer_ctrl[i].put = btfmcodec_put_mixer_control;
}
snd_soc_add_component_controls(codec, mixer_ctrl, num_mixer_ctrl);
BTFMCODEC_INFO("CODEC address while registering mixer ctrl:%p", codec);
}
} }
end:
// ToDo to add mixer control. // ToDo to add mixer control.
return 0; return 0;
} }
@@ -30,13 +123,14 @@ static int btfmcodec_codec_probe(struct snd_soc_component *codec)
static void btfmcodec_codec_remove(struct snd_soc_component *codec) static void btfmcodec_codec_remove(struct snd_soc_component *codec)
{ {
struct btfmcodec_data *btfmcodec = snd_soc_component_get_drvdata(codec); struct btfmcodec_data *btfmcodec = snd_soc_component_get_drvdata(codec);
struct btfmcodec_state_machine states = btfmcodec->states; struct btfmcodec_state_machine *state = &btfmcodec->states;
struct hwep_data *hwep_info = btfmcodec->hwep_info; struct hwep_data *hwep_info = btfmcodec->hwep_info;
BTFMCODEC_DBG(""); BTFMCODEC_DBG("");
// ToDo: check whether remove has to allowed when state if different // ToDo: check whether remove has to allowed when state if different
if (states.current_state != IDLE) { if (btfmcodec_get_current_transport(state)!= IDLE) {
BTFMCODEC_WARN("Received probe when state is :%s", coverttostring(states.current_state)); BTFMCODEC_WARN("Received probe when state is :%s",
coverttostring(btfmcodec_get_current_transport(state)));
} else if (hwep_info->drv && hwep_info->drv->hwep_remove) { } else if (hwep_info->drv && hwep_info->drv->hwep_remove) {
hwep_info->drv->hwep_remove(codec); hwep_info->drv->hwep_remove(codec);
} }
@@ -46,13 +140,14 @@ static int btfmcodec_codec_write(struct snd_soc_component *codec,
unsigned int reg, unsigned int value) unsigned int reg, unsigned int value)
{ {
struct btfmcodec_data *btfmcodec = snd_soc_component_get_drvdata(codec); struct btfmcodec_data *btfmcodec = snd_soc_component_get_drvdata(codec);
struct btfmcodec_state_machine states = btfmcodec->states; struct btfmcodec_state_machine *state = &btfmcodec->states;
struct hwep_data *hwep_info = btfmcodec->hwep_info; struct hwep_data *hwep_info = btfmcodec->hwep_info;
BTFMCODEC_DBG(""); BTFMCODEC_DBG("");
// ToDo: check whether remove has to allowed when state if different // ToDo: check whether write has to allowed when state if different
if (states.current_state != IDLE) { if (btfmcodec_get_current_transport(state)!= IDLE) {
BTFMCODEC_WARN("Received probe when state is :%s", coverttostring(states.current_state)); BTFMCODEC_WARN("Received probe when state is :%s",
coverttostring(btfmcodec_get_current_transport(state)));
} else if (hwep_info->drv && hwep_info->drv->hwep_remove) { } else if (hwep_info->drv && hwep_info->drv->hwep_remove) {
return hwep_info->drv->hwep_write(codec, reg, value); return hwep_info->drv->hwep_write(codec, reg, value);
} }
@@ -64,13 +159,14 @@ static unsigned int btfmcodec_codec_read(struct snd_soc_component *codec,
unsigned int reg) unsigned int reg)
{ {
struct btfmcodec_data *btfmcodec = snd_soc_component_get_drvdata(codec); struct btfmcodec_data *btfmcodec = snd_soc_component_get_drvdata(codec);
struct btfmcodec_state_machine states = btfmcodec->states; struct btfmcodec_state_machine *state = &btfmcodec->states;
struct hwep_data *hwep_info = btfmcodec->hwep_info; struct hwep_data *hwep_info = btfmcodec->hwep_info;
BTFMCODEC_DBG(""); BTFMCODEC_DBG("");
// ToDo: check whether remove has to allowed when state if different // ToDo: check whether read has to allowed when state if different
if (states.current_state != IDLE) { if (btfmcodec_get_current_transport(state)!= IDLE) {
BTFMCODEC_WARN("Received probe when state is :%s", coverttostring(states.current_state)); BTFMCODEC_WARN("Received probe when state is :%s",
coverttostring(btfmcodec_get_current_transport(state)));
} else if (hwep_info->drv && hwep_info->drv->hwep_read) { } else if (hwep_info->drv && hwep_info->drv->hwep_read) {
return hwep_info->drv->hwep_read(codec, reg); return hwep_info->drv->hwep_read(codec, reg);
} }
@@ -92,43 +188,178 @@ static inline void * btfmcodec_get_dai_drvdata(struct hwep_data *hwep_info)
return hwep_info->dai_drv; return hwep_info->dai_drv;
} }
static int btfmcodec_dai_startup(struct snd_pcm_substream *substream, int btfmcodec_hwep_startup(struct btfmcodec_data *btfmcodec)
struct snd_soc_dai *dai)
{ {
struct btfmcodec_data *btfmcodec = snd_soc_component_get_drvdata(dai->component); struct hwep_data *hwep_info = btfmcodec->hwep_info;
struct btfmcodec_state_machine states = btfmcodec->states; struct hwep_dai_driver *dai_drv = (struct hwep_dai_driver *)
struct hwep_dai_driver *dai_drv = (struct hwep_dai_driver *)btfmcodec_get_dai_drvdata(btfmcodec->hwep_info); btfmcodec_get_dai_drvdata(hwep_info);
BTFMCODEC_DBG("substream = %s stream = %d dai->name = %s", if (dai_drv && dai_drv->dai_ops && dai_drv->dai_ops->hwep_startup) {
substream->name, substream->stream, dai->name);
// ToDo: check whether statup has to allowed when state if different
if (states.current_state != IDLE) {
BTFMCODEC_WARN("Received probe when state is :%s", coverttostring(states.current_state));
} else if (dai && dai_drv->dai_ops && dai_drv->dai_ops->hwep_startup) {
return dai_drv->dai_ops->hwep_startup((void *)btfmcodec->hwep_info); return dai_drv->dai_ops->hwep_startup((void *)btfmcodec->hwep_info);
} else { } else {
return -1; return -1;
} }
}
static int btfmcodec_dai_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct btfmcodec_data *btfmcodec = snd_soc_component_get_drvdata(dai->component);
struct btfmcodec_state_machine *state = &btfmcodec->states;
BTFMCODEC_DBG("substream = %s stream = %d dai->name = %s",
substream->name, substream->stream, dai->name);
if (btfmcodec_get_current_transport(state) != IDLE &&
btfmcodec_get_current_transport(state) != BT_Connected) {
BTFMCODEC_DBG("Not allowing as state is:%s",
coverttostring(btfmcodec_get_current_transport(state)));
} else {
return btfmcodec_hwep_startup(btfmcodec);
}
return 0; return 0;
} }
int btfmcodec_hwep_shutdown(struct btfmcodec_data *btfmcodec, int id)
{
struct hwep_data *hwep_info = btfmcodec->hwep_info;
struct btfmcodec_char_device *btfmcodec_dev = btfmcodec->btfmcodec_dev;
struct hwep_dai_driver *dai_drv = (struct hwep_dai_driver *)
btfmcodec_get_dai_drvdata(hwep_info);
struct btfmcodec_state_machine *state = &btfmcodec->states;
struct btm_master_shutdown_req shutdown_req;
wait_queue_head_t *rsp_wait_q =
&btfmcodec_dev->rsp_wait_q[BTM_PKT_TYPE_MASTER_SHUTDOWN_RSP];
uint8_t *status = &btfmcodec_dev->status[BTM_PKT_TYPE_MASTER_SHUTDOWN_RSP];
int ret = 0;
/* for master configurations failure cases, we don't need to send
* shutdown request
*/
if (btfmcodec_get_current_transport(state) == BT_Connected) {
BTFMCODEC_DBG("sending master shutdown request..");
shutdown_req.opcode = BTM_BTFMCODEC_MASTER_SHUTDOWN_REQ;
shutdown_req.len = BTM_MASTER_SHUTDOWN_REQ_LEN;
shutdown_req.stream_id = id;
/* See if we need to protect below with lock */
*status = BTM_WAITING_RSP;
btfmcodec_dev_enqueue_pkt(btfmcodec_dev, &shutdown_req, (shutdown_req.len +
BTM_HEADER_LEN));
ret = wait_event_interruptible_timeout(*rsp_wait_q,
(*status) != BTM_WAITING_RSP,
msecs_to_jiffies(BTM_MASTER_CONFIG_RSP_TIMEOUT));
if (ret == 0) {
BTFMCODEC_ERR("failed to recevie response from BTADV audio Manager");
} else {
if (*status == BTM_RSP_RECV)
ret = 0;
else if (*status == BTM_FAIL_RESP_RECV)
ret = -1;
}
} else {
BTFMCODEC_WARN("Not sending master shutdown request as state is:%s",
coverttostring(btfmcodec_get_current_transport(state)));
}
if (dai_drv && dai_drv->dai_ops && dai_drv->dai_ops->hwep_shutdown) {
dai_drv->dai_ops->hwep_shutdown((void *)btfmcodec->hwep_info, id);
}
return ret;
}
void btfmcodec_wq_hwep_shutdown(struct work_struct *work)
{
struct btfmcodec_char_device *btfmcodec_dev = container_of(work,
struct btfmcodec_char_device,
wq_hwep_shutdown);
struct btfmcodec_data *btfmcodec = (struct btfmcodec_data *)btfmcodec_dev->btfmcodec;
struct list_head *head = &btfmcodec->config_head;
struct hwep_configurations *hwep_configs = NULL;
int ret = -1;
int idx = BTM_PKT_TYPE_HWEP_SHUTDOWN;
BTFMCODEC_INFO(" starting shutdown");
/* Just check if first Rx has to be closed first or
* any order should be ok.
*/
list_for_each_entry(hwep_configs, head, dai_list) {
BTFMCODEC_INFO("shuting down dai id:%d", hwep_configs->stream_id);
ret = btfmcodec_hwep_shutdown(btfmcodec, hwep_configs->stream_id);
if (ret < 0) {
BTFMCODEC_ERR("failed to shutdown master with id", hwep_configs->stream_id);
break;
}
}
if (ret < 0)
btfmcodec_dev->status[idx] = BTM_FAIL_RESP_RECV;
else
btfmcodec_dev->status[idx] = BTM_RSP_RECV;
wake_up_interruptible(&btfmcodec_dev->rsp_wait_q[idx]);
}
static int btfmcodec_delete_configs(struct btfmcodec_data *btfmcodec, uint8_t id)
{
struct list_head *head = &btfmcodec->config_head;
struct hwep_configurations *hwep_configs;
int ret = -1;
list_for_each_entry(hwep_configs, head, dai_list) {
if (hwep_configs->stream_id == id) {
BTFMCODEC_INFO("deleting configs with id %d", id);
list_del(&hwep_configs->dai_list);
ret = 1;
break;
}
}
return ret;
}
static void btfmcodec_dai_shutdown(struct snd_pcm_substream *substream, static void btfmcodec_dai_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai) struct snd_soc_dai *dai)
{ {
struct btfmcodec_data *btfmcodec = snd_soc_component_get_drvdata(dai->component); struct btfmcodec_data *btfmcodec = snd_soc_component_get_drvdata(dai->component);
struct btfmcodec_state_machine states = btfmcodec->states; struct btfmcodec_state_machine *state = &btfmcodec->states;
struct hwep_dai_driver *dai_drv = (struct hwep_dai_driver *)btfmcodec_get_dai_drvdata(btfmcodec->hwep_info);
BTFMCODEC_DBG("dai->name: %s, dai->id: %d, dai->rate: %d", dai->name, BTFMCODEC_DBG("dai->name: %s, dai->id: %d, dai->rate: %d", dai->name,
dai->id, dai->rate); dai->id, dai->rate);
// ToDo: check whether statup has to allowed when state if different if (btfmcodec_get_current_transport(state) != IDLE &&
if (states.current_state != IDLE) { btfmcodec_get_current_transport(state) != BT_Connected) {
BTFMCODEC_WARN("Received probe when state is :%s", coverttostring(states.current_state)); BTFMCODEC_WARN("not allowing shutdown as state is:%s",
} else if (dai && dai_drv->dai_ops && dai_drv->dai_ops->hwep_shutdown) { coverttostring(btfmcodec_get_current_transport(state)));
dai_drv->dai_ops->hwep_shutdown((void *)btfmcodec->hwep_info, dai->id); /* Delete stored configs */
btfmcodec_delete_configs(btfmcodec, dai->id);
} else {
/* first master shutdown has to done */
btfmcodec_hwep_shutdown(btfmcodec, dai->id);
btfmcodec_delete_configs(btfmcodec, dai->id);
if (!btfmcodec_is_valid_cache_avb(btfmcodec))
btfmcodec_set_current_state(state, IDLE);
else {
BTFMCODEC_WARN("valid stream id is available not updating state\n");
btfmcodec_set_current_state(state, BT_Connected);
}
}
}
int btfmcodec_hwep_hw_params (struct btfmcodec_data *btfmcodec, uint32_t bps,
uint32_t direction)
{
struct hwep_data *hwep_info = btfmcodec->hwep_info;
struct hwep_dai_driver *dai_drv = (struct hwep_dai_driver *)
btfmcodec_get_dai_drvdata(hwep_info);
if (dai_drv && dai_drv->dai_ops && dai_drv->dai_ops->hwep_hw_params) {
return dai_drv->dai_ops->hwep_hw_params((void *)btfmcodec->hwep_info,
bps, direction);
} else {
return -1;
} }
} }
@@ -137,55 +368,214 @@ static int btfmcodec_dai_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai) struct snd_soc_dai *dai)
{ {
struct btfmcodec_data *btfmcodec = snd_soc_component_get_drvdata(dai->component); struct btfmcodec_data *btfmcodec = snd_soc_component_get_drvdata(dai->component);
struct btfmcodec_state_machine states = btfmcodec->states; struct btfmcodec_state_machine *state = &btfmcodec->states;
struct hwep_dai_driver *dai_drv = (struct hwep_dai_driver *)btfmcodec_get_dai_drvdata(btfmcodec->hwep_info);
uint32_t bps = params_width(params); uint32_t bps = params_width(params);
uint32_t direction = substream->stream; uint32_t direction = substream->stream;
BTFMCODEC_DBG("dai->name = %s DAI-ID %x rate %d bps %d num_ch %d", BTFMCODEC_DBG("dai->name = %s DAI-ID %x rate %d bps %d num_ch %d",
dai->name, dai->id, params_rate(params), params_width(params), dai->name, dai->id, params_rate(params), params_width(params),
params_channels(params)); params_channels(params));
// ToDo: check whether hw_params has to allowed when state if different
if (states.current_state != IDLE) { if (btfmcodec_get_current_transport(state) != IDLE &&
BTFMCODEC_WARN("Received probe when state is :%s", coverttostring(states.current_state)); btfmcodec_get_current_transport(state) != BT_Connected) {
} else if (dai_drv && dai_drv->dai_ops && dai_drv->dai_ops->hwep_hw_params) { BTFMCODEC_WARN("caching bps as state is :%s",
return dai_drv->dai_ops->hwep_hw_params((void *)btfmcodec->hwep_info, bps, direction); coverttostring(btfmcodec_get_current_transport(state)));
bits_per_second = bps;
} else {
return btfmcodec_hwep_hw_params(btfmcodec, bps, direction);
}
return 0;
}
bool btfmcodec_is_valid_cache_avb(struct btfmcodec_data *btfmcodec)
{
struct list_head *head = &btfmcodec->config_head;
struct hwep_configurations *hwep_configs;
bool cache_avb = false;
list_for_each_entry(hwep_configs, head, dai_list) {
cache_avb = true;
break;
}
return cache_avb;
}
static int btfmcodec_check_and_cache_configs(struct btfmcodec_data *btfmcodec,
uint32_t sampling_rate, uint32_t direction,
int id, uint8_t codectype)
{
struct list_head *head = &btfmcodec->config_head;
struct hwep_configurations *hwep_configs;
list_for_each_entry(hwep_configs, head, dai_list) {
if (hwep_configs->stream_id == id) {
BTFMCODEC_WARN("previous entry for %d is already available",
id);
list_del(&hwep_configs->dai_list);
}
}
hwep_configs = kzalloc(sizeof(struct hwep_configurations),
GFP_KERNEL);
if (!hwep_configs) {
BTFMCODEC_ERR("failed to allocate memory");
return -ENOMEM;
}
hwep_configs->stream_id = id; /* Stream identifier */
hwep_configs->sample_rate = sampling_rate;
hwep_configs->bit_width = bits_per_second;
hwep_configs->codectype = codectype;
hwep_configs->direction = direction;
list_add(&hwep_configs->dai_list, head);
BTFMCODEC_INFO("added dai id:%d to list with sampling_rate :%u, direction:%u", id, sampling_rate, direction);
return 1;
}
static int btfmcodec_configure_master(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 master_hwep_configurations hwep_configs;
struct btm_master_config_req config_reg;
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_MASTER_CONFIG_RSP];
uint8_t *status = &btfmcodec_dev->status[BTM_PKT_TYPE_MASTER_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,
&hwep_configs, id);
} else {
BTFMCODEC_ERR("No hwep_get_configs is set by hw ep driver");
return -1;
}
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;
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("================================================\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 +
BTM_HEADER_LEN));
ret = wait_event_interruptible_timeout(*rsp_wait_q,
(*status) != BTM_WAITING_RSP,
msecs_to_jiffies(BTM_MASTER_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)
return -1;
}
return ret;
}
int btfmcodec_hwep_prepare(struct btfmcodec_data *btfmcodec, uint32_t sampling_rate,
uint32_t direction, int id)
{
struct hwep_data *hwep_info = btfmcodec->hwep_info;
struct hwep_dai_driver *dai_drv = (struct hwep_dai_driver *)
btfmcodec_get_dai_drvdata(hwep_info);
struct btfmcodec_state_machine *state = &btfmcodec->states;
int ret;
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);
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 { } else {
return -1; return -1;
} }
return 0; return ret;
} }
static int btfmcodec_dai_prepare(struct snd_pcm_substream *substream, static int btfmcodec_dai_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai) struct snd_soc_dai *dai)
{ {
struct btfmcodec_data *btfmcodec = snd_soc_component_get_drvdata(dai->component); struct btfmcodec_data *btfmcodec = snd_soc_component_get_drvdata(dai->component);
struct btfmcodec_state_machine states = btfmcodec->states; struct btfmcodec_state_machine *state = &btfmcodec->states;
struct hwep_dai_driver *dai_drv = (struct hwep_dai_driver *)btfmcodec_get_dai_drvdata(btfmcodec->hwep_info); struct hwep_data *hwep_info = btfmcodec->hwep_info;
struct hwep_dai_driver *dai_drv = (struct hwep_dai_driver *)
btfmcodec_get_dai_drvdata(hwep_info);
uint8_t *codectype = dai_drv->dai_ops->hwep_codectype;
uint32_t sampling_rate = dai->rate; uint32_t sampling_rate = dai->rate;
uint32_t direction = substream->stream; uint32_t direction = substream->stream;
int id = dai->id; int id = dai->id;
int ret; int ret;
BTFMCODEC_INFO("dai->name: %s, dai->id: %d, dai->rate: %d direction: %d",
dai->name, id, sampling_rate, direction);
BTFMCODEC_INFO("dai->name: %s, dai->id: %d, dai->rate: %d direction: %d", dai->name, if (btfmcodec_get_current_transport(state) != IDLE &&
id, sampling_rate, direction); btfmcodec_get_current_transport(state) != BT_Connected) {
// ToDo: check whether hw_params has to allowed when state if different BTFMCODEC_WARN("caching required info as state is:%s",
if (states.current_state != IDLE) { coverttostring(btfmcodec_get_current_transport(state)));
BTFMCODEC_WARN("Received probe when state is :%s", coverttostring(states.current_state)); ret = btfmcodec_check_and_cache_configs(btfmcodec, sampling_rate, direction,
} else if (dai_drv && dai_drv->dai_ops && dai_drv->dai_ops->hwep_prepare) { id, *codectype);
ret = dai_drv->dai_ops->hwep_prepare((void *)btfmcodec->hwep_info, sampling_rate, } else {
direction, id); ret = btfmcodec_hwep_prepare(btfmcodec, sampling_rate, direction, id);
if (ret == 0 && test_bit(BTADV_AUDIO_MASTER_CONFIG, &btfmcodec->hwep_info->flags)) { if (ret >= 0) {
BTFMCODEC_DBG("configuring master now"); btfmcodec_check_and_cache_configs(btfmcodec, sampling_rate, direction,
id, *codectype);
} }
}
return ret;
}
int btfmcodec_hwep_set_channel_map(void *hwep_info, unsigned int tx_num,
unsigned int *tx_slot, unsigned int rx_num,
unsigned int *rx_slot)
{
struct hwep_dai_driver *dai_drv = (struct hwep_dai_driver *)
btfmcodec_get_dai_drvdata(hwep_info);
if (dai_drv && dai_drv->dai_ops && dai_drv->dai_ops->hwep_set_channel_map) {
return dai_drv->dai_ops->hwep_set_channel_map(hwep_info, tx_num,
tx_slot, rx_num,
rx_slot);
} else { } else {
return -1; return -1;
} }
return 0;
return 0;
} }
static int btfmcodec_dai_set_channel_map(struct snd_soc_dai *dai, static int btfmcodec_dai_set_channel_map(struct snd_soc_dai *dai,
@@ -194,44 +584,95 @@ static int btfmcodec_dai_set_channel_map(struct snd_soc_dai *dai,
{ {
struct btfmcodec_data *btfmcodec = snd_soc_component_get_drvdata(dai->component); struct btfmcodec_data *btfmcodec = snd_soc_component_get_drvdata(dai->component);
struct btfmcodec_state_machine states = btfmcodec->states; struct btfmcodec_state_machine states = btfmcodec->states;
struct hwep_dai_driver *dai_drv = (struct hwep_dai_driver *)btfmcodec_get_dai_drvdata(btfmcodec->hwep_info);
BTFMCODEC_DBG(""); BTFMCODEC_DBG("");
// ToDo: check whether hw_params has to allowed when state if different // ToDo: check whether hw_params has to allowed when state if different
if (states.current_state != IDLE) { if (states.current_state != IDLE) {
BTFMCODEC_WARN("Received probe when state is :%s", coverttostring(states.current_state)); BTFMCODEC_WARN("Received probe when state is :%s", coverttostring(states.current_state));
} else if (dai_drv && dai_drv->dai_ops && dai_drv->dai_ops->hwep_set_channel_map) {
return dai_drv->dai_ops->hwep_set_channel_map((void *)btfmcodec->hwep_info, tx_num,
tx_slot, rx_num, rx_slot);
} else { } else {
return -1; return btfmcodec_hwep_set_channel_map((void *)btfmcodec->hwep_info, tx_num,
tx_slot, rx_num, rx_slot);
} }
return 0; return 0;
} }
int btfmcodec_hwep_get_channel_map(void *hwep_info, unsigned int *tx_num,
unsigned int *tx_slot, unsigned int *rx_num,
unsigned int *rx_slot, int id)
{
struct hwep_dai_driver *dai_drv = (struct hwep_dai_driver *)
btfmcodec_get_dai_drvdata(hwep_info);
if (dai_drv && dai_drv->dai_ops && dai_drv->dai_ops->hwep_get_channel_map) {
return dai_drv->dai_ops->hwep_get_channel_map(hwep_info, tx_num,
tx_slot, rx_num,
rx_slot, id);
} else {
return -1;
}
}
static int btfmcodec_dai_get_channel_map(struct snd_soc_dai *dai, static int btfmcodec_dai_get_channel_map(struct snd_soc_dai *dai,
unsigned int *tx_num, unsigned int *tx_slot, unsigned int *tx_num, unsigned int *tx_slot,
unsigned int *rx_num, unsigned int *rx_slot) unsigned int *rx_num, unsigned int *rx_slot)
{ {
struct btfmcodec_data *btfmcodec = snd_soc_component_get_drvdata(dai->component); struct btfmcodec_data *btfmcodec = snd_soc_component_get_drvdata(dai->component);
struct btfmcodec_state_machine states = btfmcodec->states; // struct btfmcodec_state_machine states = btfmcodec->states;
struct hwep_dai_driver *dai_drv = (struct hwep_dai_driver *)btfmcodec_get_dai_drvdata(btfmcodec->hwep_info);
BTFMCODEC_DBG(""); BTFMCODEC_DBG("");
// ToDo: check whether hw_params has to allowed when state if different // ToDo: get_channel_map is not needed for new driver
if (states.current_state != IDLE) { /* if (states.current_state != IDLE) {
BTFMCODEC_WARN("Received probe when state is :%s", coverttostring(states.current_state)); BTFMCODEC_WARN("Received probe when state is :%s", coverttostring(states.current_state));
} else if (dai_drv && dai_drv->dai_ops && dai_drv->dai_ops->hwep_get_channel_map) {
return dai_drv->dai_ops->hwep_get_channel_map((void *)btfmcodec->hwep_info, tx_num,
tx_slot, rx_num, rx_slot, dai->id);
} else { } else {
return -1; */ return btfmcodec_hwep_get_channel_map((void *)btfmcodec->hwep_info,
} tx_num, tx_slot, rx_num,
rx_slot, dai->id);
// }
return 0; return 0;
} }
void btfmcodec_wq_hwep_configure(struct work_struct *work)
{
struct btfmcodec_char_device *btfmcodec_dev = container_of(work,
struct btfmcodec_char_device,
wq_hwep_configure);
struct btfmcodec_data *btfmcodec = (struct btfmcodec_data *)btfmcodec_dev->btfmcodec;
struct list_head *head = &btfmcodec->config_head;
struct hwep_configurations *hwep_configs = NULL;
int ret;
int idx = BTM_PKT_TYPE_HWEP_CONFIG;
uint32_t sample_rate, direction;
uint8_t id, bit_width, codectype;
list_for_each_entry(hwep_configs, head, dai_list) {
id = hwep_configs->stream_id;
sample_rate = hwep_configs->sample_rate;
bit_width = hwep_configs->bit_width;
codectype = hwep_configs->codectype;
direction = hwep_configs->direction;
BTFMCODEC_INFO("configuring dai id:%d with sampling rate:%d bit_width:%d", id, sample_rate, bit_width);
ret = btfmcodec_hwep_startup(btfmcodec);
if (ret >= 0)
ret = btfmcodec_hwep_hw_params(btfmcodec, bit_width, direction);
if (ret >= 0)
ret = btfmcodec_hwep_prepare(btfmcodec, sample_rate, direction, id);
if (ret < 0) {
BTFMCODEC_ERR("failed to configure hwep", hwep_configs->stream_id);
break;
}
}
if (ret < 0)
btfmcodec_dev->status[idx] = BTM_FAIL_RESP_RECV;
else
btfmcodec_dev->status[idx] = BTM_RSP_RECV;
wake_up_interruptible(&btfmcodec_dev->rsp_wait_q[idx]);
}
static struct snd_soc_dai_ops btfmcodec_dai_ops = { static struct snd_soc_dai_ops btfmcodec_dai_ops = {
.startup = btfmcodec_dai_startup, .startup = btfmcodec_dai_startup,
.shutdown = btfmcodec_dai_shutdown, .shutdown = btfmcodec_dai_shutdown,
@@ -244,11 +685,13 @@ static struct snd_soc_dai_ops btfmcodec_dai_ops = {
int btfm_register_codec(struct hwep_data *hwep_info) int btfm_register_codec(struct hwep_data *hwep_info)
{ {
struct btfmcodec_data *btfmcodec; struct btfmcodec_data *btfmcodec;
struct btfmcodec_char_device *btfmcodec_dev;
struct device *dev; struct device *dev;
struct hwep_dai_driver *dai_drv; struct hwep_dai_driver *dai_drv;
int i, ret; int i, ret;
btfmcodec = btfm_get_btfmcodec(); btfmcodec = btfm_get_btfmcodec();
btfmcodec_dev = btfmcodec->btfmcodec_dev;
dev = &btfmcodec->dev; dev = &btfmcodec->dev;
btfmcodec_dai_info = kzalloc((sizeof(struct snd_soc_dai_driver) * hwep_info->num_dai), GFP_KERNEL); btfmcodec_dai_info = kzalloc((sizeof(struct snd_soc_dai_driver) * hwep_info->num_dai), GFP_KERNEL);
if (!btfmcodec_dai_info) { if (!btfmcodec_dai_info) {
@@ -269,6 +712,19 @@ int btfm_register_codec(struct hwep_data *hwep_info)
BTFMCODEC_INFO("slim bus driver name:%s", dev->driver->name); BTFMCODEC_INFO("slim bus driver name:%s", dev->driver->name);
ret = snd_soc_register_component(dev, &btfmcodec_codec_component, ret = snd_soc_register_component(dev, &btfmcodec_codec_component,
btfmcodec_dai_info, hwep_info->num_dai); btfmcodec_dai_info, hwep_info->num_dai);
BTFMCODEC_INFO("Dev node address: %p", dev);
BTFMCODEC_INFO("btfmcodec address :%p, btfmcodec");
BTFMCODEC_INFO("HWEPINFO address:%p", hwep_info);
BTFMCODEC_INFO("btfmcodec_dev INFO address:%p", btfmcodec->btfmcodec_dev);
BTFMCODEC_INFO("before wq_hwep_shutdown:%p", btfmcodec_dev->wq_hwep_shutdown);
BTFMCODEC_INFO("before wq_prepare_bearer:%p", btfmcodec_dev->wq_prepare_bearer);
INIT_WORK(&btfmcodec_dev->wq_hwep_shutdown, btfmcodec_wq_hwep_shutdown);
INIT_WORK(&btfmcodec_dev->wq_prepare_bearer, btfmcodec_wq_prepare_bearer);
INIT_WORK(&btfmcodec_dev->wq_hwep_configure, btfmcodec_wq_hwep_configure);
BTFMCODEC_INFO("after wq_hwep_shutdown:%p", btfmcodec_dev->wq_hwep_shutdown);
BTFMCODEC_INFO("after wq_prepare_bearer:%p", btfmcodec_dev->wq_prepare_bearer);
BTFMCODEC_INFO("btfmcodec_wq_prepare_bearer:%p", btfmcodec_wq_prepare_bearer);
BTFMCODEC_INFO("btfmcodec_wq_hwep_shutdown:%p", btfmcodec_wq_hwep_shutdown);
return ret; return ret;
} }

View File

@@ -20,24 +20,36 @@
#define DEVICE_NAME_MAX_LEN 64 #define DEVICE_NAME_MAX_LEN 64
enum btfmcodec_states { typedef enum btfmcodec_states {
/*Default state of btfm codec driver */ /*Default state of kernel proxy driver */
IDLE = 0, IDLE = 0,
/* When BT is active transport */
BT_Connected = 1,
/* Waiting for BT bearer indication after configuring HW ports */ /* Waiting for BT bearer indication after configuring HW ports */
BT_Connecting = 2, BT_Connecting = 1,
/* When BTADV_AUDIO is active transport */ /* When BT is active transport */
BTADV_AUDIO_Connected = 3, BT_Connected = 2,
/* Waiting for BTADV_AUDIO bearer switch indications */ /* Waiting for BTADV Audio bearer switch indications */
BTADV_AUDIO_Connecting = 4 BTADV_AUDIO_Connecting = 3,
/* When BTADV audio is active transport */
BTADV_AUDIO_Connected = 4
} btfmcodec_state;
enum btfm_pkt_type {
BTM_PKT_TYPE_PREPARE_REQ = 0,
BTM_PKT_TYPE_MASTER_CONFIG_RSP,
BTM_PKT_TYPE_MASTER_SHUTDOWN_RSP,
BTM_PKT_TYPE_BEARER_SWITCH_IND,
BTM_PKT_TYPE_HWEP_SHUTDOWN,
BTM_PKT_TYPE_HWEP_CONFIG,
BTM_PKT_TYPE_MAX,
}; };
char *coverttostring(enum btfmcodec_states); char *coverttostring(enum btfmcodec_states);
struct btfmcodec_state_machine { struct btfmcodec_state_machine {
enum btfmcodec_states prev_state; struct mutex state_machine_lock;
enum btfmcodec_states current_state; btfmcodec_state prev_state;
enum btfmcodec_states next_state; btfmcodec_state current_state;
btfmcodec_state next_state;
}; };
struct btfmcodec_char_device { struct btfmcodec_char_device {
@@ -46,11 +58,17 @@ struct btfmcodec_char_device {
struct mutex lock; struct mutex lock;
int reuse_minor; int reuse_minor;
char dev_name[DEVICE_NAME_MAX_LEN]; char dev_name[DEVICE_NAME_MAX_LEN];
struct workqueue_struct *workqueue;
struct sk_buff_head rxq; struct sk_buff_head rxq;
struct work_struct rx_work; struct work_struct rx_work;
struct work_struct wq_hwep_shutdown;
struct work_struct wq_prepare_bearer;
struct work_struct wq_hwep_configure;
wait_queue_head_t readq; wait_queue_head_t readq;
spinlock_t tx_queue_lock; spinlock_t tx_queue_lock;
struct sk_buff_head txq; struct sk_buff_head txq;
wait_queue_head_t rsp_wait_q[BTM_PKT_TYPE_MAX];
uint8_t status[BTM_PKT_TYPE_MAX];
void *btfmcodec; void *btfmcodec;
}; };
@@ -59,8 +77,8 @@ struct btfmcodec_data {
struct btfmcodec_state_machine states; struct btfmcodec_state_machine states;
struct btfmcodec_char_device *btfmcodec_dev; struct btfmcodec_char_device *btfmcodec_dev;
struct hwep_data *hwep_info; struct hwep_data *hwep_info;
struct list_head config_head;
}; };
int btfmcodec_dev_enqueue_pkt(struct btfmcodec_char_device *, uint8_t *, int);
struct btfmcodec_data *btfm_get_btfmcodec(void); struct btfmcodec_data *btfm_get_btfmcodec(void);
#endif /*__LINUX_BTFM_CODEC_H */ #endif /*__LINUX_BTFM_CODEC_H */

View File

@@ -0,0 +1,21 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef __LINUX_BTFM_CODEC_BTADV_INTERFACE_H
#define __LINUX_BTFM_CODEC_BTADV_INTERFACE_H
enum transport_type {
BT = 1,
BTADV,
};
static char *transport_type_text[] = {"BT", "BTADV"};
void btfmcodec_set_current_state(struct btfmcodec_state_machine *, btfmcodec_state);
void btfmcodec_wq_prepare_bearer(struct work_struct *);
void btfmcodec_wq_hwep_shutdown(struct work_struct *);
void btfmcodec_initiate_hwep_shutdown(struct btfmcodec_char_device *btfmcodec_dev);
btfmcodec_state btfmcodec_get_current_transport(struct btfmcodec_state_machine *state);
#endif /* __LINUX_BTFM_CODEC_BTADV_INTERFACE_H */

View File

@@ -20,11 +20,32 @@
#define BTADV_AUDIO_MASTER_CONFIG 0 #define BTADV_AUDIO_MASTER_CONFIG 0
#define DEVICE_NAME_MAX_LEN 64 #define DEVICE_NAME_MAX_LEN 64
struct hwep_configurations {
void *btfmcodec;
uint8_t stream_id;
uint32_t sample_rate;
uint8_t bit_width;
uint8_t codectype;
uint32_t direction;
struct list_head dai_list;
};
struct master_hwep_configurations {
uint8_t stream_id;
uint32_t device_id;
uint32_t sample_rate;
uint8_t bit_width;
uint8_t num_channels;
uint8_t chan_num;
uint8_t codectype;
uint16_t direction;
};
struct hwep_comp_drv { struct hwep_comp_drv {
int (*hwep_probe) (struct snd_soc_component *component); int (*hwep_probe) (struct snd_soc_component *);
void (*hwep_remove) (struct snd_soc_component *component); void (*hwep_remove) (struct snd_soc_component *);
unsigned int (*hwep_read)(struct snd_soc_component *component, unsigned int reg); unsigned int (*hwep_read)(struct snd_soc_component *, unsigned int );
int (*hwep_write)(struct snd_soc_component *componentm, unsigned int reg, unsigned int value); int (*hwep_write)(struct snd_soc_component *, unsigned int,
unsigned int);
}; };
struct hwep_dai_ops { struct hwep_dai_ops {
@@ -36,6 +57,9 @@ struct hwep_dai_ops {
unsigned int, unsigned int *); unsigned int, unsigned int *);
int (*hwep_get_channel_map)(void *, unsigned int *, unsigned int *, int (*hwep_get_channel_map)(void *, unsigned int *, unsigned int *,
unsigned int *, unsigned int *, int); unsigned int *, unsigned int *, int);
int (*hwep_get_configs)(void *, struct master_hwep_configurations *,
uint8_t);
uint8_t *hwep_codectype;
}; };
struct hwep_dai_driver { struct hwep_dai_driver {
@@ -51,7 +75,9 @@ struct hwep_data {
char driver_name [DEVICE_NAME_MAX_LEN]; char driver_name [DEVICE_NAME_MAX_LEN];
struct hwep_comp_drv *drv; struct hwep_comp_drv *drv;
struct hwep_dai_driver *dai_drv; struct hwep_dai_driver *dai_drv;
struct snd_kcontrol_new *mixer_ctrl;
int num_dai; int num_dai;
int num_mixer_ctrl;
unsigned long flags; unsigned long flags;
}; };

View File

@@ -6,44 +6,89 @@
#ifndef __LINUX_BTFM_CODEC_PKT_H #ifndef __LINUX_BTFM_CODEC_PKT_H
#define __LINUX_BTFM_CODEC_PKT_H #define __LINUX_BTFM_CODEC_PKT_H
typedef uint16_t btm_opcode; typedef uint32_t btm_opcode;
struct btm_req { struct btm_req {
btm_opcode opcode; btm_opcode opcode;
uint8_t len; uint32_t len;
uint8_t *data; uint8_t *data;
}; }__attribute__((packed));
struct btm_rsp { struct btm_rsp {
btm_opcode opcode; btm_opcode opcode;
uint8_t status; uint8_t status;
}; }__attribute__((packed));
struct btm_ind { struct btm_ind {
btm_opcode opcode; btm_opcode opcode;
uint8_t len; uint32_t len;
uint8_t *data; uint8_t *data;
}__attribute__((packed));
struct btm_ctrl_pkt {
btm_opcode opcode;
uint32_t len;
uint8_t active_transport;
uint8_t status;
}__attribute__((packed));
#define BTM_BTFMCODEC_PREPARE_AUDIO_BEARER_SWITCH_REQ 0x50000000
#define BTM_BTFMCODEC_PREPARE_AUDIO_BEARER_SWITCH_RSP 0x50000001
#define BTM_BTFMCODEC_MASTER_CONFIG_REQ 0x50000002
#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_BEARER_SWITCH_IND 0x58000001
#define BTM_BTFMCODEC_TRANSPORT_SWITCH_FAILED_IND 0x58000002
#define BTM_MASTER_CONFIG_REQ_LEN 13
#define BTM_MASTER_CONFIG_RSP_TIMEOUT 1000
#define BTM_HEADER_LEN 8
#define BTM_PREPARE_AUDIO_BEARER_SWITCH_RSP_LEN 2
#define BTM_MASTER_CONFIG_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
enum rx_status {
/* Waiting for response */
BTM_WAITING_RSP,
/* Response recevied */
BTM_RSP_RECV,
/* Response recevied with failure status*/
BTM_FAIL_RESP_RECV,
};
enum btfm_kp_status {
/* KP processed message succesfully */
MSG_SUCCESS = 0,
/* Error while processing the message */
MSG_FAILED,
/* Wrong transport type selected by BTADV audio manager */
MSG_WRONG_TRANSPORT_TYPE,
/* Timeout triggered to receive bearer switch indications*/
MSG_INTERNAL_TIMEOUT,
MSG_FAILED_TO_CONFIGURE_HWEP,
MSG_FAILED_TO_SHUTDOWN_HWEP,
MSG_ERR_WHILE_SHUTING_DOWN_HWEP,
}; };
#define BTM_BTFMCODEC_PREPARE_AUDIO_BEARER_SWITCH_REQ 0x5000
#define BTM_BTFMCODEC_PREPARE_AUDIO_BEARER_SWITCH_RSP 0x5001
#define BTM_BTFMCODEC_MASTER_CONFIG_REQ 0x5002
#define BTM_BTFMCODEC_MASTER_CONFIG_RSP 0x5003
#define BTM_BTFMCODEC_MASTER_SHUTDOWN_REQ 0x5004
#define BTM_BTFMCODEC_CTRL_MASTER_SHUTDOWN_RSP 0x5005
#define BTM_BTFMCODEC_BEARER_SWITCH_IND 0x50C8
#define BTM_BTFMCODEC_TRANSPORT_SWITCH_FAILED_IND 0x50C9
struct btm_master_config_req { struct btm_master_config_req {
btm_opcode opcode; btm_opcode opcode;
uint8_t len; uint32_t len;
uint8_t stream_identifier; uint8_t stream_id;
uint32_t device_identifier; uint32_t device_id;
uint32_t sampling_rate; uint32_t sample_rate;
uint8_t bit_width; uint8_t bit_width;
uint8_t num_of_channels; uint8_t num_channels;
uint8_t channel_num; uint8_t channel_num;
uint8_t codec_id; uint8_t codec_id;
}; }__attribute__((packed));
struct btm_master_shutdown_req {
btm_opcode opcode;
uint32_t len;
uint8_t stream_id;
}__attribute__((packed));
int btfmcodec_dev_enqueue_pkt(struct btfmcodec_char_device *, void *, int);
bool btfmcodec_is_valid_cache_avb(struct btfmcodec_data *);
#endif /* __LINUX_BTFM_CODEC_PKT_H*/ #endif /* __LINUX_BTFM_CODEC_PKT_H*/

View File

@@ -515,6 +515,67 @@ int btfm_slim_hw_deinit(struct btfmslim *btfmslim)
return ret; return ret;
} }
#if IS_ENABLED (CONFIG_BTFM_SLIM)
void btfm_slim_get_hwep_details(struct slim_device *dev, struct btfmslim *btfm_slim)
{
}
#else
void btfm_slim_get_hwep_details(struct slim_device *slim, struct btfmslim *btfm_slim)
{
struct device_node *np = slim->dev.of_node;
const __be32 *prop;
struct btfmslim_ch *rx_chs = btfm_slim->rx_chs;
struct btfmslim_ch *tx_chs = btfm_slim->tx_chs;
int len;
prop = of_get_property(np, "qcom,btslim-address", &len);
if (prop) {
btfm_slim->device_id = be32_to_cpup(&prop[0]);
BTFMSLIM_DBG("hwep slim address define in dt %08x", btfm_slim->device_id);
} else {
BTFMSLIM_ERR("btslim-address is not defined in dt using default address");
btfm_slim->device_id = 0;
}
if (!rx_chs || !tx_chs) {
BTFMSLIM_ERR("either rx/tx channels are configured to null");
return;
}
prop = of_get_property(np, "qcom,btslimrx-channels", &len);
if (prop) {
/* Check if we need any protection for index */
rx_chs[0].ch = (uint8_t)be32_to_cpup(&prop[0]);
rx_chs[1].ch = (uint8_t)be32_to_cpup(&prop[1]);
BTFMSLIM_DBG("Rx: id\tname\tport\tch");
BTFMSLIM_DBG(" %d\t%s\t%d\t%d", rx_chs[0].id,
rx_chs[0].name, rx_chs[0].port,
rx_chs[0].ch);
BTFMSLIM_DBG(" %d\t%s\t%d\t%d", rx_chs[1].id,
rx_chs[1].name, rx_chs[1].port,
rx_chs[1].ch);
} else {
BTFMSLIM_ERR("btslimrx channels are missing in dt using default values");
}
prop = of_get_property(np, "qcom,btslimtx-channels", &len);
if (prop) {
/* Check if we need any protection for index */
tx_chs[0].ch = (uint8_t)be32_to_cpup(&prop[0]);
tx_chs[1].ch = (uint8_t)be32_to_cpup(&prop[1]);
BTFMSLIM_DBG("Tx: id\tname\tport\tch");
BTFMSLIM_DBG(" %d\t%s\t%d\t%x", tx_chs[0].id,
tx_chs[0].name, tx_chs[0].port,
tx_chs[0].ch);
BTFMSLIM_DBG(" %d\t%s\t%d\t%x", tx_chs[1].id,
tx_chs[1].name, tx_chs[1].port,
tx_chs[1].ch);
} else {
BTFMSLIM_ERR("btslimtx channels are missing in dt using default values");
}
}
#endif
static int btfm_slim_status(struct slim_device *sdev, static int btfm_slim_status(struct slim_device *sdev,
enum slim_device_status status) enum slim_device_status status)
{ {
@@ -526,6 +587,7 @@ static int btfm_slim_status(struct slim_device *sdev,
#if IS_ENABLED(CONFIG_BTFM_SLIM) #if IS_ENABLED(CONFIG_BTFM_SLIM)
ret = btfm_slim_register_codec(btfm_slim); ret = btfm_slim_register_codec(btfm_slim);
#else #else
btfm_slim_get_hwep_details(sdev, btfm_slim);
ret = btfm_slim_register_hw_ep(btfm_slim); ret = btfm_slim_register_hw_ep(btfm_slim);
#endif #endif
if (ret) if (ret)

View File

@@ -73,6 +73,9 @@ struct btfmslim {
int (*vendor_init)(struct btfmslim *btfmslim); int (*vendor_init)(struct btfmslim *btfmslim);
int (*vendor_port_en)(struct btfmslim *btfmslim, uint8_t port_num, int (*vendor_port_en)(struct btfmslim *btfmslim, uint8_t port_num,
uint8_t rxport, uint8_t enable); uint8_t rxport, uint8_t enable);
#if IS_ENABLED(CONFIG_SLIM_BTFM_CODEC)
int device_id;
#endif
}; };
extern int btfm_feedback_ch_setting; extern int btfm_feedback_ch_setting;

View File

@@ -26,15 +26,16 @@
static int bt_soc_enable_status; static int bt_soc_enable_status;
int btfm_feedback_ch_setting; int btfm_feedback_ch_setting;
static uint8_t usecase_codec;
static int btfm_slim_codec_write(struct snd_soc_component *codec, static int btfm_slim_hwep_write(struct snd_soc_component *codec,
unsigned int reg, unsigned int value) unsigned int reg, unsigned int value)
{ {
BTFMSLIM_DBG(""); BTFMSLIM_DBG("");
return 0; return 0;
} }
static unsigned int btfm_slim_codec_read(struct snd_soc_component *codec, static unsigned int btfm_slim_hwep_read(struct snd_soc_component *codec,
unsigned int reg) unsigned int reg)
{ {
BTFMSLIM_DBG(""); BTFMSLIM_DBG("");
@@ -72,25 +73,39 @@ static int btfm_put_feedback_ch_setting(struct snd_kcontrol *kcontrol,
return 1; return 1;
} }
static const struct snd_kcontrol_new status_controls[] = { static int btfm_get_codec_type(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
BTFMSLIM_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];
BTFMSLIM_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, SOC_SINGLE_EXT("BT SOC status", 0, 0, 1, 0,
btfm_soc_status_get, btfm_soc_status_get, btfm_soc_status_put),
btfm_soc_status_put),
SOC_SINGLE_EXT("BT set feedback channel", 0, 0, 1, 0, SOC_SINGLE_EXT("BT set feedback channel", 0, 0, 1, 0,
btfm_get_feedback_ch_setting, btfm_get_feedback_ch_setting,
btfm_put_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_slim_codec_probe(struct snd_soc_component *codec) static int btfm_slim_hwep_probe(struct snd_soc_component *codec)
{ {
BTFMSLIM_DBG(""); BTFMSLIM_DBG("");
snd_soc_add_component_controls(codec, status_controls,
ARRAY_SIZE(status_controls));
return 0; return 0;
} }
static void btfm_slim_codec_remove(struct snd_soc_component *codec) static void btfm_slim_hwep_remove(struct snd_soc_component *codec)
{ {
BTFMSLIM_DBG(""); BTFMSLIM_DBG("");
} }
@@ -163,6 +178,30 @@ static int btfm_slim_dai_hw_params(void *dai, uint32_t bps,
return 0; 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)) {
BTFMSLIM_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) {
*sampling_rate = 96000;
}
BTFMSLIM_INFO("current usecase codec type %s and sampling rate:%u khz",
codec_text[usecase_codec], *sampling_rate);
}
static int btfm_slim_dai_prepare(void *dai, uint32_t sampling_rate, uint32_t direction, int id) static int btfm_slim_dai_prepare(void *dai, uint32_t sampling_rate, uint32_t direction, int id)
{ {
struct hwep_data *hwep_info = (struct hwep_data *)dai; struct hwep_data *hwep_info = (struct hwep_data *)dai;
@@ -175,6 +214,7 @@ static int btfm_slim_dai_prepare(void *dai, uint32_t sampling_rate, uint32_t dir
btfmslim->direction = direction; btfmslim->direction = direction;
bt_soc_enable_status = 0; bt_soc_enable_status = 0;
btfm_get_sampling_rate(&sampling_rate);
/* save sample rate */ /* save sample rate */
btfmslim->sample_rate = sampling_rate; btfmslim->sample_rate = sampling_rate;
@@ -254,7 +294,7 @@ static int btfm_slim_dai_set_channel_map(void *dai,
* get channel handler from slimbus driver * get channel handler from slimbus driver
*/ */
rx_chs->ch = *(uint8_t *)(rx_slot + i); rx_chs->ch = *(uint8_t *)(rx_slot + i);
BTFMSLIM_DBG(" %d\t%s\t%d\t%x\t%d\t%x", rx_chs->id, BTFMSLIM_DBG(" %d\t%s\t%d\t%x", rx_chs->id,
rx_chs->name, rx_chs->port, rx_chs->ch); rx_chs->name, rx_chs->port, rx_chs->ch);
} }
@@ -265,7 +305,7 @@ static int btfm_slim_dai_set_channel_map(void *dai,
* get channel handler from slimbus driver * get channel handler from slimbus driver
*/ */
tx_chs->ch = *(uint8_t *)(tx_slot + i); tx_chs->ch = *(uint8_t *)(tx_slot + i);
BTFMSLIM_DBG(" %d\t%s\t%d\t%x\t%d\t%x", tx_chs->id, BTFMSLIM_DBG(" %d\t%s\t%d\t%x", tx_chs->id,
tx_chs->name, tx_chs->port, tx_chs->ch); tx_chs->name, tx_chs->port, tx_chs->ch);
} }
@@ -352,6 +392,45 @@ static int btfm_slim_dai_get_channel_map(void *dai,
return 0; return 0;
} }
int btfm_slim_dai_get_configs (void * dai,
struct master_hwep_configurations *hwep_config,
uint8_t id)
{
struct hwep_data *hwep_info = (struct hwep_data *)dai;
struct btfmslim *btfmslim = dev_get_drvdata(hwep_info->dev);
struct btfmslim_ch *ch = NULL;
int i = 0;
BTFMSLIM_DBG("");
hwep_config->stream_id = id;
hwep_config->device_id = btfmslim->device_id;
hwep_config->sample_rate = btfmslim->sample_rate;
hwep_config->bit_width = (uint8_t)btfmslim->bps;
hwep_config->codectype = usecase_codec;
hwep_config->direction = btfmslim->direction;
switch (id) {
case BTFM_FM_SLIM_TX:
case BTFM_BT_SCO_SLIM_TX:
ch = btfmslim->tx_chs;
break;
case BTFM_BT_SCO_A2DP_SLIM_RX:
case BTFM_BT_SPLIT_A2DP_SLIM_RX:
ch = btfmslim->rx_chs;
break;
}
for (; i < id ; i++) {
if (ch[i].id == id) {
BTFMSLIM_DBG("id matched");
hwep_config->num_channels = 1;
hwep_config->chan_num = ch[i].ch;
break;
}
}
return 1;
}
static struct hwep_dai_ops btfmslim_hw_dai_ops = { static struct hwep_dai_ops btfmslim_hw_dai_ops = {
.hwep_startup = btfm_slim_dai_startup, .hwep_startup = btfm_slim_dai_startup,
.hwep_shutdown = btfm_slim_dai_shutdown, .hwep_shutdown = btfm_slim_dai_shutdown,
@@ -359,6 +438,8 @@ static struct hwep_dai_ops btfmslim_hw_dai_ops = {
.hwep_prepare = btfm_slim_dai_prepare, .hwep_prepare = btfm_slim_dai_prepare,
.hwep_set_channel_map = btfm_slim_dai_set_channel_map, .hwep_set_channel_map = btfm_slim_dai_set_channel_map,
.hwep_get_channel_map = btfm_slim_dai_get_channel_map, .hwep_get_channel_map = btfm_slim_dai_get_channel_map,
.hwep_get_configs = btfm_slim_dai_get_configs,
.hwep_codectype = &usecase_codec,
}; };
static struct hwep_dai_driver btfmslim_dai_driver[] = { static struct hwep_dai_driver btfmslim_dai_driver[] = {
@@ -403,10 +484,10 @@ static struct hwep_dai_driver btfmslim_dai_driver[] = {
}; };
static struct hwep_comp_drv btfmslim_hw_driver = { static struct hwep_comp_drv btfmslim_hw_driver = {
.hwep_probe = btfm_slim_codec_probe, .hwep_probe = btfm_slim_hwep_probe,
.hwep_remove = btfm_slim_codec_remove, .hwep_remove = btfm_slim_hwep_remove,
.hwep_read = btfm_slim_codec_read, .hwep_read = btfm_slim_hwep_read,
.hwep_write = btfm_slim_codec_write, .hwep_write = btfm_slim_hwep_write,
}; };
int btfm_slim_register_hw_ep(struct btfmslim *btfm_slim) int btfm_slim_register_hw_ep(struct btfmslim *btfm_slim)
@@ -431,6 +512,8 @@ int btfm_slim_register_hw_ep(struct btfmslim *btfm_slim)
hwep_info->dai_drv = btfmslim_dai_driver; hwep_info->dai_drv = btfmslim_dai_driver;
hwep_info->num_dai = ARRAY_SIZE(btfmslim_dai_driver); hwep_info->num_dai = ARRAY_SIZE(btfmslim_dai_driver);
hwep_info->num_dai = 2; 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); set_bit(BTADV_AUDIO_MASTER_CONFIG, &hwep_info->flags);
/* Register to hardware endpoint */ /* Register to hardware endpoint */
ret = btfmcodec_register_hw_ep(hwep_info); ret = btfmcodec_register_hw_ep(hwep_info);

View File

@@ -15,4 +15,28 @@
// Todo protect with flags // Todo protect with flags
int btfm_slim_register_hw_ep(struct btfmslim *btfm_slim); int btfm_slim_register_hw_ep(struct btfmslim *btfm_slim);
void btfm_slim_unregister_hwep(void); void btfm_slim_unregister_hwep(void);
typedef 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
} codectype;
static 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_SLIM_HW_INTERFACE_H*/ #endif /*__LINUX_BTFM_SLIM_HW_INTERFACE_H*/