Przeglądaj źródła

soundwire: Update soundwire driver to support btfmcodec

This change add supports in both btfmcodec and soundwire
to support btfmcodec driver has interface.

Change-Id: I2e77afaea44778147b362175aae33961dcc5042e
Signed-off-by: Balakrishna Godavarthi <[email protected]>
Balakrishna Godavarthi 2 lat temu
rodzic
commit
897ecff742

+ 16 - 3
bt_modules.bzl

@@ -1,6 +1,7 @@
 PWR_PATH = "pwr"
 SLIMBUS_PATH = "slimbus"
 FMRTC_PATH = "rtc6226"
+BTFMCODEC_PATH = "btfmcodec"
 
 # This dictionary holds all the BT modules included in the bt-kernel
 bt_modules = {}
@@ -75,11 +76,10 @@ register_bt_modules(
     deps = [":%b_btpower"],
 )
 
-# Not enabled/compiling until btfmcodec is enabled
 register_bt_modules(
     name = "btfm_slim_codec",
     path = SLIMBUS_PATH,
-    # config_opt = "CONFIG_SLIM_BTFM_CODEC",
+    config_opt = "CONFIG_SLIM_BTFM_CODEC",
     srcs = [
         "btfm_slim.c",
         "btfm_slim.h",
@@ -88,7 +88,20 @@ register_bt_modules(
         "btfm_slim_hw_interface.c",
         "btfm_slim_hw_interface.h",
     ],
-    deps = [":%b_btpower", ":btfmcodec_headers"],
+    deps = [":%b_btpower", ":%b_btfmcodec", ":btfmcodec_headers"],
+)
+
+register_bt_modules(
+   name = "btfmcodec",
+   path = BTFMCODEC_PATH,
+   config_opt = "CONFIG_BTFM_CODEC",
+   srcs = [
+        "btfm_codec.c",
+        "btfm_codec_btadv_interface.c",
+	"btfm_codec_hw_interface.c",
+	"btfm_codec_interface.c",
+	],
+   deps = [":btfmcodec_headers"],
 )
 
 register_bt_modules(

+ 70 - 2
btfmcodec/btfm_codec.c

@@ -23,6 +23,7 @@ static dev_t dev_major;
 struct btfmcodec_data *btfmcodec;
 struct device_driver driver = {.name = "btfmcodec-driver", .owner = THIS_MODULE};
 struct btfmcodec_char_device *btfmcodec_dev;
+bool is_cp_supported = true;
 
 #define cdev_to_btfmchardev(_cdev) container_of(_cdev, struct btfmcodec_char_device, cdev)
 #define MIN_PKT_LEN  0x9
@@ -183,6 +184,22 @@ static void btfmcodec_dev_rxwork(struct work_struct *work)
 				status);
 			wake_up_interruptible(&btfmcodec_dev->rsp_wait_q[idx]);
 			break;
+		case BTM_BTFMCODEC_CODEC_CONFIG_DMA_RSP:
+			idx = BTM_PKT_TYPE_DMA_CONFIG_RSP;
+			if (len == BTM_CODEC_CONFIG_DMA_RSP_LEN) {
+				status = skb->data[1];
+				if (status == MSG_SUCCESS)
+					btfmcodec_dev->status[idx] = BTM_RSP_RECV;
+				else
+					btfmcodec_dev->status[idx] = BTM_FAIL_RESP_RECV;
+			} else {
+				BTFMCODEC_ERR("wrong packet format with len:%d", len);
+				btfmcodec_dev->status[idx] = BTM_FAIL_RESP_RECV;
+			}
+			BTFMCODEC_INFO("Rx BTM_BTFMCODEC_CODEC_CONFIG_DMA_RSP status:%d",
+				status);
+			wake_up_interruptible(&btfmcodec_dev->rsp_wait_q[idx]);
+			break;
 		case BTM_BTFMCODEC_CTRL_MASTER_SHUTDOWN_RSP:
 			idx = BTM_PKT_TYPE_MASTER_SHUTDOWN_RSP;
 			if (len == BTM_MASTER_CONFIG_RSP_LEN) {
@@ -442,6 +459,56 @@ static ssize_t btfmcodec_dev_read(struct file *file,
 
 	return use;
 }
+
+bool isCpSupported(void)
+{
+	return is_cp_supported;
+}
+
+static long btfmcodec_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct btfmcodec_data *btfmcodec = file->private_data;
+	struct hwep_data *hwep_info;
+
+	BTFMCODEC_INFO("%s: command %04x", __func__, cmd);
+
+	mutex_lock(&btfmcodec->hwep_drv_lock);
+	hwep_info = btfmcodec->hwep_info;
+	if (!hwep_info) {
+		BTFMCODEC_WARN("%s: HWEP is not registered with btfmcodec", __func__);
+		BTFMCODEC_WARN("%s: caching required info", __func__);
+		is_cp_supported = ((int)arg == 1) ? true : false;
+		mutex_unlock(&btfmcodec->hwep_drv_lock);
+		return 0;
+	}
+
+	mutex_unlock(&btfmcodec->hwep_drv_lock);
+	switch (cmd) {
+	case BTM_CP_UPDATE: {
+		if ((int)arg == 1) {
+			if (!strcmp(hwep_info->driver_name, "btfmslim"))
+				set_bit(BTADV_AUDIO_MASTER_CONFIG, &hwep_info->flags);
+			else if (!strcmp(hwep_info->driver_name, "btfmswr_slave"))
+				set_bit(BTADV_CONFIGURE_DMA, &hwep_info->flags);
+			BTFMCODEC_INFO("%s: This target support CP hwep %s",
+					__func__, hwep_info->driver_name);
+		} else {
+			clear_bit(BTADV_AUDIO_MASTER_CONFIG, &hwep_info->flags);
+			clear_bit(BTADV_CONFIGURE_DMA, &hwep_info->flags);
+			BTFMCODEC_INFO("%s: This target support doesn't CP", __func__);
+		}
+
+	BTFMCODEC_INFO("%s: mastr %d dma codec %d", __func__,
+			(int)test_bit(BTADV_AUDIO_MASTER_CONFIG, &hwep_info->flags),
+			(int)test_bit(BTADV_CONFIGURE_DMA, &hwep_info->flags));
+		break;
+	} default: {
+		BTFMCODEC_ERR("%s unhandled cmd %04x", __func__, cmd);
+	}
+	}
+
+	return 0;
+}
 static const struct file_operations btfmcodec_fops = {
 	.owner = THIS_MODULE,
 	.open = btfmcodec_dev_open,
@@ -450,8 +517,8 @@ static const struct file_operations btfmcodec_fops = {
 	.poll = btfmcodec_dev_poll,
 	.read = btfmcodec_dev_read,
 	/* For Now add no hookups for below callbacks */
-	.unlocked_ioctl = NULL,
-	.compat_ioctl = NULL,
+	.unlocked_ioctl = btfmcodec_ioctl,
+	.compat_ioctl = btfmcodec_ioctl,
 };
 
 static ssize_t btfmcodec_attributes_store(struct device *dev,
@@ -503,6 +570,7 @@ static int __init btfmcodec_init(void)
 		return -ENOMEM;
 	}
 
+	mutex_init(&btfmcodec->hwep_drv_lock);
 	states = &btfmcodec->states;
 	states->current_state = IDLE;
 	states->next_state = IDLE;

+ 4 - 7
btfmcodec/btfm_codec_hw_interface.c

@@ -6,17 +6,14 @@
 #include "btfm_codec_hw_interface.h"
 #include "btfm_codec_interface.h"
 
-struct mutex hwep_drv_lock;
-
 int btfmcodec_register_hw_ep (struct hwep_data *ep_info)
 {
 	struct btfmcodec_data *btfmcodec;
 	struct hwep_data *hwep_info;
 	int ret = 0;
 
-	// ToDo Check wether we need mutex_init api
-	mutex_lock(&hwep_drv_lock);
 	btfmcodec = btfm_get_btfmcodec();
+	mutex_lock(&btfmcodec->hwep_drv_lock);
 	if (!btfmcodec) {
 		BTFMCODEC_ERR("btfm codec driver it not initialized");
 		ret = -EPERM;
@@ -54,7 +51,7 @@ int btfmcodec_register_hw_ep (struct hwep_data *ep_info)
 		&hwep_info->flags));
 	ret = btfm_register_codec(hwep_info);
 end:
-	mutex_unlock(&hwep_drv_lock);
+	mutex_unlock(&btfmcodec->hwep_drv_lock);
 	return ret;
 }
 
@@ -64,8 +61,8 @@ int btfmcodec_unregister_hw_ep (char *driver_name)
 	struct hwep_data *hwep_info;
 	int ret;
 
-	mutex_lock(&hwep_drv_lock);
 	btfmcodec = btfm_get_btfmcodec();
+	mutex_lock(&btfmcodec->hwep_drv_lock);
 	if (!btfmcodec) {
 		BTFMCODEC_ERR("btfm codec driver it not initialized");
 		ret = -EPERM;
@@ -91,7 +88,7 @@ int btfmcodec_unregister_hw_ep (char *driver_name)
 		goto end;
 	}
 end:
-	mutex_unlock(&hwep_drv_lock);
+	mutex_unlock(&btfmcodec->hwep_drv_lock);
 	return ret;
 }
 

+ 106 - 22
btfmcodec/btfm_codec_interface.c

@@ -439,7 +439,7 @@ static int btfmcodec_configure_master(struct btfmcodec_data *btfmcodec, uint8_t
 	struct btfmcodec_char_device *btfmcodec_dev = btfmcodec->btfmcodec_dev;
 	struct hwep_data *hwep_info = btfmcodec->hwep_info;
 	struct master_hwep_configurations hwep_configs;
-	struct btm_master_config_req config_reg;
+	struct btm_master_config_req config_req;
 	struct hwep_dai_driver *dai_drv = (struct hwep_dai_driver *)
 					      btfmcodec_get_dai_drvdata(hwep_info);
 	wait_queue_head_t *rsp_wait_q =
@@ -456,28 +456,28 @@ static int btfmcodec_configure_master(struct btfmcodec_data *btfmcodec, uint8_t
 	}
 
 	BTFMCODEC_INFO("framing packet for %d", id);
-	config_reg.opcode = BTM_BTFMCODEC_MASTER_CONFIG_REQ;
-	config_reg.len = BTM_MASTER_CONFIG_REQ_LEN;
-	config_reg.stream_id = hwep_configs.stream_id;
-	config_reg.device_id = hwep_configs.device_id;
-	config_reg.sample_rate = hwep_configs.sample_rate;
-	config_reg.bit_width = hwep_configs.bit_width;
-	config_reg.num_channels = hwep_configs.num_channels;
-	config_reg.channel_num = hwep_configs.chan_num;
-	config_reg.codec_id = hwep_configs.codectype;
+	config_req.opcode = BTM_BTFMCODEC_MASTER_CONFIG_REQ;
+	config_req.len = BTM_MASTER_CONFIG_REQ_LEN;
+	config_req.stream_id = hwep_configs.stream_id;
+	config_req.device_id = hwep_configs.device_id;
+	config_req.sample_rate = hwep_configs.sample_rate;
+	config_req.bit_width = hwep_configs.bit_width;
+	config_req.num_channels = hwep_configs.num_channels;
+	config_req.channel_num = hwep_configs.chan_num;
+	config_req.codec_id = hwep_configs.codectype;
 	BTFMCODEC_DBG("================================================\n");
-	BTFMCODEC_DBG("config_reg.len :%d", config_reg.len);
-	BTFMCODEC_DBG("config_reg.stream_id :%d", config_reg.stream_id);
-	BTFMCODEC_DBG("config_reg.device_id :%d", config_reg.device_id);
-	BTFMCODEC_DBG("config_reg.sample_rate :%d", config_reg.sample_rate);
-	BTFMCODEC_DBG("config_reg.bit_width :%d", config_reg.bit_width);
-	BTFMCODEC_DBG("config_reg.num_channels :%d", config_reg.num_channels);
-	BTFMCODEC_DBG("config_reg.channel_num :%d", config_reg.channel_num);
-	BTFMCODEC_DBG("config_reg.codec_id :%d", config_reg.codec_id);
+	BTFMCODEC_DBG("dma_config_req.len :%d", config_req.len);
+	BTFMCODEC_DBG("dma_config_req.stream_id :%d", config_req.stream_id);
+	BTFMCODEC_DBG("dma_config_req.device_id :%d", config_req.device_id);
+	BTFMCODEC_DBG("dma_config_req.sample_rate :%d", config_req.sample_rate);
+	BTFMCODEC_DBG("dma_config_req.bit_width :%d", config_req.bit_width);
+	BTFMCODEC_DBG("dma_config_req.num_channels :%d", config_req.num_channels);
+	BTFMCODEC_DBG("dma_config_req.channel_num :%d", config_req.channel_num);
+	BTFMCODEC_DBG("dma_config_req.codec_id :%d", config_req.codec_id);
 	BTFMCODEC_DBG("================================================\n");
 	/* See if we need to protect below with lock */
 	*status = BTM_WAITING_RSP;
-	btfmcodec_dev_enqueue_pkt(btfmcodec_dev, &config_reg, (config_reg.len +
+	btfmcodec_dev_enqueue_pkt(btfmcodec_dev, &config_req, (config_req.len +
 				BTM_HEADER_LEN));
 	ret = wait_event_interruptible_timeout(*rsp_wait_q,
 		(*status) != BTM_WAITING_RSP,
@@ -496,6 +496,73 @@ static int btfmcodec_configure_master(struct btfmcodec_data *btfmcodec, uint8_t
 	return ret;
 }
 
+static int btfmcodec_configure_dma(struct btfmcodec_data *btfmcodec, uint8_t id)
+{
+	struct btfmcodec_char_device *btfmcodec_dev = btfmcodec->btfmcodec_dev;
+	struct hwep_data *hwep_info = btfmcodec->hwep_info;
+	struct hwep_dma_configurations dma_config;
+	struct btm_dma_config_req dma_config_req;
+	struct hwep_dai_driver *dai_drv = (struct hwep_dai_driver *)
+					      btfmcodec_get_dai_drvdata(hwep_info);
+	wait_queue_head_t *rsp_wait_q =
+		&btfmcodec_dev->rsp_wait_q[BTM_PKT_TYPE_DMA_CONFIG_RSP];
+	uint8_t *status = &btfmcodec_dev->status[BTM_PKT_TYPE_DMA_CONFIG_RSP];
+	int ret = 0;
+
+	if (dai_drv && dai_drv->dai_ops && dai_drv->dai_ops->hwep_get_configs) {
+		dai_drv->dai_ops->hwep_get_configs((void *)btfmcodec->hwep_info,
+						   &dma_config, id);
+	} else {
+		BTFMCODEC_ERR("No hwep_get_configs is set by hw ep driver");
+		return -1;
+	}
+
+	BTFMCODEC_INFO("framing packet for %d", id);
+	dma_config_req.opcode = BTM_BTFMCODEC_CODEC_CONFIG_DMA_REQ;
+	dma_config_req.len = BTM_CODEC_CONFIG_DMA_REQ_LEN;
+	dma_config_req.stream_id = dma_config.stream_id;
+	dma_config_req.sample_rate = dma_config.sample_rate;
+	dma_config_req.bit_width = dma_config.bit_width;
+	dma_config_req.num_channels = dma_config.num_channels;
+	dma_config_req.codec_id = dma_config.codectype;
+	dma_config_req.lpaif = dma_config.lpaif;
+	dma_config_req.inf_index = dma_config.inf_index;
+	dma_config_req.active_channel_mask = dma_config.active_channel_mask;
+
+	BTFMCODEC_DBG("================================================\n");
+	BTFMCODEC_DBG("dma_config_req.len :%d", dma_config_req.len);
+	BTFMCODEC_DBG("dma_config_req.stream_id :%d", dma_config_req.stream_id);
+	BTFMCODEC_DBG("dma_config_req.sample_rate :%d", dma_config_req.sample_rate);
+	BTFMCODEC_DBG("dma_config_req.bit_width :%d", dma_config_req.bit_width);
+	BTFMCODEC_DBG("dma_config_req.num_channels :%d", dma_config_req.num_channels);
+	BTFMCODEC_DBG("dma_config_req.codec_id :%d", dma_config_req.codec_id);
+	BTFMCODEC_DBG("dma_config_req.lpaif :%d", dma_config_req.lpaif);
+	BTFMCODEC_DBG("dma_config_req.inf_index :%d", dma_config_req.inf_index);
+	BTFMCODEC_DBG("dma_config_req.active_channel_mask :%d", dma_config_req.active_channel_mask);
+	BTFMCODEC_DBG("================================================\n");
+
+	*status = BTM_WAITING_RSP;
+	btfmcodec_dev_enqueue_pkt(btfmcodec_dev, &dma_config_req, (dma_config_req.len +
+				BTM_HEADER_LEN));
+
+	ret = wait_event_interruptible_timeout(*rsp_wait_q,
+		(*status) != BTM_WAITING_RSP,
+		msecs_to_jiffies(BTM_MASTER_DMA_CONFIG_RSP_TIMEOUT));
+
+	if (ret == 0) {
+		BTFMCODEC_ERR("failed to recevie response from BTADV audio Manager");
+		ret = -ETIMEDOUT;
+	} else {
+		if (*status == BTM_RSP_RECV)
+			return 0;
+		else if (*status == BTM_FAIL_RESP_RECV ||
+			 *status == BTM_RSP_NOT_RECV_CLIENT_KILLED)
+			return -1;
+	}
+
+	return ret;
+}
+
 int btfmcodec_hwep_prepare(struct btfmcodec_data *btfmcodec, uint32_t sampling_rate,
 			uint32_t direction, int id)
 {
@@ -508,14 +575,20 @@ int btfmcodec_hwep_prepare(struct btfmcodec_data *btfmcodec, uint32_t sampling_r
 	if (dai_drv && dai_drv->dai_ops && dai_drv->dai_ops->hwep_prepare) {
 		ret = dai_drv->dai_ops->hwep_prepare((void *)hwep_info, sampling_rate,
 						      direction, id);
+		BTFMCODEC_ERR("%s: hwep info %d", __func__, hwep_info->flags);
 		if (ret == 0 && test_bit(BTADV_AUDIO_MASTER_CONFIG, &hwep_info->flags)) {
 			ret = btfmcodec_configure_master(btfmcodec, (uint8_t)id);
 			if (ret < 0) {
 				BTFMCODEC_ERR("failed to configure master error %d", ret);
-				/* close slave port and reset the state*/
 				btfmcodec_set_current_state(state, IDLE);
-				/* we don't need to do shutdown, ASOC is doing it */
-//				btfmcodec_hwep_shutdown(btfmcodec, id);
+			} else {
+				btfmcodec_set_current_state(state, BT_Connected);
+			}
+		} else if (ret == 0 && test_bit(BTADV_CONFIGURE_DMA, &hwep_info->flags)) {
+			ret  = btfmcodec_configure_dma(btfmcodec, (uint8_t)id);
+			if (ret < 0) {
+				BTFMCODEC_ERR("failed to configure Codec DMA %d", ret);
+				btfmcodec_set_current_state(state, IDLE);
 			} else {
 				btfmcodec_set_current_state(state, BT_Connected);
 			}
@@ -785,6 +858,17 @@ int btfm_register_codec(struct hwep_data *hwep_info)
 	BTFMCODEC_INFO("btfmcodec_wq_prepare_bearer:%p", btfmcodec_wq_prepare_bearer);
 	BTFMCODEC_INFO("btfmcodec_wq_hwep_shutdown:%p", btfmcodec_wq_hwep_shutdown);
 
+	if (isCpSupported()) {
+		if (!strcmp(hwep_info->driver_name, "btfmslim"))
+			set_bit(BTADV_AUDIO_MASTER_CONFIG, &hwep_info->flags);
+		else if (!strcmp(hwep_info->driver_name, "btfmswr_slave"))
+			set_bit(BTADV_CONFIGURE_DMA, &hwep_info->flags);
+
+	BTFMCODEC_INFO("%s: master %d dma codec %d", __func__,
+			(int)test_bit(BTADV_AUDIO_MASTER_CONFIG, &hwep_info->flags),
+			(int)test_bit(BTADV_CONFIGURE_DMA, &hwep_info->flags));
+	}
+
 	return ret;
 }
 

+ 4 - 0
btfmcodec/include/btfm_codec.h

@@ -33,6 +33,7 @@ static uint8_t log_lvl = BTM_BTFMCODEC_DEFAULT_LOG_LVL;
 				    }
 
 #define DEVICE_NAME_MAX_LEN	64
+#define BTM_CP_UPDATE           0xbfaf
 
 typedef enum btfmcodec_states {
 	/*Default state of kernel proxy driver */
@@ -54,6 +55,7 @@ enum btfm_pkt_type {
 	BTM_PKT_TYPE_BEARER_SWITCH_IND,
 	BTM_PKT_TYPE_HWEP_SHUTDOWN,
 	BTM_PKT_TYPE_HWEP_CONFIG,
+	BTM_PKT_TYPE_DMA_CONFIG_RSP,
 	BTM_PKT_TYPE_MAX,
 };
 
@@ -98,7 +100,9 @@ struct btfmcodec_data {
 	struct hwep_data *hwep_info;
 	struct list_head config_head;
 	struct adsp_notifier notifier;
+	struct mutex hwep_drv_lock;
 };
 
 struct btfmcodec_data *btfm_get_btfmcodec(void);
+bool isCpSupported(void);
 #endif /*__LINUX_BTFM_CODEC_H */

+ 14 - 2
btfmcodec/include/btfm_codec_hw_interface.h

@@ -18,6 +18,7 @@
  * responsible to configure master.
  */
 #define BTADV_AUDIO_MASTER_CONFIG	0
+#define BTADV_CONFIGURE_DMA             1
 #define DEVICE_NAME_MAX_LEN	64
 
 struct hwep_configurations {
@@ -41,6 +42,18 @@ struct master_hwep_configurations {
 	uint8_t codectype;
 	uint16_t direction;
 };
+
+struct hwep_dma_configurations {
+	uint8_t stream_id;
+	uint32_t sample_rate;
+	uint8_t bit_width;
+	uint8_t num_channels;
+	uint8_t codectype;
+	uint8_t lpaif; // Low power audio interface
+	uint8_t inf_index; // interface index
+	uint8_t active_channel_mask;
+};
+
 struct hwep_comp_drv {
 	int  (*hwep_probe)  (struct snd_soc_component *);
 	void (*hwep_remove) (struct snd_soc_component *);
@@ -58,8 +71,7 @@ struct hwep_dai_ops {
 				unsigned int, unsigned int *);
 	int (*hwep_get_channel_map)(void *, unsigned int *, unsigned int *,
 				unsigned int *, unsigned int *, int);
-	int (*hwep_get_configs)(void *, struct master_hwep_configurations *,
-				uint8_t);
+	int (*hwep_get_configs)(void *a, void *b, uint8_t c);
 	uint8_t *hwep_codectype;
 };
 

+ 19 - 0
btfmcodec/include/btfm_codec_pkt.h

@@ -38,6 +38,9 @@ struct btm_ctrl_pkt {
 #define BTM_BTFMCODEC_MASTER_CONFIG_RSP                         0x50000003
 #define BTM_BTFMCODEC_MASTER_SHUTDOWN_REQ                       0x50000004
 #define BTM_BTFMCODEC_CTRL_MASTER_SHUTDOWN_RSP                  0x50000005
+#define BTM_BTFMCODEC_CODEC_CONFIG_DMA_REQ                      0x58000006
+#define BTM_BTFMCODEC_CODEC_CONFIG_DMA_RSP                      0x58000007
+
 #define BTM_BTFMCODEC_BEARER_SWITCH_IND                         0x58000001
 #define BTM_BTFMCODEC_TRANSPORT_SWITCH_FAILED_IND               0x58000002
 #define BTM_BTFMCODEC_ADSP_STATE_IND                            0x58000003
@@ -45,14 +48,17 @@ struct btm_ctrl_pkt {
 
 #define BTM_MASTER_CONFIG_REQ_LEN			13
 #define BTM_MASTER_CONFIG_RSP_TIMEOUT			5000
+#define BTM_MASTER_DMA_CONFIG_RSP_TIMEOUT		5000
 #define BTM_HEADER_LEN					8
 #define BTM_PREPARE_AUDIO_BEARER_SWITCH_RSP_LEN		2
 #define BTM_MASTER_CONFIG_RSP_LEN			2
+#define BTM_CODEC_CONFIG_DMA_RSP_LEN			2
 #define BTM_MASTER_SHUTDOWN_REQ_LEN			1
 #define BTM_PREPARE_AUDIO_BEARER_SWITCH_REQ_LEN		1
 #define BTM_BEARER_SWITCH_IND_LEN			1
 #define BTM_LOG_LVL_IND_LEN                             1
 #define BTM_ADSP_STATE_IND_LEN				4
+#define BTM_CODEC_CONFIG_DMA_REQ_LEN			11
 
 #define BTM_BTFMCODEC_USECASE_START_IND			0x58000008
 #define BTM_USECASE_START_IND_LEN                       1
@@ -81,6 +87,7 @@ enum btfm_kp_status {
 	MSG_FAILED_TO_SHUTDOWN_HWEP,
 	MSG_ERR_WHILE_SHUTING_DOWN_HWEP,
 };
+
 struct btm_master_config_req {
 	btm_opcode opcode;
 	uint32_t len;
@@ -93,6 +100,18 @@ struct btm_master_config_req {
 	uint8_t codec_id;
 }__attribute__((packed));
 
+struct btm_dma_config_req {
+	btm_opcode opcode;
+	uint32_t len;
+	uint8_t stream_id;
+	uint32_t sample_rate;
+	uint8_t bit_width;
+	uint8_t num_channels;
+	uint8_t codec_id;
+	uint8_t lpaif;     // Low power audio interface
+	uint8_t inf_index; // interface index
+	uint8_t active_channel_mask;
+} __packed;
 
 struct btm_usecase_start_ind {
 	btm_opcode opcode;

+ 5 - 4
slimbus/btfm_slim_hw_interface.c

@@ -397,16 +397,17 @@ static int btfm_slim_dai_get_channel_map(void *dai,
 	return 0;
 }
 
-int btfm_slim_dai_get_configs (void * dai,
-				struct master_hwep_configurations *hwep_config,
-				uint8_t id)
+int btfm_slim_dai_get_configs(void *dai, void *config, uint8_t id)
 {
 	struct hwep_data *hwep_info = (struct hwep_data *)dai;
 	struct btfmslim *btfmslim = dev_get_drvdata(hwep_info->dev);
+	struct master_hwep_configurations *hwep_config;
 	struct btfmslim_ch *ch = NULL;
 	int i = 0;
 
 	BTFMSLIM_DBG("");
+
+	hwep_config = (struct master_hwep_configurations *) config;
 	hwep_config->stream_id = id;
 	hwep_config->device_id = btfmslim->device_id;
 	hwep_config->sample_rate = btfmslim->sample_rate;
@@ -436,6 +437,7 @@ int btfm_slim_dai_get_configs (void * dai,
 
 	return 1;
 }
+
 static struct hwep_dai_ops  btfmslim_hw_dai_ops = {
 	.hwep_startup = btfm_slim_dai_startup,
 	.hwep_shutdown = btfm_slim_dai_shutdown,
@@ -519,7 +521,6 @@ int btfm_slim_register_hw_ep(struct btfmslim *btfm_slim)
 	hwep_info->num_dai = 2;
 	hwep_info->num_mixer_ctrl = ARRAY_SIZE(status_controls);
 	hwep_info->mixer_ctrl = status_controls;
-	set_bit(BTADV_AUDIO_MASTER_CONFIG, &hwep_info->flags);
 	/* Register to hardware endpoint */
 	ret = btfmcodec_register_hw_ep(hwep_info);
 	if (ret) {

+ 1 - 1
soundwire/Makefile

@@ -1,3 +1,3 @@
 ccflags-y += -I$(BT_ROOT)/include
-bt_fm_swr-objs := btfm_swr.o btfm_swr_codec.o btfm_swr_slave.o
+bt_fm_swr-objs := btfm_swr.o btfm_swr_hw_interface.o btfm_swr_slave.o
 obj-$(CONFIG_BTFM_SWR) += bt_fm_swr.o

+ 3 - 3
soundwire/btfm_swr.c

@@ -21,6 +21,7 @@
 #include <sound/tlv.h>
 #include "btpower.h"
 #include "btfm_swr.h"
+#include "btfm_swr_hw_interface.h"
 #include "btfm_swr_slave.h"
 
 struct class *btfm_swr_class;
@@ -205,7 +206,7 @@ static int btfm_swr_probe(struct swr_device *pdev)
 	pbtfmswr->initialized = false;
 
 	// register with ALSA
-	ret = btfm_swr_register_codec(pbtfmswr);
+	ret = btfm_swr_register_hw_ep(pbtfmswr);
 	if (ret) {
 		BTFMSWR_ERR("registration with ALSA failed, returning");
 		goto dealloc;
@@ -239,8 +240,7 @@ class_err:
 	unregister_chrdev(btfm_swr_major, "btfm_swr");
 
 register_err:
-	btfm_swr_unregister_codec(pbtfmswr->dev);
-
+	btfm_swr_unregister_hwep();
 dealloc:
 	kfree(pbtfmswr);
 	return ret;

+ 0 - 17
soundwire/btfm_swr.h

@@ -83,21 +83,4 @@ int btfm_swr_enable_port(u8 port_num, u8 ch_count, u32 sample_rate,
 
 
 int btfm_swr_disable_port(u8 port_num, u8 ch_count, u8 usecase);
-
-/**
- * btfm_swr_register_codec: Register codec driver with ALSA
- * @btfmswr: swr slave device data pointer.
- * Returns:
- * -ENOMEM
- * 0
- */
-int btfm_swr_register_codec(struct btfmswr *btfmswr);
-
-/**
- * btfm_swr_unregister_codec: Unregister codec driver with ALSA
- * @dev: device node
- * Returns:
- * VOID
- */
-void btfm_swr_unregister_codec(struct device *dev);
 #endif /* BTFM_SWR_H */

+ 0 - 341
soundwire/btfm_swr_codec.c

@@ -1,341 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
- */
-
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/soc.h>
-#include "btfm_swr.h"
-
-static int bt_soc_enable_status;
-int btfm_feedback_ch_setting;
-
-static int btfm_swr_codec_write(struct snd_soc_component *codec,
-			unsigned int reg, unsigned int value)
-{
-	BTFMSWR_DBG("");
-	return 0;
-}
-
-static unsigned int btfm_swr_codec_read(struct snd_soc_component *codec,
-				unsigned int reg)
-{
-	BTFMSWR_DBG("");
-	return 0;
-}
-
-static int btfm_soc_status_get(struct snd_kcontrol *kcontrol,
-					struct snd_ctl_elem_value *ucontrol)
-{
-	BTFMSWR_DBG("");
-	ucontrol->value.integer.value[0] = bt_soc_enable_status;
-	return 1;
-}
-
-static int btfm_soc_status_put(struct snd_kcontrol *kcontrol,
-					struct snd_ctl_elem_value *ucontrol)
-{
-	BTFMSWR_DBG("");
-	return 1;
-}
-
-static int btfm_get_feedback_ch_setting(struct snd_kcontrol *kcontrol,
-					struct snd_ctl_elem_value *ucontrol)
-{
-	BTFMSWR_DBG("");
-	ucontrol->value.integer.value[0] = btfm_feedback_ch_setting;
-	return 1;
-}
-
-static int btfm_put_feedback_ch_setting(struct snd_kcontrol *kcontrol,
-					struct snd_ctl_elem_value *ucontrol)
-{
-	BTFMSWR_DBG("");
-	btfm_feedback_ch_setting = ucontrol->value.integer.value[0];
-	return 1;
-}
-
-static const struct snd_kcontrol_new status_controls[] = {
-	SOC_SINGLE_EXT("BT SOC status", 0, 0, 1, 0,
-			btfm_soc_status_get,
-			btfm_soc_status_put),
-	SOC_SINGLE_EXT("BT set feedback channel", 0, 0, 1, 0,
-	btfm_get_feedback_ch_setting,
-	btfm_put_feedback_ch_setting)
-};
-
-
-static int btfm_swr_codec_probe(struct snd_soc_component *codec)
-{
-	BTFMSWR_DBG("");
-	snd_soc_add_component_controls(codec, status_controls,
-				   ARRAY_SIZE(status_controls));
-	return 0;
-}
-
-static void btfm_swr_codec_remove(struct snd_soc_component *codec)
-{
-	BTFMSWR_DBG("");
-}
-
-static int btfm_swr_dai_startup(struct snd_pcm_substream *substream,
-		struct snd_soc_dai *dai)
-{
-	int ret = -1;
-
-	BTFMSWR_INFO("substream = %s  stream = %d dai->name = %s",
-		 substream->name, substream->stream, dai->name);
-	ret = btfm_swr_hw_init();
-	return ret;
-}
-
-static void btfm_swr_dai_shutdown(struct snd_pcm_substream *substream,
-		struct snd_soc_dai *dai)
-{
-	int ret = 0;
-	u8 port_type;
-
-	BTFMSWR_INFO("dai->name: %s, dai->id: %d, dai->rate: %d", dai->name,
-		dai->id, dai->rate);
-
-	switch (dai->id) {
-	case FMAUDIO_TX:
-		port_type = FM_AUDIO_TX1;
-		break;
-	case BTAUDIO_TX:
-		port_type = BT_AUDIO_TX1;
-		break;
-	case BTAUDIO_RX:
-		port_type = BT_AUDIO_RX1;
-		break;
-	case BTAUDIO_A2DP_SINK_TX:
-		port_type = BT_AUDIO_TX2;
-		break;
-	case BTFM_NUM_CODEC_DAIS:
-	default:
-		BTFMSWR_ERR("dai->id is invalid:%d", dai->id);
-		return;
-	}
-
-	ret = btfm_swr_disable_port(pbtfmswr->p_dai_port->port_info[dai->id].port,
-					pbtfmswr->num_channels, port_type);
-
-}
-
-static int btfm_swr_dai_hw_params(struct snd_pcm_substream *substream,
-			    struct snd_pcm_hw_params *params,
-			    struct snd_soc_dai *dai)
-{
-	pbtfmswr->bps = params_width(params);
-	pbtfmswr->direction = substream->stream;
-	pbtfmswr->num_channels = params_channels(params);
-
-	BTFMSWR_INFO("dai->name = %s dai id %x rate %d bps %d num_ch %d",
-		dai->name, dai->id, params_rate(params), params_width(params),
-		params_channels(params));
-	return 0;
-}
-
-static int btfm_swr_dai_prepare(struct snd_pcm_substream *substream,
-	struct snd_soc_dai *dai)
-{
-	int ret = -EINVAL;
-	u8 port_type;
-
-	bt_soc_enable_status = 0;
-	BTFMSWR_INFO("dai->name: %s, dai->id: %d, dai->rate: %d direction: %d", dai->name,
-		dai->id, dai->rate, pbtfmswr->direction);
-
-	/* save sample rate */
-	pbtfmswr->sample_rate = dai->rate;
-
-	switch (dai->id) {
-	case FMAUDIO_TX:
-		port_type = FM_AUDIO_TX1;
-		break;
-	case BTAUDIO_TX:
-		port_type = BT_AUDIO_TX1;
-		break;
-	case BTAUDIO_RX:
-		port_type = BT_AUDIO_RX1;
-		break;
-	case BTAUDIO_A2DP_SINK_TX:
-		port_type = BT_AUDIO_TX2;
-		break;
-	case BTFM_NUM_CODEC_DAIS:
-	default:
-		BTFMSWR_ERR("dai->id is invalid:%d", dai->id);
-		return -EINVAL;
-	}
-
-	ret = btfm_swr_enable_port(pbtfmswr->p_dai_port->port_info[dai->id].port,
-					pbtfmswr->num_channels, dai->rate, port_type);
-
-	/* save the enable channel status */
-	if (ret == 0)
-		bt_soc_enable_status = 1;
-
-	if (ret == -EISCONN) {
-		BTFMSWR_ERR("channel opened without closing, returning success");
-		ret = 0;
-	}
-
-	return ret;
-}
-
-/* This function will be called once during boot up */
-static int btfm_swr_dai_set_channel_map(struct snd_soc_dai *dai,
-				unsigned int tx_num, unsigned int *tx_slot,
-				unsigned int rx_num, unsigned int *rx_slot)
-{
-	BTFMSWR_DBG("");
-	return 0;
-}
-
-static int btfm_swr_dai_get_channel_map(struct snd_soc_dai *dai,
-				 unsigned int *tx_num, unsigned int *tx_slot,
-				 unsigned int *rx_num, unsigned int *rx_slot)
-{
-	*rx_slot = 0;
-	*tx_slot = 0;
-	*rx_num = 0;
-	*tx_num = 0;
-
-	switch (dai->id) {
-	case FMAUDIO_TX:
-	case BTAUDIO_TX:
-	case BTAUDIO_A2DP_SINK_TX:
-		*tx_num = pbtfmswr->num_channels;
-		*tx_slot = pbtfmswr->num_channels == 2 ? TWO_CHANNEL_MASK :
-							ONE_CHANNEL_MASK;
-		break;
-	case BTAUDIO_RX:
-		*rx_num = pbtfmswr->num_channels;
-		*rx_slot = pbtfmswr->num_channels == 2 ? TWO_CHANNEL_MASK :
-							ONE_CHANNEL_MASK;
-		break;
-
-	default:
-		BTFMSWR_ERR("Unsupported DAI %d", dai->id);
-		return -EINVAL;
-	}
-
-	return 0;
-}
-static struct snd_soc_dai_ops btfmswr_dai_ops = {
-	.startup = btfm_swr_dai_startup,
-	.shutdown = btfm_swr_dai_shutdown,
-	.hw_params = btfm_swr_dai_hw_params,
-	.prepare = btfm_swr_dai_prepare,
-	.set_channel_map = btfm_swr_dai_set_channel_map,
-	.get_channel_map = btfm_swr_dai_get_channel_map,
-};
-
-static struct snd_soc_dai_driver btfmswr_dai[] = {
-	{	/* FM Audio data multiple channel  : FM -> lpass */
-		.name = "btfm_fm_swr_tx",
-		.id = FMAUDIO_TX,
-		.capture = {
-			.stream_name = "FM TX Capture",
-			.rates = SNDRV_PCM_RATE_48000, /* 48 KHz */
-			.formats = SNDRV_PCM_FMTBIT_S16_LE, /* 16 bits */
-			.rate_max = 48000,
-			.rate_min = 48000,
-			.channels_min = 1,
-			.channels_max = 2,
-		},
-		.ops = &btfmswr_dai_ops,
-	},
-	{	/* Bluetooth SCO voice uplink: bt -> lpass */
-		.name = "btfm_bt_sco_swr_tx",
-		.id = BTAUDIO_TX,
-		.capture = {
-			.stream_name = "SCO TX Capture",
-			/* 8/16/44.1/48/88.2/96/192 Khz */
-			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000
-				| SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000
-				| SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000
-				| SNDRV_PCM_RATE_192000,
-			.formats = SNDRV_PCM_FMTBIT_S16_LE, /* 16 bits */
-			.rate_max = 192000,
-			.rate_min = 8000,
-			.channels_min = 1,
-			.channels_max = 1,
-		},
-		.ops = &btfmswr_dai_ops,
-	},
-	{	/* Bluetooth SCO voice downlink: lpass -> bt or A2DP Playback */
-		.name = "btfm_bt_sco_a2dp_swr_rx",
-		.id = BTAUDIO_RX,
-		.playback = {
-			.stream_name = "SCO A2DP RX Playback",
-			/* 8/16/44.1/48/88.2/96/192 Khz */
-			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000
-				| SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000
-				| SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000
-				| SNDRV_PCM_RATE_192000,
-			.formats = SNDRV_PCM_FMTBIT_S16_LE, /* 16 bits */
-			.rate_max = 192000,
-			.rate_min = 8000,
-			.channels_min = 1,
-			.channels_max = 1,
-		},
-		.ops = &btfmswr_dai_ops,
-	},
-	{	/* Bluetooth A2DP sink: bt -> lpass */
-		.name = "btfm_a2dp_sink_swr_tx",
-		.id = BTAUDIO_A2DP_SINK_TX,
-		.capture = {
-			.stream_name = "A2DP sink TX Capture",
-			/* 8/16/44.1/48/88.2/96/192 Khz */
-			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000
-				| SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000
-				| SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000
-				| SNDRV_PCM_RATE_192000,
-			.formats = SNDRV_PCM_FMTBIT_S16_LE, /* 16 bits */
-			.rate_max = 192000,
-			.rate_min = 8000,
-			.channels_min = 1,
-			.channels_max = 1,
-		},
-		.ops = &btfmswr_dai_ops,
-	},
-};
-
-static const struct snd_soc_component_driver btfmswr_codec = {
-	.probe	= btfm_swr_codec_probe,
-	.remove	= btfm_swr_codec_remove,
-	.read	= btfm_swr_codec_read,
-	.write	= btfm_swr_codec_write,
-};
-
-int btfm_swr_register_codec(struct btfmswr *btfm_swr)
-{
-	int ret = 0;
-	struct device *dev = btfm_swr->dev;
-
-	BTFMSWR_DBG("");
-	dev_err(dev, "\n");
-
-	/* Register Codec driver */
-	ret = snd_soc_register_component(dev, &btfmswr_codec,
-		btfmswr_dai, ARRAY_SIZE(btfmswr_dai));
-	if (ret)
-		BTFMSWR_ERR("failed to register codec (%d)", ret);
-	return ret;
-}
-
-void btfm_swr_unregister_codec(struct device *dev)
-{
-	BTFMSWR_DBG("");
-	/* Unregister Codec driver */
-	snd_soc_unregister_component(dev);
-}
-
-
-MODULE_DESCRIPTION("BTFM SoundWire Codec driver");
-MODULE_LICENSE("GPL v2");

+ 440 - 0
soundwire/btfm_swr_hw_interface.c

@@ -0,0 +1,440 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include "btfm_swr.h"
+#include "btfm_swr_hw_interface.h"
+#include "btfm_codec_hw_interface.h"
+
+#define LPAIF_AUD     0x05
+
+static int bt_soc_enable_status;
+int btfm_feedback_ch_setting;
+static uint8_t usecase_codec;
+
+static int btfm_swr_hwep_write(struct snd_soc_component *codec,
+			unsigned int reg, unsigned int value)
+{
+	BTFMSWR_DBG("");
+	return 0;
+}
+
+static unsigned int btfm_swr_hwep_read(struct snd_soc_component *codec,
+				unsigned int reg)
+{
+	BTFMSWR_DBG("");
+	return 0;
+}
+
+static int btfm_soc_status_get(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	BTFMSWR_DBG("");
+	ucontrol->value.integer.value[0] = bt_soc_enable_status;
+	return 1;
+}
+
+static int btfm_soc_status_put(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	BTFMSWR_DBG("");
+	return 1;
+}
+
+static int btfm_get_feedback_ch_setting(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	BTFMSWR_DBG("");
+	ucontrol->value.integer.value[0] = btfm_feedback_ch_setting;
+	return 1;
+}
+
+static int btfm_put_feedback_ch_setting(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	BTFMSWR_DBG("");
+	btfm_feedback_ch_setting = ucontrol->value.integer.value[0];
+	return 1;
+}
+
+static int btfm_get_codec_type(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	BTFMSWR_DBG("current codec type:%s", codec_text[usecase_codec]);
+	ucontrol->value.integer.value[0] = usecase_codec;
+	return 1;
+}
+
+static int btfm_put_codec_type(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	usecase_codec = ucontrol->value.integer.value[0];
+	BTFMSWR_DBG("codec type set to:%s", codec_text[usecase_codec]);
+	return 1;
+}
+
+static struct snd_kcontrol_new status_controls[] = {
+	SOC_SINGLE_EXT("BT SOC status", 0, 0, 1, 0,
+	btfm_soc_status_get, btfm_soc_status_put),
+	SOC_SINGLE_EXT("BT set feedback channel", 0, 0, 1, 0,
+	btfm_get_feedback_ch_setting,
+	btfm_put_feedback_ch_setting),
+	SOC_ENUM_EXT("BT codec type", codec_display,
+	btfm_get_codec_type, btfm_put_codec_type),
+};
+
+
+static int btfm_swr_hwep_probe(struct snd_soc_component *codec)
+{
+	BTFMSWR_DBG("");
+	return 0;
+}
+
+static void btfm_swr_hwep_remove(struct snd_soc_component *codec)
+{
+	BTFMSWR_DBG("");
+}
+
+static int btfm_swr_dai_startup(void *dai)
+{
+	//struct hwep_data *hwep_info = (struct hwep_data *)dai;
+	int ret = -1;
+
+	BTFMSWR_DBG("");
+
+	ret = btfm_swr_hw_init();
+	return ret;
+}
+
+static void btfm_swr_dai_shutdown(void *dai, int id)
+{
+	struct hwep_data *hwep_info = (struct hwep_data *)dai;
+	struct btfmswr *btfmswr = dev_get_drvdata(hwep_info->dev);
+	int ret = 0;
+	u8 port_type;
+
+	BTFMSWR_INFO("");
+
+	if (btfmswr == NULL)
+		BTFMSWR_INFO("btfmswr is NULL\n");
+
+	switch (id) {
+	case FMAUDIO_TX:
+		port_type = FM_AUDIO_TX1;
+		break;
+	case BTAUDIO_TX:
+		port_type = BT_AUDIO_TX1;
+		break;
+	case BTAUDIO_RX:
+		port_type = BT_AUDIO_RX1;
+		break;
+	case BTAUDIO_A2DP_SINK_TX:
+		port_type = BT_AUDIO_TX2;
+		break;
+	case BTFM_NUM_CODEC_DAIS:
+	default:
+		BTFMSWR_ERR("dai->id is invalid:%d", id);
+		return;
+	}
+
+	ret = btfm_swr_disable_port(btfmswr->p_dai_port->port_info[id].port,
+				    btfmswr->num_channels, port_type);
+}
+
+static int btfm_swr_dai_hw_params(void *dai, uint32_t bps,
+				   uint32_t direction, uint8_t num_channels)
+{
+	struct hwep_data *hwep_info = (struct hwep_data *)dai;
+	struct btfmswr *btfmswr = dev_get_drvdata(hwep_info->dev);
+
+	BTFMSWR_DBG("");
+	btfmswr->bps = bps;
+	btfmswr->direction = direction;
+	btfmswr->num_channels = num_channels;
+
+	return 0;
+}
+
+void btfm_get_sampling_rate(uint32_t *sampling_rate)
+{
+	uint8_t codec_types_avb = ARRAY_SIZE(codec_text);
+
+	if (usecase_codec > (codec_types_avb - 1)) {
+		BTFMSWR_ERR("falling back to use default sampling_rate: %u",
+				*sampling_rate);
+		return;
+	}
+
+	if (*sampling_rate == 44100 || *sampling_rate == 48000) {
+		if (usecase_codec == LDAC ||
+		    usecase_codec == APTX_AD)
+			*sampling_rate = (*sampling_rate) * 2;
+	}
+
+	if (usecase_codec == LC3_VOICE ||
+	    usecase_codec == APTX_AD_SPEECH ||
+	    usecase_codec == LC3 || usecase_codec == APTX_AD_QLEA ||
+	    usecase_codec == APTX_AD_R4) {
+		*sampling_rate = 96000;
+	}
+
+	BTFMSWR_INFO("current usecase codec type %s and sampling rate:%u khz",
+			codec_text[usecase_codec], *sampling_rate);
+}
+
+static int btfm_swr_dai_prepare(void *dai, uint32_t sampling_rate, uint32_t direction, int id)
+{
+	struct hwep_data *hwep_info = (struct hwep_data *)dai;
+	struct btfmswr *btfmswr = dev_get_drvdata(hwep_info->dev);
+	int ret = -EINVAL;
+	u8 port_type;
+
+	bt_soc_enable_status = 0;
+	BTFMSWR_INFO("dai->id: %d, dai->rate: %d direction: %d", id, sampling_rate, direction);
+
+	btfm_get_sampling_rate(&sampling_rate);
+	btfmswr->sample_rate = sampling_rate;
+
+	switch (id) {
+	case FMAUDIO_TX:
+		port_type = FM_AUDIO_TX1;
+		break;
+	case BTAUDIO_TX:
+		port_type = BT_AUDIO_TX1;
+		break;
+	case BTAUDIO_RX:
+		port_type = BT_AUDIO_RX1;
+		break;
+	case BTAUDIO_A2DP_SINK_TX:
+		port_type = BT_AUDIO_TX2;
+		break;
+	case BTFM_NUM_CODEC_DAIS:
+	default:
+		BTFMSWR_ERR("dai->id is invalid:%d", id);
+		return -EINVAL;
+	}
+
+	ret = btfm_swr_enable_port(btfmswr->p_dai_port->port_info[id].port,
+				   btfmswr->num_channels, sampling_rate, port_type);
+
+	/* save the enable channel status */
+	if (ret == 0)
+		bt_soc_enable_status = 1;
+
+	if (ret == -EISCONN) {
+		BTFMSWR_ERR("channel opened without closing, returning success");
+		ret = 0;
+	}
+
+	return ret;
+}
+
+/* This function will be called once during boot up */
+static int btfm_swr_dai_set_channel_map(void *dai,
+				unsigned int tx_num, unsigned int *tx_slot,
+				unsigned int rx_num, unsigned int *rx_slot)
+{
+	BTFMSWR_DBG("");
+	return 0;
+}
+
+static int btfm_swr_dai_get_channel_map(void *dai,
+				 unsigned int *tx_num, unsigned int *tx_slot,
+				 unsigned int *rx_num, unsigned int *rx_slot, int id)
+{
+
+	struct hwep_data *hwep_info = (struct hwep_data *)dai;
+	struct btfmswr *btfmswr = dev_get_drvdata(hwep_info->dev);
+
+	*rx_slot = 0;
+	*tx_slot = 0;
+	*rx_num = 0;
+	*tx_num = 0;
+
+	switch (id) {
+	case FMAUDIO_TX:
+	case BTAUDIO_TX:
+	case BTAUDIO_A2DP_SINK_TX:
+		*tx_num = btfmswr->num_channels;
+		*tx_slot = btfmswr->num_channels == 2 ? TWO_CHANNEL_MASK : ONE_CHANNEL_MASK;
+		break;
+	case BTAUDIO_RX:
+		*rx_num = btfmswr->num_channels;
+		*rx_slot = btfmswr->num_channels == 2 ? TWO_CHANNEL_MASK : ONE_CHANNEL_MASK;
+		break;
+
+	default:
+		BTFMSWR_ERR("Unsupported DAI %d", id);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+int btfm_swr_dai_get_configs(void *dai, void *config, uint8_t id)
+{
+	struct hwep_data *hwep_info = (struct hwep_data *)dai;
+	struct btfmswr *btfmswr = dev_get_drvdata(hwep_info->dev);
+	struct hwep_dma_configurations *hwep_config;
+
+	BTFMSWR_DBG("");
+	hwep_config = (struct hwep_dma_configurations *)config;
+
+	hwep_config->stream_id = id;
+	hwep_config->sample_rate = btfmswr->sample_rate;
+	hwep_config->bit_width = (uint8_t)btfmswr->bps;
+	hwep_config->codectype = usecase_codec;
+
+	hwep_config->num_channels = btfmswr->num_channels;
+	hwep_config->active_channel_mask = (btfmswr->num_channels == 2 ?
+					   TWO_CHANNEL_MASK : ONE_CHANNEL_MASK);
+	hwep_config->lpaif = LPAIF_AUD;
+	hwep_config->inf_index = 1;
+	return 1;
+}
+
+static struct hwep_dai_ops  btfmswr_hw_dai_ops = {
+	.hwep_startup = btfm_swr_dai_startup,
+	.hwep_shutdown = btfm_swr_dai_shutdown,
+	.hwep_hw_params = btfm_swr_dai_hw_params,
+	.hwep_prepare = btfm_swr_dai_prepare,
+	.hwep_set_channel_map = btfm_swr_dai_set_channel_map,
+	.hwep_get_channel_map = btfm_swr_dai_get_channel_map,
+	.hwep_get_configs = btfm_swr_dai_get_configs,
+	.hwep_codectype = &usecase_codec,
+};
+
+static struct hwep_dai_driver btfmswr_dai_driver[] = {
+	{	/* FM Audio data multiple channel  : FM -> lpass */
+		.dai_name = "btaudio_fm_tx",
+		.id = FMAUDIO_TX,
+		.capture = {
+			.stream_name = "FM SWR TX Capture",
+			.rates = SNDRV_PCM_RATE_48000, /* 48 KHz */
+			.formats = SNDRV_PCM_FMTBIT_S16_LE, /* 16 bits */
+			.rate_max = 48000,
+			.rate_min = 48000,
+			.channels_min = 1,
+			.channels_max = 2,
+		},
+		.dai_ops = &btfmswr_hw_dai_ops,
+	},
+	{	/* Bluetooth SCO voice uplink: bt -> lpass */
+		.dai_name = "btaudio_tx",
+		.id = BTAUDIO_TX,
+		.capture = {
+			.stream_name = "BT Audio SWR Tx Capture",
+			/* 8 KHz or 16 KHz */
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000
+				| SNDRV_PCM_RATE_8000_192000
+				| SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000
+				| SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000
+				| SNDRV_PCM_RATE_192000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE, /* 16 bits */
+			.rate_max = 192000,
+			.rate_min = 8000,
+			.channels_min = 1,
+			.channels_max = 1,
+		},
+		.dai_ops = &btfmswr_hw_dai_ops,
+	},
+	{	/* Bluetooth SCO voice downlink: lpass -> bt or A2DP Playback */
+		.dai_name = "btaudio_rx",
+		.id = BTAUDIO_RX,
+		.playback = {
+			.stream_name = "BT Audio SWR Rx Playback",
+			/* 8/16/44.1/48/88.2/96 Khz */
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000
+				| SNDRV_PCM_RATE_8000_192000
+				| SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000
+				| SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000
+				| SNDRV_PCM_RATE_192000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE, /* 16 bits */
+			.rate_max = 192000,
+			.rate_min = 8000,
+			.channels_min = 1,
+			.channels_max = 1,
+		},
+		.dai_ops = &btfmswr_hw_dai_ops,
+	},
+	{	/* Bluetooth A2DP sink: bt -> lpass */
+		.dai_name = "btfm_a2dp_sink_swr_tx",
+		.id = BTAUDIO_A2DP_SINK_TX,
+		.capture = {
+			.stream_name = "A2DP sink TX Capture",
+			/* 8/16/44.1/48/88.2/96/192 Khz */
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000
+				| SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000
+				| SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000
+				| SNDRV_PCM_RATE_192000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE, /* 16 bits */
+			.rate_max = 192000,
+			.rate_min = 8000,
+			.channels_min = 1,
+			.channels_max = 1,
+		},
+		.dai_ops = &btfmswr_hw_dai_ops,
+	}
+};
+
+static struct hwep_comp_drv btfmswr_hw_driver = {
+	.hwep_probe	= btfm_swr_hwep_probe,
+	.hwep_remove	= btfm_swr_hwep_remove,
+	.hwep_read	= btfm_swr_hwep_read,
+	.hwep_write	= btfm_swr_hwep_write,
+};
+
+int btfm_swr_register_hw_ep(struct btfmswr *btfm_swr)
+{
+	struct device *dev = btfm_swr->dev;
+	struct hwep_data *hwep_info;
+	int ret = 0;
+
+	BTFMSWR_INFO("Registering with BTFMCODEC HWEP interface\n");
+	hwep_info = kzalloc(sizeof(struct hwep_data), GFP_KERNEL);
+
+	if (!hwep_info) {
+		BTFMSWR_ERR("%s: failed to allocate memory\n", __func__);
+		ret = -ENOMEM;
+		goto end;
+	}
+
+	/* Copy EP device parameters as intercations will be on the same device */
+	hwep_info->dev = dev;
+	strscpy(hwep_info->driver_name, SWR_SLAVE_COMPATIBLE_STR, DEVICE_NAME_MAX_LEN);
+	hwep_info->drv = &btfmswr_hw_driver;
+	hwep_info->dai_drv = btfmswr_dai_driver;
+	hwep_info->num_dai = ARRAY_SIZE(btfmswr_dai_driver);
+	hwep_info->num_dai = 4;
+	hwep_info->num_mixer_ctrl = ARRAY_SIZE(status_controls);
+	hwep_info->mixer_ctrl = status_controls;
+	/* Register to hardware endpoint */
+	ret = btfmcodec_register_hw_ep(hwep_info);
+	if (ret) {
+		BTFMSWR_ERR("failed to register with btfmcodec driver hw interface (%d)", ret);
+		goto end;
+	}
+
+	BTFMSWR_INFO("Registered succesfull with BTFMCODEC HWEP interface\n");
+	return ret;
+end:
+	return ret;
+}
+
+void btfm_swr_unregister_hwep(void)
+{
+	BTFMSWR_INFO("Unregistered with BTFMCODEC HWEP	interface");
+	/* Unregister with BTFMCODEC HWEP	driver */
+	btfmcodec_unregister_hw_ep(SWR_SLAVE_COMPATIBLE_STR);
+
+}
+
+MODULE_DESCRIPTION("BTFM SoundWire Codec driver");
+MODULE_LICENSE("GPL v2");

+ 35 - 0
soundwire/btfm_swr_hw_interface.h

@@ -0,0 +1,35 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef __LINUX_BTFM_SWR_HW_INTERFACE_H
+#define __LINUX_BTFM_SWR_HW_INTERFACE_H
+
+int btfm_swr_register_hw_ep(struct btfmswr *a);
+void btfm_swr_unregister_hwep(void);
+
+enum Codec {
+	SBC = 0,
+	AAC,
+	LDAC,
+	APTX,
+	APTX_HD,
+	APTX_AD,
+	LC3,
+	APTX_AD_SPEECH,
+	LC3_VOICE,
+	APTX_AD_QLEA,
+	APTX_AD_R4,
+	NO_CODEC
+};
+
+static const char * const codec_text[] = {"CODEC_TYPE_SBC", "CODEC_TYPE_AAC",
+				   "CODEC_TYPE_LDAC", "CODEC_TYPE_APTX",
+				   "CODEC_TYPE_APTX_HD", "CODEC_TYPE_APTX_AD",
+				   "CODEC_TYPE_LC3", "CODEC_TYPE_APTX_AD_SPEECH",
+				   "CODEC_TYPE_LC3_VOICE", "CODEC_TYPE_APTX_AD_QLEA",
+				   "CODEC_TYPE_APTX_AD_R4", "CODEC_TYPE_INVALID"};
+
+static SOC_ENUM_SINGLE_EXT_DECL(codec_display, codec_text);
+#endif /*__LINUX_BTFM_SWR_HW_INTERFACE_H*/