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:
@@ -1,4 +1,4 @@
|
||||
ccflags-y += -I$(BT_ROOT)/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
|
||||
|
@@ -24,7 +24,7 @@ struct btfmcodec_data *btfmcodec;
|
||||
struct device_driver driver = {.name = "btfmcodec-driver", .owner = THIS_MODULE};
|
||||
struct btfmcodec_char_device *btfmcodec_dev;
|
||||
#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) {
|
||||
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_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_INFO("File mode :%u", file->f_mode);
|
||||
btfmcodec->states.next_state = IDLE;
|
||||
BTFMCODEC_INFO("for %s by %s:%d active_clients[%d]\n",
|
||||
btfmcodec_dev->dev_name, current->comm,
|
||||
task_pid_nr(current), refcount_read(&btfmcodec_dev->active_clients));
|
||||
/* 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__);
|
||||
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);
|
||||
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,
|
||||
task_pid_nr(current), refcount_read(&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);
|
||||
skb_queue_purge(&btfmcodec_dev->txq);
|
||||
/* 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);
|
||||
}
|
||||
|
||||
btfmcodec->states.current_state = IDLE;
|
||||
btfmcodec->states.next_state = IDLE;
|
||||
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)
|
||||
{
|
||||
struct btfmcodec_char_device *btfmcodec_dev = container_of(work, struct btfmcodec_char_device, rx_work);
|
||||
struct sk_buff *skb;
|
||||
struct btm_req *req_pkt;
|
||||
uint32_t len;
|
||||
uint8_t status;
|
||||
int idx;
|
||||
|
||||
BTFMCODEC_DBG("start");
|
||||
while ((skb = skb_dequeue(&btfmcodec_dev->rxq))) {
|
||||
btm_opcode opcode = get_opcode(skb);
|
||||
// skb_pull(skb, sizeof(btm_opcode));
|
||||
btm_opcode opcode = STREAM_TO_UINT32(skb);
|
||||
skb_pull(skb, sizeof(btm_opcode));
|
||||
len = STREAM_TO_UINT32(skb);
|
||||
skb_pull(skb, sizeof(len));
|
||||
switch (opcode) {
|
||||
case BTM_BTFMCODEC_PREPARE_AUDIO_BEARER_SWITCH_REQ:
|
||||
req_pkt = (void *)skb->data;
|
||||
req_pkt->opcode = opcode;
|
||||
BTFMCODEC_DBG("BTM_BTFMCODEC_PREPARE_AUDIO_BEARER_SWITCH_REQ opcode %4x and len %d", req_pkt->opcode, req_pkt->len);
|
||||
btfmcodec_dev_enqueue_pkt(btfmcodec_dev, skb->data, skb->len);
|
||||
idx = BTM_PKT_TYPE_PREPARE_REQ;
|
||||
BTFMCODEC_DBG("BTM_BTFMCODEC_PREPARE_AUDIO_BEARER_SWITCH_REQ");
|
||||
if (len == BTM_PREPARE_AUDIO_BEARER_SWITCH_REQ_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;
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
default:
|
||||
BTFMCODEC_ERR("wrong opcode:%04x", opcode);
|
||||
BTFMCODEC_ERR("wrong opcode:%08x", opcode);
|
||||
}
|
||||
kfree_skb(skb);
|
||||
}
|
||||
@@ -172,7 +223,7 @@ static ssize_t btfmcodec_dev_write(struct file *file,
|
||||
void *kbuf;
|
||||
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));
|
||||
return -EINVAL;
|
||||
} else {
|
||||
@@ -219,16 +270,22 @@ static ssize_t btfmcodec_dev_write(struct file *file,
|
||||
free_kbuf:
|
||||
mutex_unlock(&btfmcodec_dev->lock);
|
||||
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;
|
||||
unsigned long flags;
|
||||
uint8_t *cmd = buf;
|
||||
|
||||
BTFMCODEC_DBG("start");
|
||||
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);
|
||||
if (!skb) {
|
||||
BTFMCODEC_ERR("failed to allocate memory");
|
||||
@@ -236,7 +293,7 @@ int btfmcodec_dev_enqueue_pkt(struct btfmcodec_char_device *btfmcodec_dev, uint8
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
skb_put_data(skb, buf, len);
|
||||
skb_put_data(skb, cmd, len);
|
||||
skb_queue_tail(&btfmcodec_dev->txq, skb);
|
||||
wake_up_interruptible(&btfmcodec_dev->readq);
|
||||
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;
|
||||
|
||||
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));
|
||||
return -EINVAL;
|
||||
} else {
|
||||
@@ -273,7 +330,7 @@ static __poll_t btfmcodec_dev_poll(struct file *file, poll_table *wait)
|
||||
|
||||
mutex_lock(&btfmcodec_dev->lock);
|
||||
/* 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");
|
||||
mutex_unlock(&btfmcodec_dev->lock);
|
||||
return POLLHUP;
|
||||
@@ -312,7 +369,7 @@ static ssize_t btfmcodec_dev_read(struct file *file,
|
||||
int use;
|
||||
|
||||
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));
|
||||
return -EINVAL;
|
||||
} else {
|
||||
@@ -333,7 +390,7 @@ static ssize_t btfmcodec_dev_read(struct file *file,
|
||||
return -ERESTARTSYS;
|
||||
|
||||
/* 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;
|
||||
|
||||
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,
|
||||
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;
|
||||
}
|
||||
@@ -409,7 +466,7 @@ static int __init btfmcodec_init(void)
|
||||
struct btfmcodec_state_machine *states;
|
||||
struct btfmcodec_char_device *btfmcodec_dev;
|
||||
struct device *dev;
|
||||
int ret;
|
||||
int ret, i;
|
||||
|
||||
BTFMCODEC_INFO("starting up the module");
|
||||
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
|
||||
btfmcodec->btfmcodec_dev = btfmcodec_dev;
|
||||
refcount_set(&btfmcodec_dev->active_clients, 0);
|
||||
refcount_set(&btfmcodec_dev->active_clients, 1);
|
||||
mutex_init(&btfmcodec_dev->lock);
|
||||
strlcpy(btfmcodec_dev->dev_name, "btfmcodec_dev", DEVICE_NAME_MAX_LEN);
|
||||
device_initialize(dev);
|
||||
@@ -499,6 +556,17 @@ static int __init btfmcodec_init(void)
|
||||
init_waitqueue_head(&btfmcodec_dev->readq);
|
||||
spin_lock_init(&btfmcodec_dev->tx_queue_lock);
|
||||
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;
|
||||
|
||||
free_device:
|
||||
|
314
btfmcodec/btfm_codec_btadv_interface.c
Normal file
314
btfmcodec/btfm_codec_btadv_interface.c
Normal 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]);
|
||||
}
|
@@ -6,23 +6,116 @@
|
||||
#include <linux/kernel.h>
|
||||
#include "btfm_codec.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;
|
||||
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)
|
||||
{
|
||||
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;
|
||||
int num_mixer_ctrl = hwep_info->num_mixer_ctrl;
|
||||
BTFMCODEC_DBG("");
|
||||
|
||||
// ToDo: check weather probe has to allowed when state if different
|
||||
if (states.current_state != IDLE) {
|
||||
BTFMCODEC_WARN("Received probe when state is :%s", coverttostring(states.current_state));
|
||||
if (btfmcodec_get_current_transport(state)!= IDLE) {
|
||||
BTFMCODEC_WARN("Received probe when state is :%s",
|
||||
coverttostring(btfmcodec_get_current_transport(state)));
|
||||
} 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.
|
||||
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)
|
||||
{
|
||||
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;
|
||||
BTFMCODEC_DBG("");
|
||||
|
||||
// ToDo: check whether remove has to allowed when state if different
|
||||
if (states.current_state != IDLE) {
|
||||
BTFMCODEC_WARN("Received probe when state is :%s", coverttostring(states.current_state));
|
||||
if (btfmcodec_get_current_transport(state)!= IDLE) {
|
||||
BTFMCODEC_WARN("Received probe when state is :%s",
|
||||
coverttostring(btfmcodec_get_current_transport(state)));
|
||||
} else if (hwep_info->drv && hwep_info->drv->hwep_remove) {
|
||||
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)
|
||||
{
|
||||
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;
|
||||
BTFMCODEC_DBG("");
|
||||
|
||||
// ToDo: check whether remove has to allowed when state if different
|
||||
if (states.current_state != IDLE) {
|
||||
BTFMCODEC_WARN("Received probe when state is :%s", coverttostring(states.current_state));
|
||||
// ToDo: check whether write has to allowed when state if different
|
||||
if (btfmcodec_get_current_transport(state)!= IDLE) {
|
||||
BTFMCODEC_WARN("Received probe when state is :%s",
|
||||
coverttostring(btfmcodec_get_current_transport(state)));
|
||||
} else if (hwep_info->drv && hwep_info->drv->hwep_remove) {
|
||||
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)
|
||||
{
|
||||
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;
|
||||
BTFMCODEC_DBG("");
|
||||
|
||||
// ToDo: check whether remove has to allowed when state if different
|
||||
if (states.current_state != IDLE) {
|
||||
BTFMCODEC_WARN("Received probe when state is :%s", coverttostring(states.current_state));
|
||||
// ToDo: check whether read has to allowed when state if different
|
||||
if (btfmcodec_get_current_transport(state)!= IDLE) {
|
||||
BTFMCODEC_WARN("Received probe when state is :%s",
|
||||
coverttostring(btfmcodec_get_current_transport(state)));
|
||||
} else if (hwep_info->drv && hwep_info->drv->hwep_read) {
|
||||
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;
|
||||
}
|
||||
|
||||
static int btfmcodec_dai_startup(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
int btfmcodec_hwep_startup(struct btfmcodec_data *btfmcodec)
|
||||
{
|
||||
struct btfmcodec_data *btfmcodec = snd_soc_component_get_drvdata(dai->component);
|
||||
struct btfmcodec_state_machine states = 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);
|
||||
|
||||
BTFMCODEC_DBG("substream = %s stream = %d dai->name = %s",
|
||||
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) {
|
||||
if (dai_drv && dai_drv->dai_ops && dai_drv->dai_ops->hwep_startup) {
|
||||
return dai_drv->dai_ops->hwep_startup((void *)btfmcodec->hwep_info);
|
||||
} else {
|
||||
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;
|
||||
}
|
||||
|
||||
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,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct btfmcodec_data *btfmcodec = snd_soc_component_get_drvdata(dai->component);
|
||||
struct btfmcodec_state_machine states = btfmcodec->states;
|
||||
struct hwep_dai_driver *dai_drv = (struct hwep_dai_driver *)btfmcodec_get_dai_drvdata(btfmcodec->hwep_info);
|
||||
struct btfmcodec_state_machine *state = &btfmcodec->states;
|
||||
|
||||
BTFMCODEC_DBG("dai->name: %s, dai->id: %d, dai->rate: %d", dai->name,
|
||||
dai->id, dai->rate);
|
||||
|
||||
// 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_shutdown) {
|
||||
dai_drv->dai_ops->hwep_shutdown((void *)btfmcodec->hwep_info, dai->id);
|
||||
if (btfmcodec_get_current_transport(state) != IDLE &&
|
||||
btfmcodec_get_current_transport(state) != BT_Connected) {
|
||||
BTFMCODEC_WARN("not allowing shutdown as state is:%s",
|
||||
coverttostring(btfmcodec_get_current_transport(state)));
|
||||
/* 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 btfmcodec_data *btfmcodec = snd_soc_component_get_drvdata(dai->component);
|
||||
struct btfmcodec_state_machine states = btfmcodec->states;
|
||||
struct hwep_dai_driver *dai_drv = (struct hwep_dai_driver *)btfmcodec_get_dai_drvdata(btfmcodec->hwep_info);
|
||||
struct btfmcodec_state_machine *state = &btfmcodec->states;
|
||||
uint32_t bps = params_width(params);
|
||||
uint32_t direction = substream->stream;
|
||||
|
||||
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),
|
||||
params_channels(params));
|
||||
// ToDo: check whether hw_params 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_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);
|
||||
|
||||
if (btfmcodec_get_current_transport(state) != IDLE &&
|
||||
btfmcodec_get_current_transport(state) != BT_Connected) {
|
||||
BTFMCODEC_WARN("caching bps as state is :%s",
|
||||
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 {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int btfmcodec_dai_prepare(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 states = btfmcodec->states;
|
||||
struct hwep_dai_driver *dai_drv = (struct hwep_dai_driver *)btfmcodec_get_dai_drvdata(btfmcodec->hwep_info);
|
||||
struct btfmcodec_state_machine *state = &btfmcodec->states;
|
||||
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 direction = substream->stream;
|
||||
int id = dai->id;
|
||||
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,
|
||||
id, sampling_rate, direction);
|
||||
// ToDo: check whether hw_params 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_drv && dai_drv->dai_ops && dai_drv->dai_ops->hwep_prepare) {
|
||||
ret = dai_drv->dai_ops->hwep_prepare((void *)btfmcodec->hwep_info, sampling_rate,
|
||||
direction, id);
|
||||
if (ret == 0 && test_bit(BTADV_AUDIO_MASTER_CONFIG, &btfmcodec->hwep_info->flags)) {
|
||||
BTFMCODEC_DBG("configuring master now");
|
||||
if (btfmcodec_get_current_transport(state) != IDLE &&
|
||||
btfmcodec_get_current_transport(state) != BT_Connected) {
|
||||
BTFMCODEC_WARN("caching required info as state is:%s",
|
||||
coverttostring(btfmcodec_get_current_transport(state)));
|
||||
ret = btfmcodec_check_and_cache_configs(btfmcodec, sampling_rate, direction,
|
||||
id, *codectype);
|
||||
} else {
|
||||
ret = btfmcodec_hwep_prepare(btfmcodec, sampling_rate, direction, id);
|
||||
if (ret >= 0) {
|
||||
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 {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
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_state_machine states = btfmcodec->states;
|
||||
struct hwep_dai_driver *dai_drv = (struct hwep_dai_driver *)btfmcodec_get_dai_drvdata(btfmcodec->hwep_info);
|
||||
|
||||
BTFMCODEC_DBG("");
|
||||
// ToDo: check whether hw_params 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_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 {
|
||||
return -1;
|
||||
return btfmcodec_hwep_set_channel_map((void *)btfmcodec->hwep_info, tx_num,
|
||||
tx_slot, rx_num, rx_slot);
|
||||
}
|
||||
|
||||
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,
|
||||
unsigned int *tx_num, unsigned int *tx_slot,
|
||||
unsigned int *rx_num, unsigned int *rx_slot)
|
||||
{
|
||||
struct btfmcodec_data *btfmcodec = snd_soc_component_get_drvdata(dai->component);
|
||||
struct btfmcodec_state_machine states = btfmcodec->states;
|
||||
struct hwep_dai_driver *dai_drv = (struct hwep_dai_driver *)btfmcodec_get_dai_drvdata(btfmcodec->hwep_info);
|
||||
// struct btfmcodec_state_machine states = btfmcodec->states;
|
||||
|
||||
BTFMCODEC_DBG("");
|
||||
// ToDo: check whether hw_params has to allowed when state if different
|
||||
if (states.current_state != IDLE) {
|
||||
// ToDo: get_channel_map is not needed for new driver
|
||||
/* if (states.current_state != IDLE) {
|
||||
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 {
|
||||
return -1;
|
||||
}
|
||||
*/ return btfmcodec_hwep_get_channel_map((void *)btfmcodec->hwep_info,
|
||||
tx_num, tx_slot, rx_num,
|
||||
rx_slot, dai->id);
|
||||
// }
|
||||
|
||||
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 = {
|
||||
.startup = btfmcodec_dai_startup,
|
||||
.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)
|
||||
{
|
||||
struct btfmcodec_data *btfmcodec;
|
||||
struct btfmcodec_char_device *btfmcodec_dev;
|
||||
struct device *dev;
|
||||
struct hwep_dai_driver *dai_drv;
|
||||
int i, ret;
|
||||
|
||||
btfmcodec = btfm_get_btfmcodec();
|
||||
btfmcodec_dev = btfmcodec->btfmcodec_dev;
|
||||
dev = &btfmcodec->dev;
|
||||
btfmcodec_dai_info = kzalloc((sizeof(struct snd_soc_dai_driver) * hwep_info->num_dai), GFP_KERNEL);
|
||||
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);
|
||||
ret = snd_soc_register_component(dev, &btfmcodec_codec_component,
|
||||
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;
|
||||
}
|
||||
|
@@ -20,24 +20,36 @@
|
||||
|
||||
#define DEVICE_NAME_MAX_LEN 64
|
||||
|
||||
enum btfmcodec_states {
|
||||
/*Default state of btfm codec driver */
|
||||
typedef enum btfmcodec_states {
|
||||
/*Default state of kernel proxy driver */
|
||||
IDLE = 0,
|
||||
/* When BT is active transport */
|
||||
BT_Connected = 1,
|
||||
/* Waiting for BT bearer indication after configuring HW ports */
|
||||
BT_Connecting = 2,
|
||||
/* When BTADV_AUDIO is active transport */
|
||||
BTADV_AUDIO_Connected = 3,
|
||||
/* Waiting for BTADV_AUDIO bearer switch indications */
|
||||
BTADV_AUDIO_Connecting = 4
|
||||
BT_Connecting = 1,
|
||||
/* When BT is active transport */
|
||||
BT_Connected = 2,
|
||||
/* Waiting for BTADV Audio bearer switch indications */
|
||||
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);
|
||||
struct btfmcodec_state_machine {
|
||||
enum btfmcodec_states prev_state;
|
||||
enum btfmcodec_states current_state;
|
||||
enum btfmcodec_states next_state;
|
||||
struct mutex state_machine_lock;
|
||||
btfmcodec_state prev_state;
|
||||
btfmcodec_state current_state;
|
||||
btfmcodec_state next_state;
|
||||
};
|
||||
|
||||
struct btfmcodec_char_device {
|
||||
@@ -46,11 +58,17 @@ struct btfmcodec_char_device {
|
||||
struct mutex lock;
|
||||
int reuse_minor;
|
||||
char dev_name[DEVICE_NAME_MAX_LEN];
|
||||
struct workqueue_struct *workqueue;
|
||||
struct sk_buff_head rxq;
|
||||
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;
|
||||
spinlock_t tx_queue_lock;
|
||||
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;
|
||||
};
|
||||
|
||||
@@ -59,8 +77,8 @@ struct btfmcodec_data {
|
||||
struct btfmcodec_state_machine states;
|
||||
struct btfmcodec_char_device *btfmcodec_dev;
|
||||
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);
|
||||
#endif /*__LINUX_BTFM_CODEC_H */
|
||||
|
21
btfmcodec/include/btfm_codec_btadv_interface.h
Normal file
21
btfmcodec/include/btfm_codec_btadv_interface.h
Normal 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 */
|
@@ -20,11 +20,32 @@
|
||||
#define BTADV_AUDIO_MASTER_CONFIG 0
|
||||
#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 {
|
||||
int (*hwep_probe) (struct snd_soc_component *component);
|
||||
void (*hwep_remove) (struct snd_soc_component *component);
|
||||
unsigned int (*hwep_read)(struct snd_soc_component *component, unsigned int reg);
|
||||
int (*hwep_write)(struct snd_soc_component *componentm, unsigned int reg, unsigned int value);
|
||||
int (*hwep_probe) (struct snd_soc_component *);
|
||||
void (*hwep_remove) (struct snd_soc_component *);
|
||||
unsigned int (*hwep_read)(struct snd_soc_component *, unsigned int );
|
||||
int (*hwep_write)(struct snd_soc_component *, unsigned int,
|
||||
unsigned int);
|
||||
};
|
||||
|
||||
struct hwep_dai_ops {
|
||||
@@ -36,6 +57,9 @@ 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);
|
||||
uint8_t *hwep_codectype;
|
||||
};
|
||||
|
||||
struct hwep_dai_driver {
|
||||
@@ -51,7 +75,9 @@ struct hwep_data {
|
||||
char driver_name [DEVICE_NAME_MAX_LEN];
|
||||
struct hwep_comp_drv *drv;
|
||||
struct hwep_dai_driver *dai_drv;
|
||||
struct snd_kcontrol_new *mixer_ctrl;
|
||||
int num_dai;
|
||||
int num_mixer_ctrl;
|
||||
unsigned long flags;
|
||||
};
|
||||
|
||||
|
@@ -6,44 +6,89 @@
|
||||
#ifndef __LINUX_BTFM_CODEC_PKT_H
|
||||
#define __LINUX_BTFM_CODEC_PKT_H
|
||||
|
||||
typedef uint16_t btm_opcode;
|
||||
typedef uint32_t btm_opcode;
|
||||
|
||||
struct btm_req {
|
||||
btm_opcode opcode;
|
||||
uint8_t len;
|
||||
uint32_t len;
|
||||
uint8_t *data;
|
||||
};
|
||||
}__attribute__((packed));
|
||||
|
||||
struct btm_rsp {
|
||||
btm_opcode opcode;
|
||||
uint8_t status;
|
||||
};
|
||||
}__attribute__((packed));
|
||||
|
||||
struct btm_ind {
|
||||
btm_opcode opcode;
|
||||
uint8_t len;
|
||||
uint32_t len;
|
||||
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 {
|
||||
btm_opcode opcode;
|
||||
uint8_t len;
|
||||
uint8_t stream_identifier;
|
||||
uint32_t device_identifier;
|
||||
uint32_t sampling_rate;
|
||||
uint32_t len;
|
||||
uint8_t stream_id;
|
||||
uint32_t device_id;
|
||||
uint32_t sample_rate;
|
||||
uint8_t bit_width;
|
||||
uint8_t num_of_channels;
|
||||
uint8_t num_channels;
|
||||
uint8_t channel_num;
|
||||
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*/
|
||||
|
@@ -515,6 +515,67 @@ int btfm_slim_hw_deinit(struct btfmslim *btfmslim)
|
||||
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,
|
||||
enum slim_device_status status)
|
||||
{
|
||||
@@ -526,6 +587,7 @@ static int btfm_slim_status(struct slim_device *sdev,
|
||||
#if IS_ENABLED(CONFIG_BTFM_SLIM)
|
||||
ret = btfm_slim_register_codec(btfm_slim);
|
||||
#else
|
||||
btfm_slim_get_hwep_details(sdev, btfm_slim);
|
||||
ret = btfm_slim_register_hw_ep(btfm_slim);
|
||||
#endif
|
||||
if (ret)
|
||||
|
@@ -73,6 +73,9 @@ struct btfmslim {
|
||||
int (*vendor_init)(struct btfmslim *btfmslim);
|
||||
int (*vendor_port_en)(struct btfmslim *btfmslim, uint8_t port_num,
|
||||
uint8_t rxport, uint8_t enable);
|
||||
#if IS_ENABLED(CONFIG_SLIM_BTFM_CODEC)
|
||||
int device_id;
|
||||
#endif
|
||||
};
|
||||
|
||||
extern int btfm_feedback_ch_setting;
|
||||
|
@@ -26,15 +26,16 @@
|
||||
|
||||
static int bt_soc_enable_status;
|
||||
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)
|
||||
{
|
||||
BTFMSLIM_DBG("");
|
||||
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)
|
||||
{
|
||||
BTFMSLIM_DBG("");
|
||||
@@ -72,25 +73,39 @@ static int btfm_put_feedback_ch_setting(struct snd_kcontrol *kcontrol,
|
||||
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,
|
||||
btfm_soc_status_get,
|
||||
btfm_soc_status_put),
|
||||
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)
|
||||
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("");
|
||||
snd_soc_add_component_controls(codec, status_controls,
|
||||
ARRAY_SIZE(status_controls));
|
||||
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("");
|
||||
}
|
||||
@@ -163,6 +178,30 @@ static int btfm_slim_dai_hw_params(void *dai, uint32_t bps,
|
||||
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)
|
||||
{
|
||||
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;
|
||||
bt_soc_enable_status = 0;
|
||||
|
||||
btfm_get_sampling_rate(&sampling_rate);
|
||||
/* save sample 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
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -265,7 +305,7 @@ static int btfm_slim_dai_set_channel_map(void *dai,
|
||||
* get channel handler from slimbus driver
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -352,6 +392,45 @@ 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)
|
||||
{
|
||||
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 = {
|
||||
.hwep_startup = btfm_slim_dai_startup,
|
||||
.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_set_channel_map = btfm_slim_dai_set_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[] = {
|
||||
@@ -403,10 +484,10 @@ static struct hwep_dai_driver btfmslim_dai_driver[] = {
|
||||
};
|
||||
|
||||
static struct hwep_comp_drv btfmslim_hw_driver = {
|
||||
.hwep_probe = btfm_slim_codec_probe,
|
||||
.hwep_remove = btfm_slim_codec_remove,
|
||||
.hwep_read = btfm_slim_codec_read,
|
||||
.hwep_write = btfm_slim_codec_write,
|
||||
.hwep_probe = btfm_slim_hwep_probe,
|
||||
.hwep_remove = btfm_slim_hwep_remove,
|
||||
.hwep_read = btfm_slim_hwep_read,
|
||||
.hwep_write = btfm_slim_hwep_write,
|
||||
};
|
||||
|
||||
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->num_dai = ARRAY_SIZE(btfmslim_dai_driver);
|
||||
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);
|
||||
|
@@ -15,4 +15,28 @@
|
||||
// Todo protect with flags
|
||||
int btfm_slim_register_hw_ep(struct btfmslim *btfm_slim);
|
||||
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*/
|
||||
|
Reference in New Issue
Block a user