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)/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
|
||||||
|
@@ -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:
|
||||||
|
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 <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;
|
||||||
}
|
}
|
||||||
|
@@ -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 */
|
||||||
|
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 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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -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*/
|
||||||
|
@@ -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)
|
||||||
|
@@ -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;
|
||||||
|
@@ -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);
|
||||||
|
@@ -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*/
|
||||||
|
Reference in New Issue
Block a user