Forráskód Böngészése

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
Balakrishna Godavarthi 2 éve
szülő
commit
4a2649332e

+ 1 - 1
btfmcodec/Makefile

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

+ 98 - 30
btfmcodec/btfm_codec.c

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

+ 314 - 0
btfmcodec/btfm_codec_btadv_interface.c

@@ -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]);
+}

+ 522 - 66
btfmcodec/btfm_codec_interface.c

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

+ 31 - 13
btfmcodec/include/btfm_codec.h

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

+ 21 - 0
btfmcodec/include/btfm_codec_btadv_interface.h

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

+ 30 - 4
btfmcodec/include/btfm_codec_hw_interface.h

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

+ 65 - 20
btfmcodec/include/btfm_codec_pkt.h

@@ -6,44 +6,89 @@
 #ifndef __LINUX_BTFM_CODEC_PKT_H
 #define __LINUX_BTFM_CODEC_PKT_H
 
-typedef uint16_t btm_opcode;
+typedef uint32_t btm_opcode;
 
 struct btm_req {
 	btm_opcode opcode;
-	uint8_t len;
+	uint32_t len;
 	uint8_t *data;
-};
+}__attribute__((packed));
 
 struct btm_rsp {
 	btm_opcode opcode;
 	uint8_t status;
-};
+}__attribute__((packed));
 
 struct btm_ind {
 	btm_opcode opcode;
-	uint8_t len;
+	uint32_t len;
 	uint8_t *data;
-};
+}__attribute__((packed));
+
+struct btm_ctrl_pkt {
+	btm_opcode opcode;
+	uint32_t len;
+	uint8_t active_transport;
+	uint8_t status;
+}__attribute__((packed));
 
-#define	BTM_BTFMCODEC_PREPARE_AUDIO_BEARER_SWITCH_REQ		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
+#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,
+};
 struct btm_master_config_req {
 	btm_opcode opcode;
-	uint8_t len;
-	uint8_t stream_identifier;
-	uint32_t device_identifier;
-	uint32_t sampling_rate;
+	uint32_t len;
+	uint8_t stream_id;
+	uint32_t device_id;
+	uint32_t sample_rate;
 	uint8_t bit_width;
-	uint8_t num_of_channels;
+	uint8_t num_channels;
 	uint8_t channel_num;
 	uint8_t codec_id;
-};
+}__attribute__((packed));
+
 
+struct btm_master_shutdown_req {
+	btm_opcode opcode;
+	uint32_t len;
+	uint8_t stream_id;
+}__attribute__((packed));
+int btfmcodec_dev_enqueue_pkt(struct btfmcodec_char_device *, void *, int);
+bool btfmcodec_is_valid_cache_avb(struct btfmcodec_data *);
 #endif /* __LINUX_BTFM_CODEC_PKT_H*/

+ 1 - 1
slimbus/Makefile

@@ -5,4 +5,4 @@ bt_fm_slim-objs := btfm_slim.o btfm_slim_codec.o btfm_slim_slave.o
 obj-$(CONFIG_BTFM_SLIM) += bt_fm_slim.o
 # Below src is for BTFM Driver support based on btfm codec
 btfm_slim_codec-objs := btfm_slim.o btfm_slim_hw_interface.o btfm_slim_slave.o
-obj-$(CONFIG_SLIM_BTFM_CODEC) += btfm_slim_codec.o
+obj-$(CONFIG_SLIM_BTFM_CODEC) += btfm_slim_codec.o

+ 62 - 0
slimbus/btfm_slim.c

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

+ 3 - 0
slimbus/btfm_slim.h

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

+ 99 - 16
slimbus/btfm_slim_hw_interface.c

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

+ 24 - 0
slimbus/btfm_slim_hw_interface.h

@@ -15,4 +15,28 @@
 // Todo protect with flags
 int btfm_slim_register_hw_ep(struct btfmslim *btfm_slim);
 void btfm_slim_unregister_hwep(void);
+
+typedef enum Codec {
+	SBC = 0,
+	AAC,
+	LDAC,
+	APTX,
+	APTX_HD,
+	APTX_AD,
+	LC3,
+	APTX_AD_SPEECH,
+	LC3_VOICE,
+	APTX_AD_QLEA,
+	APTX_AD_R4,
+	NO_CODEC
+} codectype;
+
+static char const *codec_text[] = {"CODEC_TYPE_SBC", "CODEC_TYPE_AAC",
+				   "CODEC_TYPE_LDAC", "CODEC_TYPE_APTX",
+				   "CODEC_TYPE_APTX_HD", "CODEC_TYPE_APTX_AD",
+				   "CODEC_TYPE_LC3", "CODEC_TYPE_APTX_AD_SPEECH",
+				   "CODEC_TYPE_LC3_VOICE", "CODEC_TYPE_APTX_AD_QLEA",
+				   "CODEC_TYPE_APTX_AD_R4","CODEC_TYPE_INVALID"};
+
+static SOC_ENUM_SINGLE_EXT_DECL(codec_display, codec_text);
 #endif /*__LINUX_BTFM_SLIM_HW_INTERFACE_H*/