xref: /linux/sound/soc/mediatek/common/mtk-btcvsd.c (revision 3a39d672e7f48b8d6b91a09afa4b55352773b4b5)
14bd8597dSKaiChieh Chuang // SPDX-License-Identifier: GPL-2.0
24bd8597dSKaiChieh Chuang //
34bd8597dSKaiChieh Chuang // Mediatek ALSA BT SCO CVSD/MSBC Driver
44bd8597dSKaiChieh Chuang //
54bd8597dSKaiChieh Chuang // Copyright (c) 2019 MediaTek Inc.
64bd8597dSKaiChieh Chuang // Author: KaiChieh Chuang <kaichieh.chuang@mediatek.com>
74bd8597dSKaiChieh Chuang 
84bd8597dSKaiChieh Chuang #include <linux/mfd/syscon.h>
94bd8597dSKaiChieh Chuang #include <linux/module.h>
104bd8597dSKaiChieh Chuang #include <linux/of_address.h>
114bd8597dSKaiChieh Chuang #include <linux/sched/clock.h>
124bd8597dSKaiChieh Chuang 
134bd8597dSKaiChieh Chuang #include <sound/soc.h>
144bd8597dSKaiChieh Chuang 
154bd8597dSKaiChieh Chuang #define BTCVSD_SND_NAME "mtk-btcvsd-snd"
164bd8597dSKaiChieh Chuang 
174bd8597dSKaiChieh Chuang #define BT_CVSD_TX_NREADY	BIT(21)
184bd8597dSKaiChieh Chuang #define BT_CVSD_RX_READY	BIT(22)
194bd8597dSKaiChieh Chuang #define BT_CVSD_TX_UNDERFLOW	BIT(23)
204bd8597dSKaiChieh Chuang #define BT_CVSD_RX_OVERFLOW	BIT(24)
214bd8597dSKaiChieh Chuang #define BT_CVSD_INTERRUPT	BIT(31)
224bd8597dSKaiChieh Chuang 
234bd8597dSKaiChieh Chuang #define BT_CVSD_CLEAR \
244bd8597dSKaiChieh Chuang 	(BT_CVSD_TX_NREADY | BT_CVSD_RX_READY | BT_CVSD_TX_UNDERFLOW |\
254bd8597dSKaiChieh Chuang 	 BT_CVSD_RX_OVERFLOW | BT_CVSD_INTERRUPT)
264bd8597dSKaiChieh Chuang 
274bd8597dSKaiChieh Chuang /* TX */
284bd8597dSKaiChieh Chuang #define SCO_TX_ENCODE_SIZE (60)
294bd8597dSKaiChieh Chuang /* 18 = 6 * 180 / SCO_TX_ENCODE_SIZE */
304bd8597dSKaiChieh Chuang #define SCO_TX_PACKER_BUF_NUM (18)
314bd8597dSKaiChieh Chuang 
324bd8597dSKaiChieh Chuang /* RX */
334bd8597dSKaiChieh Chuang #define SCO_RX_PLC_SIZE (30)
344bd8597dSKaiChieh Chuang #define SCO_RX_PACKER_BUF_NUM (64)
354bd8597dSKaiChieh Chuang #define SCO_RX_PACKET_MASK (0x3F)
364bd8597dSKaiChieh Chuang 
374bd8597dSKaiChieh Chuang #define SCO_CVSD_PACKET_VALID_SIZE 2
384bd8597dSKaiChieh Chuang 
394bd8597dSKaiChieh Chuang #define SCO_PACKET_120 120
404bd8597dSKaiChieh Chuang #define SCO_PACKET_180 180
414bd8597dSKaiChieh Chuang 
424bd8597dSKaiChieh Chuang #define BTCVSD_RX_PACKET_SIZE (SCO_RX_PLC_SIZE + SCO_CVSD_PACKET_VALID_SIZE)
434bd8597dSKaiChieh Chuang #define BTCVSD_TX_PACKET_SIZE (SCO_TX_ENCODE_SIZE)
444bd8597dSKaiChieh Chuang 
454bd8597dSKaiChieh Chuang #define BTCVSD_RX_BUF_SIZE (BTCVSD_RX_PACKET_SIZE * SCO_RX_PACKER_BUF_NUM)
464bd8597dSKaiChieh Chuang #define BTCVSD_TX_BUF_SIZE (BTCVSD_TX_PACKET_SIZE * SCO_TX_PACKER_BUF_NUM)
474bd8597dSKaiChieh Chuang 
484bd8597dSKaiChieh Chuang enum bt_sco_state {
494bd8597dSKaiChieh Chuang 	BT_SCO_STATE_IDLE,
504bd8597dSKaiChieh Chuang 	BT_SCO_STATE_RUNNING,
514bd8597dSKaiChieh Chuang 	BT_SCO_STATE_ENDING,
52f060f46fSKaiChieh Chuang 	BT_SCO_STATE_LOOPBACK,
534bd8597dSKaiChieh Chuang };
544bd8597dSKaiChieh Chuang 
554bd8597dSKaiChieh Chuang enum bt_sco_direct {
564bd8597dSKaiChieh Chuang 	BT_SCO_DIRECT_BT2ARM,
574bd8597dSKaiChieh Chuang 	BT_SCO_DIRECT_ARM2BT,
584bd8597dSKaiChieh Chuang };
594bd8597dSKaiChieh Chuang 
604bd8597dSKaiChieh Chuang enum bt_sco_packet_len {
614bd8597dSKaiChieh Chuang 	BT_SCO_CVSD_30 = 0,
624bd8597dSKaiChieh Chuang 	BT_SCO_CVSD_60,
634bd8597dSKaiChieh Chuang 	BT_SCO_CVSD_90,
644bd8597dSKaiChieh Chuang 	BT_SCO_CVSD_120,
654bd8597dSKaiChieh Chuang 	BT_SCO_CVSD_10,
664bd8597dSKaiChieh Chuang 	BT_SCO_CVSD_20,
674bd8597dSKaiChieh Chuang 	BT_SCO_CVSD_MAX,
684bd8597dSKaiChieh Chuang };
694bd8597dSKaiChieh Chuang 
704bd8597dSKaiChieh Chuang enum BT_SCO_BAND {
714bd8597dSKaiChieh Chuang 	BT_SCO_NB,
724bd8597dSKaiChieh Chuang 	BT_SCO_WB,
734bd8597dSKaiChieh Chuang };
744bd8597dSKaiChieh Chuang 
754bd8597dSKaiChieh Chuang struct mtk_btcvsd_snd_hw_info {
764bd8597dSKaiChieh Chuang 	unsigned int num_valid_addr;
774bd8597dSKaiChieh Chuang 	unsigned long bt_sram_addr[20];
784bd8597dSKaiChieh Chuang 	unsigned int packet_length;
794bd8597dSKaiChieh Chuang 	unsigned int packet_num;
804bd8597dSKaiChieh Chuang };
814bd8597dSKaiChieh Chuang 
824bd8597dSKaiChieh Chuang struct mtk_btcvsd_snd_stream {
834bd8597dSKaiChieh Chuang 	struct snd_pcm_substream *substream;
844bd8597dSKaiChieh Chuang 	int stream;
854bd8597dSKaiChieh Chuang 
864bd8597dSKaiChieh Chuang 	enum bt_sco_state state;
874bd8597dSKaiChieh Chuang 
884bd8597dSKaiChieh Chuang 	unsigned int packet_size;
894bd8597dSKaiChieh Chuang 	unsigned int buf_size;
904bd8597dSKaiChieh Chuang 	u8 temp_packet_buf[SCO_PACKET_180];
914bd8597dSKaiChieh Chuang 
924bd8597dSKaiChieh Chuang 	int packet_w;
934bd8597dSKaiChieh Chuang 	int packet_r;
944bd8597dSKaiChieh Chuang 	snd_pcm_uframes_t prev_frame;
954bd8597dSKaiChieh Chuang 	int prev_packet_idx;
964bd8597dSKaiChieh Chuang 
974bd8597dSKaiChieh Chuang 	unsigned int xrun:1;
984bd8597dSKaiChieh Chuang 	unsigned int timeout:1;
994bd8597dSKaiChieh Chuang 	unsigned int mute:1;
1004bd8597dSKaiChieh Chuang 	unsigned int trigger_start:1;
1014bd8597dSKaiChieh Chuang 	unsigned int wait_flag:1;
1024bd8597dSKaiChieh Chuang 	unsigned int rw_cnt;
1034bd8597dSKaiChieh Chuang 
1044bd8597dSKaiChieh Chuang 	unsigned long long time_stamp;
1054bd8597dSKaiChieh Chuang 	unsigned long long buf_data_equivalent_time;
1064bd8597dSKaiChieh Chuang 
1074bd8597dSKaiChieh Chuang 	struct mtk_btcvsd_snd_hw_info buffer_info;
1084bd8597dSKaiChieh Chuang };
1094bd8597dSKaiChieh Chuang 
1104bd8597dSKaiChieh Chuang struct mtk_btcvsd_snd {
1114bd8597dSKaiChieh Chuang 	struct device *dev;
1124bd8597dSKaiChieh Chuang 	int irq_id;
1134bd8597dSKaiChieh Chuang 
1144bd8597dSKaiChieh Chuang 	struct regmap *infra;
1154bd8597dSKaiChieh Chuang 	void __iomem *bt_pkv_base;
1164bd8597dSKaiChieh Chuang 	void __iomem *bt_sram_bank2_base;
1174bd8597dSKaiChieh Chuang 
1184bd8597dSKaiChieh Chuang 	unsigned int infra_misc_offset;
1194bd8597dSKaiChieh Chuang 	unsigned int conn_bt_cvsd_mask;
1204bd8597dSKaiChieh Chuang 	unsigned int cvsd_mcu_read_offset;
1214bd8597dSKaiChieh Chuang 	unsigned int cvsd_mcu_write_offset;
1224bd8597dSKaiChieh Chuang 	unsigned int cvsd_packet_indicator;
1234bd8597dSKaiChieh Chuang 
1244bd8597dSKaiChieh Chuang 	u32 *bt_reg_pkt_r;
1254bd8597dSKaiChieh Chuang 	u32 *bt_reg_pkt_w;
1264bd8597dSKaiChieh Chuang 	u32 *bt_reg_ctl;
1274bd8597dSKaiChieh Chuang 
1284bd8597dSKaiChieh Chuang 	unsigned int irq_disabled:1;
1294bd8597dSKaiChieh Chuang 
1304bd8597dSKaiChieh Chuang 	spinlock_t tx_lock;	/* spinlock for bt tx stream control */
1314bd8597dSKaiChieh Chuang 	spinlock_t rx_lock;	/* spinlock for bt rx stream control */
1324bd8597dSKaiChieh Chuang 	wait_queue_head_t tx_wait;
1334bd8597dSKaiChieh Chuang 	wait_queue_head_t rx_wait;
1344bd8597dSKaiChieh Chuang 
1354bd8597dSKaiChieh Chuang 	struct mtk_btcvsd_snd_stream *tx;
1364bd8597dSKaiChieh Chuang 	struct mtk_btcvsd_snd_stream *rx;
1374bd8597dSKaiChieh Chuang 	u8 tx_packet_buf[BTCVSD_TX_BUF_SIZE];
1384bd8597dSKaiChieh Chuang 	u8 rx_packet_buf[BTCVSD_RX_BUF_SIZE];
1394bd8597dSKaiChieh Chuang 
1404bd8597dSKaiChieh Chuang 	enum BT_SCO_BAND band;
1414bd8597dSKaiChieh Chuang };
1424bd8597dSKaiChieh Chuang 
1434bd8597dSKaiChieh Chuang struct mtk_btcvsd_snd_time_buffer_info {
1444bd8597dSKaiChieh Chuang 	unsigned long long data_count_equi_time;
1454bd8597dSKaiChieh Chuang 	unsigned long long time_stamp_us;
1464bd8597dSKaiChieh Chuang };
1474bd8597dSKaiChieh Chuang 
1484bd8597dSKaiChieh Chuang static const unsigned int btsco_packet_valid_mask[BT_SCO_CVSD_MAX][6] = {
1494bd8597dSKaiChieh Chuang 	{0x1, 0x1 << 1, 0x1 << 2, 0x1 << 3, 0x1 << 4, 0x1 << 5},
1504bd8597dSKaiChieh Chuang 	{0x1, 0x1, 0x2, 0x2, 0x4, 0x4},
1514bd8597dSKaiChieh Chuang 	{0x1, 0x1, 0x1, 0x2, 0x2, 0x2},
1524bd8597dSKaiChieh Chuang 	{0x1, 0x1, 0x1, 0x1, 0x0, 0x0},
1534bd8597dSKaiChieh Chuang 	{0x7, 0x7 << 3, 0x7 << 6, 0x7 << 9, 0x7 << 12, 0x7 << 15},
1544bd8597dSKaiChieh Chuang 	{0x3, 0x3 << 1, 0x3 << 3, 0x3 << 4, 0x3 << 6, 0x3 << 7},
1554bd8597dSKaiChieh Chuang };
1564bd8597dSKaiChieh Chuang 
1574bd8597dSKaiChieh Chuang static const unsigned int btsco_packet_info[BT_SCO_CVSD_MAX][4] = {
1584bd8597dSKaiChieh Chuang 	{30, 6, SCO_PACKET_180 / SCO_TX_ENCODE_SIZE,
1594bd8597dSKaiChieh Chuang 	 SCO_PACKET_180 / SCO_RX_PLC_SIZE},
1604bd8597dSKaiChieh Chuang 	{60, 3, SCO_PACKET_180 / SCO_TX_ENCODE_SIZE,
1614bd8597dSKaiChieh Chuang 	 SCO_PACKET_180 / SCO_RX_PLC_SIZE},
1624bd8597dSKaiChieh Chuang 	{90, 2, SCO_PACKET_180 / SCO_TX_ENCODE_SIZE,
1634bd8597dSKaiChieh Chuang 	 SCO_PACKET_180 / SCO_RX_PLC_SIZE},
1644bd8597dSKaiChieh Chuang 	{120, 1, SCO_PACKET_120 / SCO_TX_ENCODE_SIZE,
1654bd8597dSKaiChieh Chuang 	 SCO_PACKET_120 / SCO_RX_PLC_SIZE},
1664bd8597dSKaiChieh Chuang 	{10, 18, SCO_PACKET_180 / SCO_TX_ENCODE_SIZE,
1674bd8597dSKaiChieh Chuang 	 SCO_PACKET_180 / SCO_RX_PLC_SIZE},
1684bd8597dSKaiChieh Chuang 	{20, 9, SCO_PACKET_180 / SCO_TX_ENCODE_SIZE,
1694bd8597dSKaiChieh Chuang 	 SCO_PACKET_180 / SCO_RX_PLC_SIZE},
1704bd8597dSKaiChieh Chuang };
1714bd8597dSKaiChieh Chuang 
1724bd8597dSKaiChieh Chuang static const u8 table_msbc_silence[SCO_PACKET_180] = {
1734bd8597dSKaiChieh Chuang 	0x01, 0x38, 0xad, 0x00, 0x00, 0xc5, 0x00, 0x00, 0x00, 0x00,
1744bd8597dSKaiChieh Chuang 	0x77, 0x6d, 0xb6, 0xdd, 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6d,
1754bd8597dSKaiChieh Chuang 	0xdd, 0xb6, 0xdb, 0x77, 0x6d, 0xb6, 0xdd, 0xdb, 0x6d, 0xb7,
1764bd8597dSKaiChieh Chuang 	0x76, 0xdb, 0x6d, 0xdd, 0xb6, 0xdb, 0x77, 0x6d, 0xb6, 0xdd,
1774bd8597dSKaiChieh Chuang 	0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6d, 0xdd, 0xb6, 0xdb, 0x77,
1784bd8597dSKaiChieh Chuang 	0x6d, 0xb6, 0xdd, 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6c, 0x00,
1794bd8597dSKaiChieh Chuang 	0x01, 0xc8, 0xad, 0x00, 0x00, 0xc5, 0x00, 0x00, 0x00, 0x00,
1804bd8597dSKaiChieh Chuang 	0x77, 0x6d, 0xb6, 0xdd, 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6d,
1814bd8597dSKaiChieh Chuang 	0xdd, 0xb6, 0xdb, 0x77, 0x6d, 0xb6, 0xdd, 0xdb, 0x6d, 0xb7,
1824bd8597dSKaiChieh Chuang 	0x76, 0xdb, 0x6d, 0xdd, 0xb6, 0xdb, 0x77, 0x6d, 0xb6, 0xdd,
1834bd8597dSKaiChieh Chuang 	0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6d, 0xdd, 0xb6, 0xdb, 0x77,
1844bd8597dSKaiChieh Chuang 	0x6d, 0xb6, 0xdd, 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6c, 0x00,
1854bd8597dSKaiChieh Chuang 	0x01, 0xf8, 0xad, 0x00, 0x00, 0xc5, 0x00, 0x00, 0x00, 0x00,
1864bd8597dSKaiChieh Chuang 	0x77, 0x6d, 0xb6, 0xdd, 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6d,
1874bd8597dSKaiChieh Chuang 	0xdd, 0xb6, 0xdb, 0x77, 0x6d, 0xb6, 0xdd, 0xdb, 0x6d, 0xb7,
1884bd8597dSKaiChieh Chuang 	0x76, 0xdb, 0x6d, 0xdd, 0xb6, 0xdb, 0x77, 0x6d, 0xb6, 0xdd,
1894bd8597dSKaiChieh Chuang 	0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6d, 0xdd, 0xb6, 0xdb, 0x77,
1904bd8597dSKaiChieh Chuang 	0x6d, 0xb6, 0xdd, 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6c, 0x00
1914bd8597dSKaiChieh Chuang };
1924bd8597dSKaiChieh Chuang 
mtk_btcvsd_snd_irq_enable(struct mtk_btcvsd_snd * bt)1934bd8597dSKaiChieh Chuang static void mtk_btcvsd_snd_irq_enable(struct mtk_btcvsd_snd *bt)
1944bd8597dSKaiChieh Chuang {
1954bd8597dSKaiChieh Chuang 	regmap_update_bits(bt->infra, bt->infra_misc_offset,
196780f202fSKaiChieh Chuang 			   bt->conn_bt_cvsd_mask, 0);
1974bd8597dSKaiChieh Chuang }
1984bd8597dSKaiChieh Chuang 
mtk_btcvsd_snd_irq_disable(struct mtk_btcvsd_snd * bt)1994bd8597dSKaiChieh Chuang static void mtk_btcvsd_snd_irq_disable(struct mtk_btcvsd_snd *bt)
2004bd8597dSKaiChieh Chuang {
2014bd8597dSKaiChieh Chuang 	regmap_update_bits(bt->infra, bt->infra_misc_offset,
202780f202fSKaiChieh Chuang 			   bt->conn_bt_cvsd_mask, bt->conn_bt_cvsd_mask);
2034bd8597dSKaiChieh Chuang }
2044bd8597dSKaiChieh Chuang 
mtk_btcvsd_snd_set_state(struct mtk_btcvsd_snd * bt,struct mtk_btcvsd_snd_stream * bt_stream,int state)2054bd8597dSKaiChieh Chuang static void mtk_btcvsd_snd_set_state(struct mtk_btcvsd_snd *bt,
2064bd8597dSKaiChieh Chuang 				     struct mtk_btcvsd_snd_stream *bt_stream,
2074bd8597dSKaiChieh Chuang 				     int state)
2084bd8597dSKaiChieh Chuang {
2094bd8597dSKaiChieh Chuang 	dev_dbg(bt->dev, "%s(), stream %d, state %d, tx->state %d, rx->state %d, irq_disabled %d\n",
2104bd8597dSKaiChieh Chuang 		__func__,
2114bd8597dSKaiChieh Chuang 		bt_stream->stream, state,
2124bd8597dSKaiChieh Chuang 		bt->tx->state, bt->rx->state, bt->irq_disabled);
2134bd8597dSKaiChieh Chuang 
2144bd8597dSKaiChieh Chuang 	bt_stream->state = state;
2154bd8597dSKaiChieh Chuang 
2164bd8597dSKaiChieh Chuang 	if (bt->tx->state == BT_SCO_STATE_IDLE &&
2174bd8597dSKaiChieh Chuang 	    bt->rx->state == BT_SCO_STATE_IDLE) {
2184bd8597dSKaiChieh Chuang 		if (!bt->irq_disabled) {
2194bd8597dSKaiChieh Chuang 			disable_irq(bt->irq_id);
2204bd8597dSKaiChieh Chuang 			mtk_btcvsd_snd_irq_disable(bt);
2214bd8597dSKaiChieh Chuang 			bt->irq_disabled = 1;
2224bd8597dSKaiChieh Chuang 		}
2234bd8597dSKaiChieh Chuang 	} else {
2244bd8597dSKaiChieh Chuang 		if (bt->irq_disabled) {
2254bd8597dSKaiChieh Chuang 			enable_irq(bt->irq_id);
2264bd8597dSKaiChieh Chuang 			mtk_btcvsd_snd_irq_enable(bt);
2274bd8597dSKaiChieh Chuang 			bt->irq_disabled = 0;
2284bd8597dSKaiChieh Chuang 		}
2294bd8597dSKaiChieh Chuang 	}
2304bd8597dSKaiChieh Chuang }
2314bd8597dSKaiChieh Chuang 
mtk_btcvsd_snd_tx_init(struct mtk_btcvsd_snd * bt)2324bd8597dSKaiChieh Chuang static int mtk_btcvsd_snd_tx_init(struct mtk_btcvsd_snd *bt)
2334bd8597dSKaiChieh Chuang {
2344bd8597dSKaiChieh Chuang 	memset(bt->tx, 0, sizeof(*bt->tx));
2354bd8597dSKaiChieh Chuang 	memset(bt->tx_packet_buf, 0, sizeof(bt->tx_packet_buf));
2364bd8597dSKaiChieh Chuang 
2374bd8597dSKaiChieh Chuang 	bt->tx->packet_size = BTCVSD_TX_PACKET_SIZE;
2384bd8597dSKaiChieh Chuang 	bt->tx->buf_size = BTCVSD_TX_BUF_SIZE;
2394bd8597dSKaiChieh Chuang 	bt->tx->timeout = 0;
2404bd8597dSKaiChieh Chuang 	bt->tx->rw_cnt = 0;
2414bd8597dSKaiChieh Chuang 	bt->tx->stream = SNDRV_PCM_STREAM_PLAYBACK;
2424bd8597dSKaiChieh Chuang 	return 0;
2434bd8597dSKaiChieh Chuang }
2444bd8597dSKaiChieh Chuang 
mtk_btcvsd_snd_rx_init(struct mtk_btcvsd_snd * bt)2454bd8597dSKaiChieh Chuang static int mtk_btcvsd_snd_rx_init(struct mtk_btcvsd_snd *bt)
2464bd8597dSKaiChieh Chuang {
2474bd8597dSKaiChieh Chuang 	memset(bt->rx, 0, sizeof(*bt->rx));
2484bd8597dSKaiChieh Chuang 	memset(bt->rx_packet_buf, 0, sizeof(bt->rx_packet_buf));
2494bd8597dSKaiChieh Chuang 
2504bd8597dSKaiChieh Chuang 	bt->rx->packet_size = BTCVSD_RX_PACKET_SIZE;
2514bd8597dSKaiChieh Chuang 	bt->rx->buf_size = BTCVSD_RX_BUF_SIZE;
2524bd8597dSKaiChieh Chuang 	bt->rx->timeout = 0;
2534bd8597dSKaiChieh Chuang 	bt->rx->rw_cnt = 0;
254fc23af99SKaiChieh Chuang 	bt->rx->stream = SNDRV_PCM_STREAM_CAPTURE;
2554bd8597dSKaiChieh Chuang 	return 0;
2564bd8597dSKaiChieh Chuang }
2574bd8597dSKaiChieh Chuang 
get_tx_time_stamp(struct mtk_btcvsd_snd * bt,struct mtk_btcvsd_snd_time_buffer_info * ts)2584bd8597dSKaiChieh Chuang static void get_tx_time_stamp(struct mtk_btcvsd_snd *bt,
2594bd8597dSKaiChieh Chuang 			      struct mtk_btcvsd_snd_time_buffer_info *ts)
2604bd8597dSKaiChieh Chuang {
2614bd8597dSKaiChieh Chuang 	ts->time_stamp_us = bt->tx->time_stamp;
2624bd8597dSKaiChieh Chuang 	ts->data_count_equi_time = bt->tx->buf_data_equivalent_time;
2634bd8597dSKaiChieh Chuang }
2644bd8597dSKaiChieh Chuang 
get_rx_time_stamp(struct mtk_btcvsd_snd * bt,struct mtk_btcvsd_snd_time_buffer_info * ts)2654bd8597dSKaiChieh Chuang static void get_rx_time_stamp(struct mtk_btcvsd_snd *bt,
2664bd8597dSKaiChieh Chuang 			      struct mtk_btcvsd_snd_time_buffer_info *ts)
2674bd8597dSKaiChieh Chuang {
2684bd8597dSKaiChieh Chuang 	ts->time_stamp_us = bt->rx->time_stamp;
2694bd8597dSKaiChieh Chuang 	ts->data_count_equi_time = bt->rx->buf_data_equivalent_time;
2704bd8597dSKaiChieh Chuang }
2714bd8597dSKaiChieh Chuang 
btcvsd_bytes_to_frame(struct snd_pcm_substream * substream,int bytes)2724bd8597dSKaiChieh Chuang static int btcvsd_bytes_to_frame(struct snd_pcm_substream *substream,
2734bd8597dSKaiChieh Chuang 				 int bytes)
2744bd8597dSKaiChieh Chuang {
2754bd8597dSKaiChieh Chuang 	int count = bytes;
2764bd8597dSKaiChieh Chuang 	struct snd_pcm_runtime *runtime = substream->runtime;
2774bd8597dSKaiChieh Chuang 
2784bd8597dSKaiChieh Chuang 	if (runtime->format == SNDRV_PCM_FORMAT_S32_LE ||
2794bd8597dSKaiChieh Chuang 	    runtime->format == SNDRV_PCM_FORMAT_U32_LE)
2804bd8597dSKaiChieh Chuang 		count = count >> 2;
2814bd8597dSKaiChieh Chuang 	else
2824bd8597dSKaiChieh Chuang 		count = count >> 1;
2834bd8597dSKaiChieh Chuang 
2844bd8597dSKaiChieh Chuang 	count = count / runtime->channels;
2854bd8597dSKaiChieh Chuang 	return count;
2864bd8597dSKaiChieh Chuang }
2874bd8597dSKaiChieh Chuang 
mtk_btcvsd_snd_data_transfer(enum bt_sco_direct dir,u8 * src,u8 * dst,unsigned int blk_size,unsigned int blk_num)2884bd8597dSKaiChieh Chuang static void mtk_btcvsd_snd_data_transfer(enum bt_sco_direct dir,
2894bd8597dSKaiChieh Chuang 					 u8 *src, u8 *dst,
2904bd8597dSKaiChieh Chuang 					 unsigned int blk_size,
2914bd8597dSKaiChieh Chuang 					 unsigned int blk_num)
2924bd8597dSKaiChieh Chuang {
2934bd8597dSKaiChieh Chuang 	unsigned int i, j;
2944bd8597dSKaiChieh Chuang 
2954bd8597dSKaiChieh Chuang 	if (blk_size == 60 || blk_size == 120 || blk_size == 20) {
2964bd8597dSKaiChieh Chuang 		u32 *src_32 = (u32 *)src;
2974bd8597dSKaiChieh Chuang 		u32 *dst_32 = (u32 *)dst;
2984bd8597dSKaiChieh Chuang 
2994bd8597dSKaiChieh Chuang 		for (i = 0; i < (blk_size * blk_num / 4); i++)
3004bd8597dSKaiChieh Chuang 			*dst_32++ = *src_32++;
3014bd8597dSKaiChieh Chuang 	} else {
3024bd8597dSKaiChieh Chuang 		u16 *src_16 = (u16 *)src;
3034bd8597dSKaiChieh Chuang 		u16 *dst_16 = (u16 *)dst;
3044bd8597dSKaiChieh Chuang 
3054bd8597dSKaiChieh Chuang 		for (j = 0; j < blk_num; j++) {
3064bd8597dSKaiChieh Chuang 			for (i = 0; i < (blk_size / 2); i++)
3074bd8597dSKaiChieh Chuang 				*dst_16++ = *src_16++;
3084bd8597dSKaiChieh Chuang 
3094bd8597dSKaiChieh Chuang 			if (dir == BT_SCO_DIRECT_BT2ARM)
3104bd8597dSKaiChieh Chuang 				src_16++;
3114bd8597dSKaiChieh Chuang 			else
3124bd8597dSKaiChieh Chuang 				dst_16++;
3134bd8597dSKaiChieh Chuang 		}
3144bd8597dSKaiChieh Chuang 	}
3154bd8597dSKaiChieh Chuang }
3164bd8597dSKaiChieh Chuang 
3174bd8597dSKaiChieh Chuang /* write encoded mute data to bt sram */
btcvsd_tx_clean_buffer(struct mtk_btcvsd_snd * bt)3184bd8597dSKaiChieh Chuang static int btcvsd_tx_clean_buffer(struct mtk_btcvsd_snd *bt)
3194bd8597dSKaiChieh Chuang {
3204bd8597dSKaiChieh Chuang 	unsigned int i;
3214bd8597dSKaiChieh Chuang 	unsigned int num_valid_addr;
3224bd8597dSKaiChieh Chuang 	unsigned long flags;
3234bd8597dSKaiChieh Chuang 	enum BT_SCO_BAND band = bt->band;
3244bd8597dSKaiChieh Chuang 
3254bd8597dSKaiChieh Chuang 	/* prepare encoded mute data */
3264bd8597dSKaiChieh Chuang 	if (band == BT_SCO_NB)
3274bd8597dSKaiChieh Chuang 		memset(bt->tx->temp_packet_buf, 170, SCO_PACKET_180);
3284bd8597dSKaiChieh Chuang 	else
3294bd8597dSKaiChieh Chuang 		memcpy(bt->tx->temp_packet_buf,
3304bd8597dSKaiChieh Chuang 		       table_msbc_silence, SCO_PACKET_180);
3314bd8597dSKaiChieh Chuang 
3324bd8597dSKaiChieh Chuang 	/* write mute data to bt tx sram buffer */
3334bd8597dSKaiChieh Chuang 	spin_lock_irqsave(&bt->tx_lock, flags);
3344bd8597dSKaiChieh Chuang 	num_valid_addr = bt->tx->buffer_info.num_valid_addr;
3354bd8597dSKaiChieh Chuang 
3364bd8597dSKaiChieh Chuang 	dev_info(bt->dev, "%s(), band %d, num_valid_addr %u\n",
3374bd8597dSKaiChieh Chuang 		 __func__, band, num_valid_addr);
3384bd8597dSKaiChieh Chuang 
3394bd8597dSKaiChieh Chuang 	for (i = 0; i < num_valid_addr; i++) {
3404bd8597dSKaiChieh Chuang 		void *dst;
3414bd8597dSKaiChieh Chuang 
3424bd8597dSKaiChieh Chuang 		dev_info(bt->dev, "%s(), clean addr 0x%lx\n", __func__,
3434bd8597dSKaiChieh Chuang 			 bt->tx->buffer_info.bt_sram_addr[i]);
3444bd8597dSKaiChieh Chuang 
3454bd8597dSKaiChieh Chuang 		dst = (void *)bt->tx->buffer_info.bt_sram_addr[i];
3464bd8597dSKaiChieh Chuang 
3474bd8597dSKaiChieh Chuang 		mtk_btcvsd_snd_data_transfer(BT_SCO_DIRECT_ARM2BT,
3484bd8597dSKaiChieh Chuang 					     bt->tx->temp_packet_buf, dst,
3494bd8597dSKaiChieh Chuang 					     bt->tx->buffer_info.packet_length,
3504bd8597dSKaiChieh Chuang 					     bt->tx->buffer_info.packet_num);
3514bd8597dSKaiChieh Chuang 	}
3524bd8597dSKaiChieh Chuang 	spin_unlock_irqrestore(&bt->tx_lock, flags);
3534bd8597dSKaiChieh Chuang 
3544bd8597dSKaiChieh Chuang 	return 0;
3554bd8597dSKaiChieh Chuang }
3564bd8597dSKaiChieh Chuang 
mtk_btcvsd_read_from_bt(struct mtk_btcvsd_snd * bt,enum bt_sco_packet_len packet_type,unsigned int packet_length,unsigned int packet_num,unsigned int blk_size,unsigned int control)3574bd8597dSKaiChieh Chuang static int mtk_btcvsd_read_from_bt(struct mtk_btcvsd_snd *bt,
3584bd8597dSKaiChieh Chuang 				   enum bt_sco_packet_len packet_type,
3594bd8597dSKaiChieh Chuang 				   unsigned int packet_length,
3604bd8597dSKaiChieh Chuang 				   unsigned int packet_num,
3614bd8597dSKaiChieh Chuang 				   unsigned int blk_size,
3624bd8597dSKaiChieh Chuang 				   unsigned int control)
3634bd8597dSKaiChieh Chuang {
3644bd8597dSKaiChieh Chuang 	unsigned int i;
3654bd8597dSKaiChieh Chuang 	int pv;
3664bd8597dSKaiChieh Chuang 	u8 *src;
3674bd8597dSKaiChieh Chuang 	unsigned int packet_buf_ofs;
3684bd8597dSKaiChieh Chuang 	unsigned long flags;
3694bd8597dSKaiChieh Chuang 	unsigned long connsys_addr_rx, ap_addr_rx;
3704bd8597dSKaiChieh Chuang 
3714bd8597dSKaiChieh Chuang 	connsys_addr_rx = *bt->bt_reg_pkt_r;
3724bd8597dSKaiChieh Chuang 	ap_addr_rx = (unsigned long)bt->bt_sram_bank2_base +
3734bd8597dSKaiChieh Chuang 		     (connsys_addr_rx & 0xFFFF);
3744bd8597dSKaiChieh Chuang 
3754bd8597dSKaiChieh Chuang 	if (connsys_addr_rx == 0xdeadfeed) {
3764bd8597dSKaiChieh Chuang 		/* bt return 0xdeadfeed if read register during bt sleep */
3774bd8597dSKaiChieh Chuang 		dev_warn(bt->dev, "%s(), connsys_addr_rx == 0xdeadfeed",
3784bd8597dSKaiChieh Chuang 			 __func__);
3794bd8597dSKaiChieh Chuang 		return -EIO;
3804bd8597dSKaiChieh Chuang 	}
3814bd8597dSKaiChieh Chuang 
3824bd8597dSKaiChieh Chuang 	src = (u8 *)ap_addr_rx;
3834bd8597dSKaiChieh Chuang 
3844bd8597dSKaiChieh Chuang 	mtk_btcvsd_snd_data_transfer(BT_SCO_DIRECT_BT2ARM, src,
3854bd8597dSKaiChieh Chuang 				     bt->rx->temp_packet_buf, packet_length,
3864bd8597dSKaiChieh Chuang 				     packet_num);
3874bd8597dSKaiChieh Chuang 
3884bd8597dSKaiChieh Chuang 	spin_lock_irqsave(&bt->rx_lock, flags);
3894bd8597dSKaiChieh Chuang 	for (i = 0; i < blk_size; i++) {
3904bd8597dSKaiChieh Chuang 		packet_buf_ofs = (bt->rx->packet_w & SCO_RX_PACKET_MASK) *
3914bd8597dSKaiChieh Chuang 				 bt->rx->packet_size;
3924bd8597dSKaiChieh Chuang 		memcpy(bt->rx_packet_buf + packet_buf_ofs,
3934bd8597dSKaiChieh Chuang 		       bt->rx->temp_packet_buf + (SCO_RX_PLC_SIZE * i),
3944bd8597dSKaiChieh Chuang 		       SCO_RX_PLC_SIZE);
3954bd8597dSKaiChieh Chuang 		if ((control & btsco_packet_valid_mask[packet_type][i]) ==
3964bd8597dSKaiChieh Chuang 		    btsco_packet_valid_mask[packet_type][i])
3974bd8597dSKaiChieh Chuang 			pv = 1;
3984bd8597dSKaiChieh Chuang 		else
3994bd8597dSKaiChieh Chuang 			pv = 0;
4004bd8597dSKaiChieh Chuang 
4014bd8597dSKaiChieh Chuang 		packet_buf_ofs += SCO_RX_PLC_SIZE;
4024bd8597dSKaiChieh Chuang 		memcpy(bt->rx_packet_buf + packet_buf_ofs, (void *)&pv,
4034bd8597dSKaiChieh Chuang 		       SCO_CVSD_PACKET_VALID_SIZE);
4044bd8597dSKaiChieh Chuang 		bt->rx->packet_w++;
4054bd8597dSKaiChieh Chuang 	}
4064bd8597dSKaiChieh Chuang 	spin_unlock_irqrestore(&bt->rx_lock, flags);
4074bd8597dSKaiChieh Chuang 	return 0;
4084bd8597dSKaiChieh Chuang }
4094bd8597dSKaiChieh Chuang 
mtk_btcvsd_write_to_bt(struct mtk_btcvsd_snd * bt,enum bt_sco_packet_len packet_type,unsigned int packet_length,unsigned int packet_num,unsigned int blk_size)41052194513SYueHaibing static int mtk_btcvsd_write_to_bt(struct mtk_btcvsd_snd *bt,
4114bd8597dSKaiChieh Chuang 				  enum bt_sco_packet_len packet_type,
4124bd8597dSKaiChieh Chuang 				  unsigned int packet_length,
4134bd8597dSKaiChieh Chuang 				  unsigned int packet_num,
4144bd8597dSKaiChieh Chuang 				  unsigned int blk_size)
4154bd8597dSKaiChieh Chuang {
4164bd8597dSKaiChieh Chuang 	unsigned int i;
4174bd8597dSKaiChieh Chuang 	unsigned long flags;
4184bd8597dSKaiChieh Chuang 	u8 *dst;
4194bd8597dSKaiChieh Chuang 	unsigned long connsys_addr_tx, ap_addr_tx;
4204bd8597dSKaiChieh Chuang 	bool new_ap_addr_tx = true;
4214bd8597dSKaiChieh Chuang 
4224bd8597dSKaiChieh Chuang 	connsys_addr_tx = *bt->bt_reg_pkt_w;
4234bd8597dSKaiChieh Chuang 	ap_addr_tx = (unsigned long)bt->bt_sram_bank2_base +
4244bd8597dSKaiChieh Chuang 		     (connsys_addr_tx & 0xFFFF);
4254bd8597dSKaiChieh Chuang 
4264bd8597dSKaiChieh Chuang 	if (connsys_addr_tx == 0xdeadfeed) {
4274bd8597dSKaiChieh Chuang 		/* bt return 0xdeadfeed if read register during bt sleep */
4284bd8597dSKaiChieh Chuang 		dev_warn(bt->dev, "%s(), connsys_addr_tx == 0xdeadfeed\n",
4294bd8597dSKaiChieh Chuang 			 __func__);
4304bd8597dSKaiChieh Chuang 		return -EIO;
4314bd8597dSKaiChieh Chuang 	}
4324bd8597dSKaiChieh Chuang 
4334bd8597dSKaiChieh Chuang 	spin_lock_irqsave(&bt->tx_lock, flags);
4344bd8597dSKaiChieh Chuang 	for (i = 0; i < blk_size; i++) {
4354bd8597dSKaiChieh Chuang 		memcpy(bt->tx->temp_packet_buf + (bt->tx->packet_size * i),
4364bd8597dSKaiChieh Chuang 		       (bt->tx_packet_buf +
4374bd8597dSKaiChieh Chuang 			(bt->tx->packet_r % SCO_TX_PACKER_BUF_NUM) *
4384bd8597dSKaiChieh Chuang 			bt->tx->packet_size),
4394bd8597dSKaiChieh Chuang 		       bt->tx->packet_size);
4404bd8597dSKaiChieh Chuang 
4414bd8597dSKaiChieh Chuang 		bt->tx->packet_r++;
4424bd8597dSKaiChieh Chuang 	}
4434bd8597dSKaiChieh Chuang 	spin_unlock_irqrestore(&bt->tx_lock, flags);
4444bd8597dSKaiChieh Chuang 
4454bd8597dSKaiChieh Chuang 	dst = (u8 *)ap_addr_tx;
4464bd8597dSKaiChieh Chuang 
4474bd8597dSKaiChieh Chuang 	if (!bt->tx->mute) {
4484bd8597dSKaiChieh Chuang 		mtk_btcvsd_snd_data_transfer(BT_SCO_DIRECT_ARM2BT,
4494bd8597dSKaiChieh Chuang 					     bt->tx->temp_packet_buf, dst,
4504bd8597dSKaiChieh Chuang 					     packet_length, packet_num);
4514bd8597dSKaiChieh Chuang 	}
4524bd8597dSKaiChieh Chuang 
4534bd8597dSKaiChieh Chuang 	/* store bt tx buffer sram info */
4544bd8597dSKaiChieh Chuang 	bt->tx->buffer_info.packet_length = packet_length;
4554bd8597dSKaiChieh Chuang 	bt->tx->buffer_info.packet_num = packet_num;
4564bd8597dSKaiChieh Chuang 	for (i = 0; i < bt->tx->buffer_info.num_valid_addr; i++) {
4574bd8597dSKaiChieh Chuang 		if (bt->tx->buffer_info.bt_sram_addr[i] == ap_addr_tx) {
4584bd8597dSKaiChieh Chuang 			new_ap_addr_tx = false;
4594bd8597dSKaiChieh Chuang 			break;
4604bd8597dSKaiChieh Chuang 		}
4614bd8597dSKaiChieh Chuang 	}
4624bd8597dSKaiChieh Chuang 	if (new_ap_addr_tx) {
4634bd8597dSKaiChieh Chuang 		unsigned int next_idx;
4644bd8597dSKaiChieh Chuang 
4654bd8597dSKaiChieh Chuang 		spin_lock_irqsave(&bt->tx_lock, flags);
4664bd8597dSKaiChieh Chuang 		bt->tx->buffer_info.num_valid_addr++;
4674bd8597dSKaiChieh Chuang 		next_idx = bt->tx->buffer_info.num_valid_addr - 1;
4684bd8597dSKaiChieh Chuang 		bt->tx->buffer_info.bt_sram_addr[next_idx] = ap_addr_tx;
4694bd8597dSKaiChieh Chuang 		spin_unlock_irqrestore(&bt->tx_lock, flags);
4704bd8597dSKaiChieh Chuang 		dev_info(bt->dev, "%s(), new ap_addr_tx = 0x%lx, num_valid_addr %d\n",
4714bd8597dSKaiChieh Chuang 			 __func__, ap_addr_tx,
4724bd8597dSKaiChieh Chuang 			 bt->tx->buffer_info.num_valid_addr);
4734bd8597dSKaiChieh Chuang 	}
4744bd8597dSKaiChieh Chuang 
4754bd8597dSKaiChieh Chuang 	if (bt->tx->mute)
4764bd8597dSKaiChieh Chuang 		btcvsd_tx_clean_buffer(bt);
4774bd8597dSKaiChieh Chuang 
4784bd8597dSKaiChieh Chuang 	return 0;
4794bd8597dSKaiChieh Chuang }
4804bd8597dSKaiChieh Chuang 
mtk_btcvsd_snd_irq_handler(int irq_id,void * dev)4814bd8597dSKaiChieh Chuang static irqreturn_t mtk_btcvsd_snd_irq_handler(int irq_id, void *dev)
4824bd8597dSKaiChieh Chuang {
4834bd8597dSKaiChieh Chuang 	struct mtk_btcvsd_snd *bt = dev;
4844bd8597dSKaiChieh Chuang 	unsigned int packet_type, packet_num, packet_length;
4854bd8597dSKaiChieh Chuang 	unsigned int buf_cnt_tx, buf_cnt_rx, control;
4864bd8597dSKaiChieh Chuang 
4874bd8597dSKaiChieh Chuang 	if (bt->rx->state != BT_SCO_STATE_RUNNING &&
4884bd8597dSKaiChieh Chuang 	    bt->rx->state != BT_SCO_STATE_ENDING &&
4894bd8597dSKaiChieh Chuang 	    bt->tx->state != BT_SCO_STATE_RUNNING &&
490f060f46fSKaiChieh Chuang 	    bt->tx->state != BT_SCO_STATE_ENDING &&
491f060f46fSKaiChieh Chuang 	    bt->tx->state != BT_SCO_STATE_LOOPBACK) {
4924bd8597dSKaiChieh Chuang 		dev_warn(bt->dev, "%s(), in idle state: rx->state: %d, tx->state: %d\n",
4934bd8597dSKaiChieh Chuang 			 __func__, bt->rx->state, bt->tx->state);
4944bd8597dSKaiChieh Chuang 		goto irq_handler_exit;
4954bd8597dSKaiChieh Chuang 	}
4964bd8597dSKaiChieh Chuang 
4974bd8597dSKaiChieh Chuang 	control = *bt->bt_reg_ctl;
4984bd8597dSKaiChieh Chuang 	packet_type = (control >> 18) & 0x7;
4994bd8597dSKaiChieh Chuang 
5004bd8597dSKaiChieh Chuang 	if (((control >> 31) & 1) == 0) {
5014bd8597dSKaiChieh Chuang 		dev_warn(bt->dev, "%s(), ((control >> 31) & 1) == 0, control 0x%x\n",
5024bd8597dSKaiChieh Chuang 			 __func__, control);
5034bd8597dSKaiChieh Chuang 		goto irq_handler_exit;
5044bd8597dSKaiChieh Chuang 	}
5054bd8597dSKaiChieh Chuang 
5064bd8597dSKaiChieh Chuang 	if (packet_type >= BT_SCO_CVSD_MAX) {
5074bd8597dSKaiChieh Chuang 		dev_warn(bt->dev, "%s(), invalid packet_type %u, exit\n",
5084bd8597dSKaiChieh Chuang 			 __func__, packet_type);
5094bd8597dSKaiChieh Chuang 		goto irq_handler_exit;
5104bd8597dSKaiChieh Chuang 	}
5114bd8597dSKaiChieh Chuang 
5124bd8597dSKaiChieh Chuang 	packet_length = btsco_packet_info[packet_type][0];
5134bd8597dSKaiChieh Chuang 	packet_num = btsco_packet_info[packet_type][1];
5144bd8597dSKaiChieh Chuang 	buf_cnt_tx = btsco_packet_info[packet_type][2];
5154bd8597dSKaiChieh Chuang 	buf_cnt_rx = btsco_packet_info[packet_type][3];
5164bd8597dSKaiChieh Chuang 
517f060f46fSKaiChieh Chuang 	if (bt->tx->state == BT_SCO_STATE_LOOPBACK) {
518f060f46fSKaiChieh Chuang 		u8 *src, *dst;
519f060f46fSKaiChieh Chuang 		unsigned long connsys_addr_rx, ap_addr_rx;
520f060f46fSKaiChieh Chuang 		unsigned long connsys_addr_tx, ap_addr_tx;
521f060f46fSKaiChieh Chuang 
522f060f46fSKaiChieh Chuang 		connsys_addr_rx = *bt->bt_reg_pkt_r;
523f060f46fSKaiChieh Chuang 		ap_addr_rx = (unsigned long)bt->bt_sram_bank2_base +
524f060f46fSKaiChieh Chuang 			     (connsys_addr_rx & 0xFFFF);
525f060f46fSKaiChieh Chuang 
526f060f46fSKaiChieh Chuang 		connsys_addr_tx = *bt->bt_reg_pkt_w;
527f060f46fSKaiChieh Chuang 		ap_addr_tx = (unsigned long)bt->bt_sram_bank2_base +
528f060f46fSKaiChieh Chuang 			     (connsys_addr_tx & 0xFFFF);
529f060f46fSKaiChieh Chuang 
530f060f46fSKaiChieh Chuang 		if (connsys_addr_tx == 0xdeadfeed ||
531f060f46fSKaiChieh Chuang 		    connsys_addr_rx == 0xdeadfeed) {
532f060f46fSKaiChieh Chuang 			/* bt return 0xdeadfeed if read reg during bt sleep */
533f060f46fSKaiChieh Chuang 			dev_warn(bt->dev, "%s(), connsys_addr_tx == 0xdeadfeed\n",
534f060f46fSKaiChieh Chuang 				 __func__);
535f060f46fSKaiChieh Chuang 			goto irq_handler_exit;
536f060f46fSKaiChieh Chuang 		}
537f060f46fSKaiChieh Chuang 
538f060f46fSKaiChieh Chuang 		src = (u8 *)ap_addr_rx;
539f060f46fSKaiChieh Chuang 		dst = (u8 *)ap_addr_tx;
540f060f46fSKaiChieh Chuang 
541f060f46fSKaiChieh Chuang 		mtk_btcvsd_snd_data_transfer(BT_SCO_DIRECT_BT2ARM, src,
542f060f46fSKaiChieh Chuang 					     bt->tx->temp_packet_buf,
543f060f46fSKaiChieh Chuang 					     packet_length,
544f060f46fSKaiChieh Chuang 					     packet_num);
545f060f46fSKaiChieh Chuang 		mtk_btcvsd_snd_data_transfer(BT_SCO_DIRECT_ARM2BT,
546f060f46fSKaiChieh Chuang 					     bt->tx->temp_packet_buf, dst,
547f060f46fSKaiChieh Chuang 					     packet_length,
548f060f46fSKaiChieh Chuang 					     packet_num);
549f060f46fSKaiChieh Chuang 		bt->rx->rw_cnt++;
550f060f46fSKaiChieh Chuang 		bt->tx->rw_cnt++;
551f060f46fSKaiChieh Chuang 	}
552f060f46fSKaiChieh Chuang 
5534bd8597dSKaiChieh Chuang 	if (bt->rx->state == BT_SCO_STATE_RUNNING ||
5544bd8597dSKaiChieh Chuang 	    bt->rx->state == BT_SCO_STATE_ENDING) {
5554bd8597dSKaiChieh Chuang 		if (bt->rx->xrun) {
5564bd8597dSKaiChieh Chuang 			if (bt->rx->packet_w - bt->rx->packet_r <=
5574bd8597dSKaiChieh Chuang 			    SCO_RX_PACKER_BUF_NUM - 2 * buf_cnt_rx) {
5584bd8597dSKaiChieh Chuang 				/*
5594bd8597dSKaiChieh Chuang 				 * free space is larger then
5604bd8597dSKaiChieh Chuang 				 * twice interrupt rx data size
5614bd8597dSKaiChieh Chuang 				 */
5624bd8597dSKaiChieh Chuang 				bt->rx->xrun = 0;
5634bd8597dSKaiChieh Chuang 				dev_warn(bt->dev, "%s(), rx->xrun 0!\n",
5644bd8597dSKaiChieh Chuang 					 __func__);
5654bd8597dSKaiChieh Chuang 			}
5664bd8597dSKaiChieh Chuang 		}
5674bd8597dSKaiChieh Chuang 
5684bd8597dSKaiChieh Chuang 		if (!bt->rx->xrun &&
5694bd8597dSKaiChieh Chuang 		    (bt->rx->packet_w - bt->rx->packet_r <=
5704bd8597dSKaiChieh Chuang 		     SCO_RX_PACKER_BUF_NUM - buf_cnt_rx)) {
5714bd8597dSKaiChieh Chuang 			mtk_btcvsd_read_from_bt(bt,
5724bd8597dSKaiChieh Chuang 						packet_type,
5734bd8597dSKaiChieh Chuang 						packet_length,
5744bd8597dSKaiChieh Chuang 						packet_num,
5754bd8597dSKaiChieh Chuang 						buf_cnt_rx,
5764bd8597dSKaiChieh Chuang 						control);
5774bd8597dSKaiChieh Chuang 			bt->rx->rw_cnt++;
5784bd8597dSKaiChieh Chuang 		} else {
5794bd8597dSKaiChieh Chuang 			bt->rx->xrun = 1;
5804bd8597dSKaiChieh Chuang 			dev_warn(bt->dev, "%s(), rx->xrun 1\n", __func__);
5814bd8597dSKaiChieh Chuang 		}
5824bd8597dSKaiChieh Chuang 	}
5834bd8597dSKaiChieh Chuang 
5844bd8597dSKaiChieh Chuang 	/* tx */
5854bd8597dSKaiChieh Chuang 	bt->tx->timeout = 0;
5864bd8597dSKaiChieh Chuang 	if ((bt->tx->state == BT_SCO_STATE_RUNNING ||
5874bd8597dSKaiChieh Chuang 	     bt->tx->state == BT_SCO_STATE_ENDING) &&
5884bd8597dSKaiChieh Chuang 	    bt->tx->trigger_start) {
5894bd8597dSKaiChieh Chuang 		if (bt->tx->xrun) {
5904bd8597dSKaiChieh Chuang 			/* prepared data is larger then twice
5914bd8597dSKaiChieh Chuang 			 * interrupt tx data size
5924bd8597dSKaiChieh Chuang 			 */
5934bd8597dSKaiChieh Chuang 			if (bt->tx->packet_w - bt->tx->packet_r >=
5944bd8597dSKaiChieh Chuang 			    2 * buf_cnt_tx) {
5954bd8597dSKaiChieh Chuang 				bt->tx->xrun = 0;
5964bd8597dSKaiChieh Chuang 				dev_warn(bt->dev, "%s(), tx->xrun 0\n",
5974bd8597dSKaiChieh Chuang 					 __func__);
5984bd8597dSKaiChieh Chuang 			}
5994bd8597dSKaiChieh Chuang 		}
6004bd8597dSKaiChieh Chuang 
6014bd8597dSKaiChieh Chuang 		if ((!bt->tx->xrun &&
6024bd8597dSKaiChieh Chuang 		     (bt->tx->packet_w - bt->tx->packet_r >= buf_cnt_tx)) ||
6034bd8597dSKaiChieh Chuang 		    bt->tx->state == BT_SCO_STATE_ENDING) {
6044bd8597dSKaiChieh Chuang 			mtk_btcvsd_write_to_bt(bt,
6054bd8597dSKaiChieh Chuang 					       packet_type,
6064bd8597dSKaiChieh Chuang 					       packet_length,
6074bd8597dSKaiChieh Chuang 					       packet_num,
6084bd8597dSKaiChieh Chuang 					       buf_cnt_tx);
6094bd8597dSKaiChieh Chuang 			bt->tx->rw_cnt++;
6104bd8597dSKaiChieh Chuang 		} else {
6114bd8597dSKaiChieh Chuang 			bt->tx->xrun = 1;
6124bd8597dSKaiChieh Chuang 			dev_warn(bt->dev, "%s(), tx->xrun 1\n", __func__);
6134bd8597dSKaiChieh Chuang 		}
6144bd8597dSKaiChieh Chuang 	}
6154bd8597dSKaiChieh Chuang 
6164bd8597dSKaiChieh Chuang 	*bt->bt_reg_ctl &= ~BT_CVSD_CLEAR;
6174bd8597dSKaiChieh Chuang 
6184bd8597dSKaiChieh Chuang 	if (bt->rx->state == BT_SCO_STATE_RUNNING ||
6194bd8597dSKaiChieh Chuang 	    bt->rx->state == BT_SCO_STATE_ENDING) {
6204bd8597dSKaiChieh Chuang 		bt->rx->wait_flag = 1;
6214bd8597dSKaiChieh Chuang 		wake_up_interruptible(&bt->rx_wait);
6224bd8597dSKaiChieh Chuang 		snd_pcm_period_elapsed(bt->rx->substream);
6234bd8597dSKaiChieh Chuang 	}
6244bd8597dSKaiChieh Chuang 	if (bt->tx->state == BT_SCO_STATE_RUNNING ||
6254bd8597dSKaiChieh Chuang 	    bt->tx->state == BT_SCO_STATE_ENDING) {
6264bd8597dSKaiChieh Chuang 		bt->tx->wait_flag = 1;
6274bd8597dSKaiChieh Chuang 		wake_up_interruptible(&bt->tx_wait);
6284bd8597dSKaiChieh Chuang 		snd_pcm_period_elapsed(bt->tx->substream);
6294bd8597dSKaiChieh Chuang 	}
6304bd8597dSKaiChieh Chuang 
6314bd8597dSKaiChieh Chuang 	return IRQ_HANDLED;
6324bd8597dSKaiChieh Chuang irq_handler_exit:
6334bd8597dSKaiChieh Chuang 	*bt->bt_reg_ctl &= ~BT_CVSD_CLEAR;
6344bd8597dSKaiChieh Chuang 	return IRQ_HANDLED;
6354bd8597dSKaiChieh Chuang }
6364bd8597dSKaiChieh Chuang 
wait_for_bt_irq(struct mtk_btcvsd_snd * bt,struct mtk_btcvsd_snd_stream * bt_stream)6374bd8597dSKaiChieh Chuang static int wait_for_bt_irq(struct mtk_btcvsd_snd *bt,
6384bd8597dSKaiChieh Chuang 			   struct mtk_btcvsd_snd_stream *bt_stream)
6394bd8597dSKaiChieh Chuang {
6404bd8597dSKaiChieh Chuang 	unsigned long long t1, t2;
6414bd8597dSKaiChieh Chuang 	/* one interrupt period = 22.5ms */
6424bd8597dSKaiChieh Chuang 	unsigned long long timeout_limit = 22500000;
6434bd8597dSKaiChieh Chuang 	int max_timeout_trial = 2;
6444bd8597dSKaiChieh Chuang 	int ret;
6454bd8597dSKaiChieh Chuang 
6464bd8597dSKaiChieh Chuang 	bt_stream->wait_flag = 0;
6474bd8597dSKaiChieh Chuang 
6484bd8597dSKaiChieh Chuang 	while (max_timeout_trial && !bt_stream->wait_flag) {
6494bd8597dSKaiChieh Chuang 		t1 = sched_clock();
6504bd8597dSKaiChieh Chuang 		if (bt_stream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
6514bd8597dSKaiChieh Chuang 			ret = wait_event_interruptible_timeout(bt->tx_wait,
6524bd8597dSKaiChieh Chuang 				bt_stream->wait_flag,
6534bd8597dSKaiChieh Chuang 				nsecs_to_jiffies(timeout_limit));
6544bd8597dSKaiChieh Chuang 		} else {
6554bd8597dSKaiChieh Chuang 			ret = wait_event_interruptible_timeout(bt->rx_wait,
6564bd8597dSKaiChieh Chuang 				bt_stream->wait_flag,
6574bd8597dSKaiChieh Chuang 				nsecs_to_jiffies(timeout_limit));
6584bd8597dSKaiChieh Chuang 		}
6594bd8597dSKaiChieh Chuang 
6604bd8597dSKaiChieh Chuang 		t2 = sched_clock();
6614bd8597dSKaiChieh Chuang 		t2 = t2 - t1; /* in ns (10^9) */
6624bd8597dSKaiChieh Chuang 
6634bd8597dSKaiChieh Chuang 		if (t2 > timeout_limit) {
6644bd8597dSKaiChieh Chuang 			dev_warn(bt->dev, "%s(), stream %d, timeout %llu, limit %llu, ret %d, flag %d\n",
6654bd8597dSKaiChieh Chuang 				 __func__, bt_stream->stream,
6664bd8597dSKaiChieh Chuang 				 t2, timeout_limit, ret,
6674bd8597dSKaiChieh Chuang 				 bt_stream->wait_flag);
6684bd8597dSKaiChieh Chuang 		}
6694bd8597dSKaiChieh Chuang 
6704bd8597dSKaiChieh Chuang 		if (ret < 0) {
6714bd8597dSKaiChieh Chuang 			/*
6724bd8597dSKaiChieh Chuang 			 * error, -ERESTARTSYS if it was interrupted by
6734bd8597dSKaiChieh Chuang 			 * a signal
6744bd8597dSKaiChieh Chuang 			 */
6754bd8597dSKaiChieh Chuang 			dev_warn(bt->dev, "%s(), stream %d, error, trial left %d\n",
6764bd8597dSKaiChieh Chuang 				 __func__,
6774bd8597dSKaiChieh Chuang 				 bt_stream->stream, max_timeout_trial);
6784bd8597dSKaiChieh Chuang 
6794bd8597dSKaiChieh Chuang 			bt_stream->timeout = 1;
6804bd8597dSKaiChieh Chuang 			return ret;
6814bd8597dSKaiChieh Chuang 		} else if (ret == 0) {
6824bd8597dSKaiChieh Chuang 			/* conidtion is false after timeout */
6834bd8597dSKaiChieh Chuang 			max_timeout_trial--;
6844bd8597dSKaiChieh Chuang 			dev_warn(bt->dev, "%s(), stream %d, error, timeout, condition is false, trial left %d\n",
6854bd8597dSKaiChieh Chuang 				 __func__,
6864bd8597dSKaiChieh Chuang 				 bt_stream->stream, max_timeout_trial);
6874bd8597dSKaiChieh Chuang 
6884bd8597dSKaiChieh Chuang 			if (max_timeout_trial <= 0) {
6894bd8597dSKaiChieh Chuang 				bt_stream->timeout = 1;
6904bd8597dSKaiChieh Chuang 				return -ETIME;
6914bd8597dSKaiChieh Chuang 			}
6924bd8597dSKaiChieh Chuang 		}
6934bd8597dSKaiChieh Chuang 	}
6944bd8597dSKaiChieh Chuang 
6954bd8597dSKaiChieh Chuang 	return 0;
6964bd8597dSKaiChieh Chuang }
6974bd8597dSKaiChieh Chuang 
mtk_btcvsd_snd_read(struct mtk_btcvsd_snd * bt,struct iov_iter * buf,size_t count)69852194513SYueHaibing static ssize_t mtk_btcvsd_snd_read(struct mtk_btcvsd_snd *bt,
69995396d83STakashi Iwai 				   struct iov_iter *buf,
7004bd8597dSKaiChieh Chuang 				   size_t count)
7014bd8597dSKaiChieh Chuang {
7024bd8597dSKaiChieh Chuang 	ssize_t read_size = 0, read_count = 0, cur_read_idx, cont;
7034bd8597dSKaiChieh Chuang 	unsigned long avail;
7044bd8597dSKaiChieh Chuang 	unsigned long flags;
7054bd8597dSKaiChieh Chuang 	unsigned int packet_size = bt->rx->packet_size;
7064bd8597dSKaiChieh Chuang 
7074bd8597dSKaiChieh Chuang 	while (count) {
7084bd8597dSKaiChieh Chuang 		spin_lock_irqsave(&bt->rx_lock, flags);
7094bd8597dSKaiChieh Chuang 		/* available data in RX packet buffer */
7104bd8597dSKaiChieh Chuang 		avail = (bt->rx->packet_w - bt->rx->packet_r) * packet_size;
7114bd8597dSKaiChieh Chuang 
7124bd8597dSKaiChieh Chuang 		cur_read_idx = (bt->rx->packet_r & SCO_RX_PACKET_MASK) *
7134bd8597dSKaiChieh Chuang 			       packet_size;
7144bd8597dSKaiChieh Chuang 		spin_unlock_irqrestore(&bt->rx_lock, flags);
7154bd8597dSKaiChieh Chuang 
7164bd8597dSKaiChieh Chuang 		if (!avail) {
7174bd8597dSKaiChieh Chuang 			int ret = wait_for_bt_irq(bt, bt->rx);
7184bd8597dSKaiChieh Chuang 
7194bd8597dSKaiChieh Chuang 			if (ret)
7204bd8597dSKaiChieh Chuang 				return read_count;
7214bd8597dSKaiChieh Chuang 
7224bd8597dSKaiChieh Chuang 			continue;
7234bd8597dSKaiChieh Chuang 		}
7244bd8597dSKaiChieh Chuang 
7254bd8597dSKaiChieh Chuang 		/* count must be multiple of packet_size */
7264bd8597dSKaiChieh Chuang 		if (count % packet_size != 0 ||
7274bd8597dSKaiChieh Chuang 		    avail % packet_size != 0) {
7284bd8597dSKaiChieh Chuang 			dev_warn(bt->dev, "%s(), count %zu or d %lu is not multiple of packet_size %dd\n",
7294bd8597dSKaiChieh Chuang 				 __func__, count, avail, packet_size);
7304bd8597dSKaiChieh Chuang 
7314bd8597dSKaiChieh Chuang 			count -= count % packet_size;
7324bd8597dSKaiChieh Chuang 			avail -= avail % packet_size;
7334bd8597dSKaiChieh Chuang 		}
7344bd8597dSKaiChieh Chuang 
7354bd8597dSKaiChieh Chuang 		if (count > avail)
7364bd8597dSKaiChieh Chuang 			read_size = avail;
7374bd8597dSKaiChieh Chuang 		else
7384bd8597dSKaiChieh Chuang 			read_size = count;
7394bd8597dSKaiChieh Chuang 
7404bd8597dSKaiChieh Chuang 		/* calculate continue space */
7414bd8597dSKaiChieh Chuang 		cont = bt->rx->buf_size - cur_read_idx;
7424bd8597dSKaiChieh Chuang 		if (read_size > cont)
7434bd8597dSKaiChieh Chuang 			read_size = cont;
7444bd8597dSKaiChieh Chuang 
74595396d83STakashi Iwai 		if (copy_to_iter(bt->rx_packet_buf + cur_read_idx,
74695396d83STakashi Iwai 				 read_size, buf) != read_size) {
74795396d83STakashi Iwai 			dev_warn(bt->dev, "%s(), copy_to_iter fail\n",
7484bd8597dSKaiChieh Chuang 				 __func__);
7494bd8597dSKaiChieh Chuang 			return -EFAULT;
7504bd8597dSKaiChieh Chuang 		}
7514bd8597dSKaiChieh Chuang 
7524bd8597dSKaiChieh Chuang 		spin_lock_irqsave(&bt->rx_lock, flags);
7534bd8597dSKaiChieh Chuang 		bt->rx->packet_r += read_size / packet_size;
7544bd8597dSKaiChieh Chuang 		spin_unlock_irqrestore(&bt->rx_lock, flags);
7554bd8597dSKaiChieh Chuang 
7564bd8597dSKaiChieh Chuang 		read_count += read_size;
7574bd8597dSKaiChieh Chuang 		count -= read_size;
7584bd8597dSKaiChieh Chuang 	}
7594bd8597dSKaiChieh Chuang 
7604bd8597dSKaiChieh Chuang 	/*
7614bd8597dSKaiChieh Chuang 	 * save current timestamp & buffer time in times_tamp and
7624bd8597dSKaiChieh Chuang 	 * buf_data_equivalent_time
7634bd8597dSKaiChieh Chuang 	 */
7644bd8597dSKaiChieh Chuang 	bt->rx->time_stamp = sched_clock();
7654bd8597dSKaiChieh Chuang 	bt->rx->buf_data_equivalent_time =
7664bd8597dSKaiChieh Chuang 		(unsigned long long)(bt->rx->packet_w - bt->rx->packet_r) *
7674bd8597dSKaiChieh Chuang 		SCO_RX_PLC_SIZE * 16 * 1000 / 2 / 64;
7684bd8597dSKaiChieh Chuang 	bt->rx->buf_data_equivalent_time += read_count * SCO_RX_PLC_SIZE *
7694bd8597dSKaiChieh Chuang 					    16 * 1000 / packet_size / 2 / 64;
7704bd8597dSKaiChieh Chuang 	/* return equivalent time(us) to data count */
7714bd8597dSKaiChieh Chuang 	bt->rx->buf_data_equivalent_time *= 1000;
7724bd8597dSKaiChieh Chuang 
7734bd8597dSKaiChieh Chuang 	return read_count;
7744bd8597dSKaiChieh Chuang }
7754bd8597dSKaiChieh Chuang 
mtk_btcvsd_snd_write(struct mtk_btcvsd_snd * bt,struct iov_iter * buf,size_t count)77652194513SYueHaibing static ssize_t mtk_btcvsd_snd_write(struct mtk_btcvsd_snd *bt,
77795396d83STakashi Iwai 				    struct iov_iter *buf,
7784bd8597dSKaiChieh Chuang 				    size_t count)
7794bd8597dSKaiChieh Chuang {
7802e5e57f0SPierre-Louis Bossart 	int written_size = count, avail, cur_write_idx, write_size, cont;
7814bd8597dSKaiChieh Chuang 	unsigned long flags;
7824bd8597dSKaiChieh Chuang 	unsigned int packet_size = bt->tx->packet_size;
7834bd8597dSKaiChieh Chuang 
7844bd8597dSKaiChieh Chuang 	/*
7854bd8597dSKaiChieh Chuang 	 * save current timestamp & buffer time in time_stamp and
7864bd8597dSKaiChieh Chuang 	 * buf_data_equivalent_time
7874bd8597dSKaiChieh Chuang 	 */
7884bd8597dSKaiChieh Chuang 	bt->tx->time_stamp = sched_clock();
7894bd8597dSKaiChieh Chuang 	bt->tx->buf_data_equivalent_time =
7904bd8597dSKaiChieh Chuang 		(unsigned long long)(bt->tx->packet_w - bt->tx->packet_r) *
7914bd8597dSKaiChieh Chuang 		packet_size * 16 * 1000 / 2 / 64;
7924bd8597dSKaiChieh Chuang 
7934bd8597dSKaiChieh Chuang 	/* return equivalent time(us) to data count */
7944bd8597dSKaiChieh Chuang 	bt->tx->buf_data_equivalent_time *= 1000;
7954bd8597dSKaiChieh Chuang 
7964bd8597dSKaiChieh Chuang 	while (count) {
7974bd8597dSKaiChieh Chuang 		spin_lock_irqsave(&bt->tx_lock, flags);
7984bd8597dSKaiChieh Chuang 		/* free space of TX packet buffer */
7994bd8597dSKaiChieh Chuang 		avail = bt->tx->buf_size -
8004bd8597dSKaiChieh Chuang 			(bt->tx->packet_w - bt->tx->packet_r) * packet_size;
8014bd8597dSKaiChieh Chuang 
8024bd8597dSKaiChieh Chuang 		cur_write_idx = (bt->tx->packet_w % SCO_TX_PACKER_BUF_NUM) *
8034bd8597dSKaiChieh Chuang 				packet_size;
8044bd8597dSKaiChieh Chuang 		spin_unlock_irqrestore(&bt->tx_lock, flags);
8054bd8597dSKaiChieh Chuang 
8064bd8597dSKaiChieh Chuang 		if (!avail) {
807bbe1f69dSLumi Lee 			int ret = wait_for_bt_irq(bt, bt->tx);
8084bd8597dSKaiChieh Chuang 
8094bd8597dSKaiChieh Chuang 			if (ret)
8104bd8597dSKaiChieh Chuang 				return written_size;
8114bd8597dSKaiChieh Chuang 
8124bd8597dSKaiChieh Chuang 			continue;
8134bd8597dSKaiChieh Chuang 		}
8144bd8597dSKaiChieh Chuang 
8154bd8597dSKaiChieh Chuang 		/* count must be multiple of bt->tx->packet_size */
8164bd8597dSKaiChieh Chuang 		if (count % packet_size != 0 ||
8174bd8597dSKaiChieh Chuang 		    avail % packet_size != 0) {
8184bd8597dSKaiChieh Chuang 			dev_warn(bt->dev, "%s(), count %zu or avail %d is not multiple of packet_size %d\n",
8194bd8597dSKaiChieh Chuang 				 __func__, count, avail, packet_size);
8204bd8597dSKaiChieh Chuang 			count -= count % packet_size;
8214bd8597dSKaiChieh Chuang 			avail -= avail % packet_size;
8224bd8597dSKaiChieh Chuang 		}
8234bd8597dSKaiChieh Chuang 
8244bd8597dSKaiChieh Chuang 		if (count > avail)
8254bd8597dSKaiChieh Chuang 			write_size = avail;
8264bd8597dSKaiChieh Chuang 		else
8274bd8597dSKaiChieh Chuang 			write_size = count;
8284bd8597dSKaiChieh Chuang 
8294bd8597dSKaiChieh Chuang 		/* calculate continue space */
8304bd8597dSKaiChieh Chuang 		cont = bt->tx->buf_size - cur_write_idx;
8314bd8597dSKaiChieh Chuang 		if (write_size > cont)
8324bd8597dSKaiChieh Chuang 			write_size = cont;
8334bd8597dSKaiChieh Chuang 
83495396d83STakashi Iwai 		if (copy_from_iter(bt->tx_packet_buf + cur_write_idx,
83595396d83STakashi Iwai 				   write_size, buf) != write_size) {
83695396d83STakashi Iwai 			dev_warn(bt->dev, "%s(), copy_from_iter fail\n",
8374bd8597dSKaiChieh Chuang 				 __func__);
8384bd8597dSKaiChieh Chuang 			return -EFAULT;
8394bd8597dSKaiChieh Chuang 		}
8404bd8597dSKaiChieh Chuang 
8414bd8597dSKaiChieh Chuang 		spin_lock_irqsave(&bt->tx_lock, flags);
8424bd8597dSKaiChieh Chuang 		bt->tx->packet_w += write_size / packet_size;
8434bd8597dSKaiChieh Chuang 		spin_unlock_irqrestore(&bt->tx_lock, flags);
8444bd8597dSKaiChieh Chuang 		count -= write_size;
8454bd8597dSKaiChieh Chuang 	}
8464bd8597dSKaiChieh Chuang 
8474bd8597dSKaiChieh Chuang 	return written_size;
8484bd8597dSKaiChieh Chuang }
8494bd8597dSKaiChieh Chuang 
get_bt_stream(struct mtk_btcvsd_snd * bt,struct snd_pcm_substream * substream)8504bd8597dSKaiChieh Chuang static struct mtk_btcvsd_snd_stream *get_bt_stream
8514bd8597dSKaiChieh Chuang 	(struct mtk_btcvsd_snd *bt, struct snd_pcm_substream *substream)
8524bd8597dSKaiChieh Chuang {
8534bd8597dSKaiChieh Chuang 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
8544bd8597dSKaiChieh Chuang 		return bt->tx;
8554bd8597dSKaiChieh Chuang 	else
8564bd8597dSKaiChieh Chuang 		return bt->rx;
8574bd8597dSKaiChieh Chuang }
8584bd8597dSKaiChieh Chuang 
8594bd8597dSKaiChieh Chuang /* pcm ops */
8604bd8597dSKaiChieh Chuang static const struct snd_pcm_hardware mtk_btcvsd_hardware = {
8614bd8597dSKaiChieh Chuang 	.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
8624bd8597dSKaiChieh Chuang 		 SNDRV_PCM_INFO_RESUME),
8634bd8597dSKaiChieh Chuang 	.formats = SNDRV_PCM_FMTBIT_S16_LE,
8644bd8597dSKaiChieh Chuang 	.buffer_bytes_max = 24 * 1024,
8654bd8597dSKaiChieh Chuang 	.period_bytes_max = 24 * 1024,
8664bd8597dSKaiChieh Chuang 	.periods_min = 2,
8674bd8597dSKaiChieh Chuang 	.periods_max = 16,
8684bd8597dSKaiChieh Chuang 	.fifo_size = 0,
8694bd8597dSKaiChieh Chuang };
8704bd8597dSKaiChieh Chuang 
mtk_pcm_btcvsd_open(struct snd_soc_component * component,struct snd_pcm_substream * substream)8719a0b72b7SKuninori Morimoto static int mtk_pcm_btcvsd_open(struct snd_soc_component *component,
8729a0b72b7SKuninori Morimoto 			       struct snd_pcm_substream *substream)
8734bd8597dSKaiChieh Chuang {
8744bd8597dSKaiChieh Chuang 	struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(component);
8754bd8597dSKaiChieh Chuang 	int ret;
8764bd8597dSKaiChieh Chuang 
8774bd8597dSKaiChieh Chuang 	dev_dbg(bt->dev, "%s(), stream %d, substream %p\n",
8784bd8597dSKaiChieh Chuang 		__func__, substream->stream, substream);
8794bd8597dSKaiChieh Chuang 
8804bd8597dSKaiChieh Chuang 	snd_soc_set_runtime_hwparams(substream, &mtk_btcvsd_hardware);
8814bd8597dSKaiChieh Chuang 
8824bd8597dSKaiChieh Chuang 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
8834bd8597dSKaiChieh Chuang 		ret = mtk_btcvsd_snd_tx_init(bt);
8844bd8597dSKaiChieh Chuang 		bt->tx->substream = substream;
8854bd8597dSKaiChieh Chuang 	} else {
8864bd8597dSKaiChieh Chuang 		ret = mtk_btcvsd_snd_rx_init(bt);
8874bd8597dSKaiChieh Chuang 		bt->rx->substream = substream;
8884bd8597dSKaiChieh Chuang 	}
8894bd8597dSKaiChieh Chuang 
8904bd8597dSKaiChieh Chuang 	return ret;
8914bd8597dSKaiChieh Chuang }
8924bd8597dSKaiChieh Chuang 
mtk_pcm_btcvsd_close(struct snd_soc_component * component,struct snd_pcm_substream * substream)8939a0b72b7SKuninori Morimoto static int mtk_pcm_btcvsd_close(struct snd_soc_component *component,
8949a0b72b7SKuninori Morimoto 				struct snd_pcm_substream *substream)
8954bd8597dSKaiChieh Chuang {
8964bd8597dSKaiChieh Chuang 	struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(component);
8974bd8597dSKaiChieh Chuang 	struct mtk_btcvsd_snd_stream *bt_stream = get_bt_stream(bt, substream);
8984bd8597dSKaiChieh Chuang 
8994bd8597dSKaiChieh Chuang 	dev_dbg(bt->dev, "%s(), stream %d\n", __func__, substream->stream);
9004bd8597dSKaiChieh Chuang 
9014bd8597dSKaiChieh Chuang 	mtk_btcvsd_snd_set_state(bt, bt_stream, BT_SCO_STATE_IDLE);
9024bd8597dSKaiChieh Chuang 	bt_stream->substream = NULL;
9034bd8597dSKaiChieh Chuang 	return 0;
9044bd8597dSKaiChieh Chuang }
9054bd8597dSKaiChieh Chuang 
mtk_pcm_btcvsd_hw_params(struct snd_soc_component * component,struct snd_pcm_substream * substream,struct snd_pcm_hw_params * hw_params)9069a0b72b7SKuninori Morimoto static int mtk_pcm_btcvsd_hw_params(struct snd_soc_component *component,
9079a0b72b7SKuninori Morimoto 				    struct snd_pcm_substream *substream,
9084bd8597dSKaiChieh Chuang 				    struct snd_pcm_hw_params *hw_params)
9094bd8597dSKaiChieh Chuang {
9104bd8597dSKaiChieh Chuang 	struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(component);
9114bd8597dSKaiChieh Chuang 
9124bd8597dSKaiChieh Chuang 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
9134bd8597dSKaiChieh Chuang 	    params_buffer_bytes(hw_params) % bt->tx->packet_size != 0) {
9144bd8597dSKaiChieh Chuang 		dev_warn(bt->dev, "%s(), error, buffer size %d not valid\n",
9154bd8597dSKaiChieh Chuang 			 __func__,
9164bd8597dSKaiChieh Chuang 			 params_buffer_bytes(hw_params));
9174bd8597dSKaiChieh Chuang 		return -EINVAL;
9184bd8597dSKaiChieh Chuang 	}
9194bd8597dSKaiChieh Chuang 
9204bd8597dSKaiChieh Chuang 	substream->runtime->dma_bytes = params_buffer_bytes(hw_params);
9214bd8597dSKaiChieh Chuang 	return 0;
9224bd8597dSKaiChieh Chuang }
9234bd8597dSKaiChieh Chuang 
mtk_pcm_btcvsd_hw_free(struct snd_soc_component * component,struct snd_pcm_substream * substream)9249a0b72b7SKuninori Morimoto static int mtk_pcm_btcvsd_hw_free(struct snd_soc_component *component,
9259a0b72b7SKuninori Morimoto 				  struct snd_pcm_substream *substream)
9264bd8597dSKaiChieh Chuang {
9274bd8597dSKaiChieh Chuang 	struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(component);
9284bd8597dSKaiChieh Chuang 
9294bd8597dSKaiChieh Chuang 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
9304bd8597dSKaiChieh Chuang 		btcvsd_tx_clean_buffer(bt);
9314bd8597dSKaiChieh Chuang 
9324bd8597dSKaiChieh Chuang 	return 0;
9334bd8597dSKaiChieh Chuang }
9344bd8597dSKaiChieh Chuang 
mtk_pcm_btcvsd_prepare(struct snd_soc_component * component,struct snd_pcm_substream * substream)9359a0b72b7SKuninori Morimoto static int mtk_pcm_btcvsd_prepare(struct snd_soc_component *component,
9369a0b72b7SKuninori Morimoto 				  struct snd_pcm_substream *substream)
9374bd8597dSKaiChieh Chuang {
9384bd8597dSKaiChieh Chuang 	struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(component);
9394bd8597dSKaiChieh Chuang 	struct mtk_btcvsd_snd_stream *bt_stream = get_bt_stream(bt, substream);
9404bd8597dSKaiChieh Chuang 
9414bd8597dSKaiChieh Chuang 	dev_dbg(bt->dev, "%s(), stream %d\n", __func__, substream->stream);
9424bd8597dSKaiChieh Chuang 
9434bd8597dSKaiChieh Chuang 	mtk_btcvsd_snd_set_state(bt, bt_stream, BT_SCO_STATE_RUNNING);
9444bd8597dSKaiChieh Chuang 	return 0;
9454bd8597dSKaiChieh Chuang }
9464bd8597dSKaiChieh Chuang 
mtk_pcm_btcvsd_trigger(struct snd_soc_component * component,struct snd_pcm_substream * substream,int cmd)9479a0b72b7SKuninori Morimoto static int mtk_pcm_btcvsd_trigger(struct snd_soc_component *component,
9489a0b72b7SKuninori Morimoto 				  struct snd_pcm_substream *substream, int cmd)
9494bd8597dSKaiChieh Chuang {
9504bd8597dSKaiChieh Chuang 	struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(component);
9514bd8597dSKaiChieh Chuang 	struct mtk_btcvsd_snd_stream *bt_stream = get_bt_stream(bt, substream);
9524bd8597dSKaiChieh Chuang 	int stream = substream->stream;
9534bd8597dSKaiChieh Chuang 	int hw_packet_ptr;
9544bd8597dSKaiChieh Chuang 
9554bd8597dSKaiChieh Chuang 	dev_dbg(bt->dev, "%s(), stream %d, cmd %d\n",
9564bd8597dSKaiChieh Chuang 		__func__, substream->stream, cmd);
9574bd8597dSKaiChieh Chuang 
9584bd8597dSKaiChieh Chuang 	switch (cmd) {
9594bd8597dSKaiChieh Chuang 	case SNDRV_PCM_TRIGGER_START:
9604bd8597dSKaiChieh Chuang 	case SNDRV_PCM_TRIGGER_RESUME:
9614bd8597dSKaiChieh Chuang 		hw_packet_ptr = stream == SNDRV_PCM_STREAM_PLAYBACK ?
9624bd8597dSKaiChieh Chuang 				bt_stream->packet_r : bt_stream->packet_w;
9634bd8597dSKaiChieh Chuang 		bt_stream->prev_packet_idx = hw_packet_ptr;
9644bd8597dSKaiChieh Chuang 		bt_stream->prev_frame = 0;
9654bd8597dSKaiChieh Chuang 		bt_stream->trigger_start = 1;
9664bd8597dSKaiChieh Chuang 		return 0;
9674bd8597dSKaiChieh Chuang 	case SNDRV_PCM_TRIGGER_STOP:
9684bd8597dSKaiChieh Chuang 	case SNDRV_PCM_TRIGGER_SUSPEND:
9694bd8597dSKaiChieh Chuang 		bt_stream->trigger_start = 0;
9704bd8597dSKaiChieh Chuang 		mtk_btcvsd_snd_set_state(bt, bt_stream, BT_SCO_STATE_ENDING);
9714bd8597dSKaiChieh Chuang 		return 0;
9724bd8597dSKaiChieh Chuang 	default:
9734bd8597dSKaiChieh Chuang 		return -EINVAL;
9744bd8597dSKaiChieh Chuang 	}
9754bd8597dSKaiChieh Chuang }
9764bd8597dSKaiChieh Chuang 
mtk_pcm_btcvsd_pointer(struct snd_soc_component * component,struct snd_pcm_substream * substream)9779a0b72b7SKuninori Morimoto static snd_pcm_uframes_t mtk_pcm_btcvsd_pointer(
9789a0b72b7SKuninori Morimoto 	struct snd_soc_component *component,
9799a0b72b7SKuninori Morimoto 	struct snd_pcm_substream *substream)
9804bd8597dSKaiChieh Chuang {
9814bd8597dSKaiChieh Chuang 	struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(component);
9824bd8597dSKaiChieh Chuang 	struct mtk_btcvsd_snd_stream *bt_stream;
9834bd8597dSKaiChieh Chuang 	snd_pcm_uframes_t frame = 0;
9844bd8597dSKaiChieh Chuang 	int byte = 0;
9854bd8597dSKaiChieh Chuang 	int hw_packet_ptr;
9864bd8597dSKaiChieh Chuang 	int packet_diff;
9874bd8597dSKaiChieh Chuang 	spinlock_t *lock;	/* spinlock for bt stream control */
9884bd8597dSKaiChieh Chuang 	unsigned long flags;
9894bd8597dSKaiChieh Chuang 
9904bd8597dSKaiChieh Chuang 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
9914bd8597dSKaiChieh Chuang 		lock = &bt->tx_lock;
9924bd8597dSKaiChieh Chuang 		bt_stream = bt->tx;
9934bd8597dSKaiChieh Chuang 	} else {
9944bd8597dSKaiChieh Chuang 		lock = &bt->rx_lock;
9954bd8597dSKaiChieh Chuang 		bt_stream = bt->rx;
9964bd8597dSKaiChieh Chuang 	}
9974bd8597dSKaiChieh Chuang 
9984bd8597dSKaiChieh Chuang 	spin_lock_irqsave(lock, flags);
9994bd8597dSKaiChieh Chuang 	hw_packet_ptr = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
10004bd8597dSKaiChieh Chuang 			bt->tx->packet_r : bt->rx->packet_w;
10014bd8597dSKaiChieh Chuang 
10024bd8597dSKaiChieh Chuang 	/* get packet diff from last time */
10034bd8597dSKaiChieh Chuang 	if (hw_packet_ptr >= bt_stream->prev_packet_idx) {
10044bd8597dSKaiChieh Chuang 		packet_diff = hw_packet_ptr - bt_stream->prev_packet_idx;
10054bd8597dSKaiChieh Chuang 	} else {
10064bd8597dSKaiChieh Chuang 		/* integer overflow */
10074bd8597dSKaiChieh Chuang 		packet_diff = (INT_MAX - bt_stream->prev_packet_idx) +
10084bd8597dSKaiChieh Chuang 			      (hw_packet_ptr - INT_MIN) + 1;
10094bd8597dSKaiChieh Chuang 	}
10104bd8597dSKaiChieh Chuang 	bt_stream->prev_packet_idx = hw_packet_ptr;
10114bd8597dSKaiChieh Chuang 
10124bd8597dSKaiChieh Chuang 	/* increased bytes */
10134bd8597dSKaiChieh Chuang 	byte = packet_diff * bt_stream->packet_size;
10144bd8597dSKaiChieh Chuang 
10154bd8597dSKaiChieh Chuang 	frame = btcvsd_bytes_to_frame(substream, byte);
10164bd8597dSKaiChieh Chuang 	frame += bt_stream->prev_frame;
10174bd8597dSKaiChieh Chuang 	frame %= substream->runtime->buffer_size;
10184bd8597dSKaiChieh Chuang 
10194bd8597dSKaiChieh Chuang 	bt_stream->prev_frame = frame;
10204bd8597dSKaiChieh Chuang 
10214bd8597dSKaiChieh Chuang 	spin_unlock_irqrestore(lock, flags);
10224bd8597dSKaiChieh Chuang 
10234bd8597dSKaiChieh Chuang 	return frame;
10244bd8597dSKaiChieh Chuang }
10254bd8597dSKaiChieh Chuang 
mtk_pcm_btcvsd_copy(struct snd_soc_component * component,struct snd_pcm_substream * substream,int channel,unsigned long pos,struct iov_iter * buf,unsigned long count)10269a0b72b7SKuninori Morimoto static int mtk_pcm_btcvsd_copy(struct snd_soc_component *component,
10279a0b72b7SKuninori Morimoto 			       struct snd_pcm_substream *substream,
10284bd8597dSKaiChieh Chuang 			       int channel, unsigned long pos,
102995396d83STakashi Iwai 			       struct iov_iter *buf, unsigned long count)
10304bd8597dSKaiChieh Chuang {
10314bd8597dSKaiChieh Chuang 	struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(component);
10324bd8597dSKaiChieh Chuang 
10334bd8597dSKaiChieh Chuang 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
1034d067b337SJiasheng Jiang 		return mtk_btcvsd_snd_write(bt, buf, count);
10354bd8597dSKaiChieh Chuang 	else
1036d067b337SJiasheng Jiang 		return mtk_btcvsd_snd_read(bt, buf, count);
10374bd8597dSKaiChieh Chuang }
10384bd8597dSKaiChieh Chuang 
10394bd8597dSKaiChieh Chuang /* kcontrol */
10404bd8597dSKaiChieh Chuang static const char *const btsco_band_str[] = {"NB", "WB"};
10414bd8597dSKaiChieh Chuang 
10424bd8597dSKaiChieh Chuang static const struct soc_enum btcvsd_enum[] = {
10434bd8597dSKaiChieh Chuang 	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(btsco_band_str), btsco_band_str),
10444bd8597dSKaiChieh Chuang };
10454bd8597dSKaiChieh Chuang 
btcvsd_band_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)10464bd8597dSKaiChieh Chuang static int btcvsd_band_get(struct snd_kcontrol *kcontrol,
10474bd8597dSKaiChieh Chuang 			   struct snd_ctl_elem_value *ucontrol)
10484bd8597dSKaiChieh Chuang {
10494bd8597dSKaiChieh Chuang 	struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
10504bd8597dSKaiChieh Chuang 	struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(cmpnt);
10514bd8597dSKaiChieh Chuang 
10524bd8597dSKaiChieh Chuang 	ucontrol->value.integer.value[0] = bt->band;
10534bd8597dSKaiChieh Chuang 	return 0;
10544bd8597dSKaiChieh Chuang }
10554bd8597dSKaiChieh Chuang 
btcvsd_band_set(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)10564bd8597dSKaiChieh Chuang static int btcvsd_band_set(struct snd_kcontrol *kcontrol,
10574bd8597dSKaiChieh Chuang 			   struct snd_ctl_elem_value *ucontrol)
10584bd8597dSKaiChieh Chuang {
10594bd8597dSKaiChieh Chuang 	struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
10604bd8597dSKaiChieh Chuang 	struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(cmpnt);
10614bd8597dSKaiChieh Chuang 	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
10624bd8597dSKaiChieh Chuang 
10634bd8597dSKaiChieh Chuang 	if (ucontrol->value.enumerated.item[0] >= e->items)
10644bd8597dSKaiChieh Chuang 		return -EINVAL;
10654bd8597dSKaiChieh Chuang 
10664bd8597dSKaiChieh Chuang 	bt->band = ucontrol->value.integer.value[0];
10674bd8597dSKaiChieh Chuang 	dev_dbg(bt->dev, "%s(), band %d\n", __func__, bt->band);
10684bd8597dSKaiChieh Chuang 	return 0;
10694bd8597dSKaiChieh Chuang }
10704bd8597dSKaiChieh Chuang 
btcvsd_loopback_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)1071f060f46fSKaiChieh Chuang static int btcvsd_loopback_get(struct snd_kcontrol *kcontrol,
1072f060f46fSKaiChieh Chuang 			       struct snd_ctl_elem_value *ucontrol)
1073f060f46fSKaiChieh Chuang {
1074f060f46fSKaiChieh Chuang 	struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
1075f060f46fSKaiChieh Chuang 	struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(cmpnt);
1076f060f46fSKaiChieh Chuang 	bool lpbk_en = bt->tx->state == BT_SCO_STATE_LOOPBACK;
1077f060f46fSKaiChieh Chuang 
1078f060f46fSKaiChieh Chuang 	ucontrol->value.integer.value[0] = lpbk_en;
1079f060f46fSKaiChieh Chuang 	return 0;
1080f060f46fSKaiChieh Chuang }
1081f060f46fSKaiChieh Chuang 
btcvsd_loopback_set(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)1082f060f46fSKaiChieh Chuang static int btcvsd_loopback_set(struct snd_kcontrol *kcontrol,
1083f060f46fSKaiChieh Chuang 			       struct snd_ctl_elem_value *ucontrol)
1084f060f46fSKaiChieh Chuang {
1085f060f46fSKaiChieh Chuang 	struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
1086f060f46fSKaiChieh Chuang 	struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(cmpnt);
1087f060f46fSKaiChieh Chuang 
1088f060f46fSKaiChieh Chuang 	if (ucontrol->value.integer.value[0]) {
1089f060f46fSKaiChieh Chuang 		mtk_btcvsd_snd_set_state(bt, bt->tx, BT_SCO_STATE_LOOPBACK);
1090f060f46fSKaiChieh Chuang 		mtk_btcvsd_snd_set_state(bt, bt->rx, BT_SCO_STATE_LOOPBACK);
1091f060f46fSKaiChieh Chuang 	} else {
1092f060f46fSKaiChieh Chuang 		mtk_btcvsd_snd_set_state(bt, bt->tx, BT_SCO_STATE_RUNNING);
1093f060f46fSKaiChieh Chuang 		mtk_btcvsd_snd_set_state(bt, bt->rx, BT_SCO_STATE_RUNNING);
1094f060f46fSKaiChieh Chuang 	}
1095f060f46fSKaiChieh Chuang 	return 0;
1096f060f46fSKaiChieh Chuang }
1097f060f46fSKaiChieh Chuang 
btcvsd_tx_mute_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)10984bd8597dSKaiChieh Chuang static int btcvsd_tx_mute_get(struct snd_kcontrol *kcontrol,
10994bd8597dSKaiChieh Chuang 			      struct snd_ctl_elem_value *ucontrol)
11004bd8597dSKaiChieh Chuang {
11014bd8597dSKaiChieh Chuang 	struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
11024bd8597dSKaiChieh Chuang 	struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(cmpnt);
11034bd8597dSKaiChieh Chuang 
11044bd8597dSKaiChieh Chuang 	if (!bt->tx) {
11054bd8597dSKaiChieh Chuang 		ucontrol->value.integer.value[0] = 0;
11064bd8597dSKaiChieh Chuang 		return 0;
11074bd8597dSKaiChieh Chuang 	}
11084bd8597dSKaiChieh Chuang 
11094bd8597dSKaiChieh Chuang 	ucontrol->value.integer.value[0] = bt->tx->mute;
11104bd8597dSKaiChieh Chuang 	return 0;
11114bd8597dSKaiChieh Chuang }
11124bd8597dSKaiChieh Chuang 
btcvsd_tx_mute_set(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)11134bd8597dSKaiChieh Chuang static int btcvsd_tx_mute_set(struct snd_kcontrol *kcontrol,
11144bd8597dSKaiChieh Chuang 			      struct snd_ctl_elem_value *ucontrol)
11154bd8597dSKaiChieh Chuang {
11164bd8597dSKaiChieh Chuang 	struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
11174bd8597dSKaiChieh Chuang 	struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(cmpnt);
11184bd8597dSKaiChieh Chuang 
11194bd8597dSKaiChieh Chuang 	if (!bt->tx)
11204bd8597dSKaiChieh Chuang 		return 0;
11214bd8597dSKaiChieh Chuang 
11224bd8597dSKaiChieh Chuang 	bt->tx->mute = ucontrol->value.integer.value[0];
11234bd8597dSKaiChieh Chuang 	return 0;
11244bd8597dSKaiChieh Chuang }
11254bd8597dSKaiChieh Chuang 
btcvsd_rx_irq_received_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)11264bd8597dSKaiChieh Chuang static int btcvsd_rx_irq_received_get(struct snd_kcontrol *kcontrol,
11274bd8597dSKaiChieh Chuang 				      struct snd_ctl_elem_value *ucontrol)
11284bd8597dSKaiChieh Chuang {
11294bd8597dSKaiChieh Chuang 	struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
11304bd8597dSKaiChieh Chuang 	struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(cmpnt);
11314bd8597dSKaiChieh Chuang 
11324bd8597dSKaiChieh Chuang 	if (!bt->rx)
11334bd8597dSKaiChieh Chuang 		return 0;
11344bd8597dSKaiChieh Chuang 
11354bd8597dSKaiChieh Chuang 	ucontrol->value.integer.value[0] = bt->rx->rw_cnt ? 1 : 0;
11364bd8597dSKaiChieh Chuang 	return 0;
11374bd8597dSKaiChieh Chuang }
11384bd8597dSKaiChieh Chuang 
btcvsd_rx_timeout_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)11394bd8597dSKaiChieh Chuang static int btcvsd_rx_timeout_get(struct snd_kcontrol *kcontrol,
11404bd8597dSKaiChieh Chuang 				 struct snd_ctl_elem_value *ucontrol)
11414bd8597dSKaiChieh Chuang {
11424bd8597dSKaiChieh Chuang 	struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
11434bd8597dSKaiChieh Chuang 	struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(cmpnt);
11444bd8597dSKaiChieh Chuang 
11454bd8597dSKaiChieh Chuang 	if (!bt->rx)
11464bd8597dSKaiChieh Chuang 		return 0;
11474bd8597dSKaiChieh Chuang 
11484bd8597dSKaiChieh Chuang 	ucontrol->value.integer.value[0] = bt->rx->timeout;
11494bd8597dSKaiChieh Chuang 	bt->rx->timeout = 0;
11504bd8597dSKaiChieh Chuang 	return 0;
11514bd8597dSKaiChieh Chuang }
11524bd8597dSKaiChieh Chuang 
btcvsd_rx_timestamp_get(struct snd_kcontrol * kcontrol,unsigned int __user * data,unsigned int size)11534bd8597dSKaiChieh Chuang static int btcvsd_rx_timestamp_get(struct snd_kcontrol *kcontrol,
11544bd8597dSKaiChieh Chuang 				   unsigned int __user *data, unsigned int size)
11554bd8597dSKaiChieh Chuang {
11564bd8597dSKaiChieh Chuang 	struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
11574bd8597dSKaiChieh Chuang 	struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(cmpnt);
11584bd8597dSKaiChieh Chuang 	int ret = 0;
11594bd8597dSKaiChieh Chuang 	struct mtk_btcvsd_snd_time_buffer_info time_buffer_info_rx;
11604bd8597dSKaiChieh Chuang 
11614bd8597dSKaiChieh Chuang 	if (size > sizeof(struct mtk_btcvsd_snd_time_buffer_info))
11624bd8597dSKaiChieh Chuang 		return -EINVAL;
11634bd8597dSKaiChieh Chuang 
11644bd8597dSKaiChieh Chuang 	get_rx_time_stamp(bt, &time_buffer_info_rx);
11654bd8597dSKaiChieh Chuang 
11664bd8597dSKaiChieh Chuang 	dev_dbg(bt->dev, "%s(), time_stamp_us %llu, data_count_equi_time %llu",
11674bd8597dSKaiChieh Chuang 		__func__,
11684bd8597dSKaiChieh Chuang 		time_buffer_info_rx.time_stamp_us,
11694bd8597dSKaiChieh Chuang 		time_buffer_info_rx.data_count_equi_time);
11704bd8597dSKaiChieh Chuang 
11714bd8597dSKaiChieh Chuang 	if (copy_to_user(data, &time_buffer_info_rx,
11724bd8597dSKaiChieh Chuang 			 sizeof(struct mtk_btcvsd_snd_time_buffer_info))) {
11734bd8597dSKaiChieh Chuang 		dev_warn(bt->dev, "%s(), copy_to_user fail", __func__);
11744bd8597dSKaiChieh Chuang 		ret = -EFAULT;
11754bd8597dSKaiChieh Chuang 	}
11764bd8597dSKaiChieh Chuang 
11774bd8597dSKaiChieh Chuang 	return ret;
11784bd8597dSKaiChieh Chuang }
11794bd8597dSKaiChieh Chuang 
btcvsd_tx_irq_received_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)11804bd8597dSKaiChieh Chuang static int btcvsd_tx_irq_received_get(struct snd_kcontrol *kcontrol,
11814bd8597dSKaiChieh Chuang 				      struct snd_ctl_elem_value *ucontrol)
11824bd8597dSKaiChieh Chuang {
11834bd8597dSKaiChieh Chuang 	struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
11844bd8597dSKaiChieh Chuang 	struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(cmpnt);
11854bd8597dSKaiChieh Chuang 
11864bd8597dSKaiChieh Chuang 	if (!bt->tx)
11874bd8597dSKaiChieh Chuang 		return 0;
11884bd8597dSKaiChieh Chuang 
11894bd8597dSKaiChieh Chuang 	ucontrol->value.integer.value[0] = bt->tx->rw_cnt ? 1 : 0;
11904bd8597dSKaiChieh Chuang 	return 0;
11914bd8597dSKaiChieh Chuang }
11924bd8597dSKaiChieh Chuang 
btcvsd_tx_timeout_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)11934bd8597dSKaiChieh Chuang static int btcvsd_tx_timeout_get(struct snd_kcontrol *kcontrol,
11944bd8597dSKaiChieh Chuang 				 struct snd_ctl_elem_value *ucontrol)
11954bd8597dSKaiChieh Chuang {
11964bd8597dSKaiChieh Chuang 	struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
11974bd8597dSKaiChieh Chuang 	struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(cmpnt);
11984bd8597dSKaiChieh Chuang 
11994bd8597dSKaiChieh Chuang 	ucontrol->value.integer.value[0] = bt->tx->timeout;
12004bd8597dSKaiChieh Chuang 	return 0;
12014bd8597dSKaiChieh Chuang }
12024bd8597dSKaiChieh Chuang 
btcvsd_tx_timestamp_get(struct snd_kcontrol * kcontrol,unsigned int __user * data,unsigned int size)12034bd8597dSKaiChieh Chuang static int btcvsd_tx_timestamp_get(struct snd_kcontrol *kcontrol,
12044bd8597dSKaiChieh Chuang 				   unsigned int __user *data, unsigned int size)
12054bd8597dSKaiChieh Chuang {
12064bd8597dSKaiChieh Chuang 	struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
12074bd8597dSKaiChieh Chuang 	struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(cmpnt);
12084bd8597dSKaiChieh Chuang 	int ret = 0;
12094bd8597dSKaiChieh Chuang 	struct mtk_btcvsd_snd_time_buffer_info time_buffer_info_tx;
12104bd8597dSKaiChieh Chuang 
12114bd8597dSKaiChieh Chuang 	if (size > sizeof(struct mtk_btcvsd_snd_time_buffer_info))
12124bd8597dSKaiChieh Chuang 		return -EINVAL;
12134bd8597dSKaiChieh Chuang 
12144bd8597dSKaiChieh Chuang 	get_tx_time_stamp(bt, &time_buffer_info_tx);
12154bd8597dSKaiChieh Chuang 
12164bd8597dSKaiChieh Chuang 	dev_dbg(bt->dev, "%s(), time_stamp_us %llu, data_count_equi_time %llu",
12174bd8597dSKaiChieh Chuang 		__func__,
12184bd8597dSKaiChieh Chuang 		time_buffer_info_tx.time_stamp_us,
12194bd8597dSKaiChieh Chuang 		time_buffer_info_tx.data_count_equi_time);
12204bd8597dSKaiChieh Chuang 
12214bd8597dSKaiChieh Chuang 	if (copy_to_user(data, &time_buffer_info_tx,
12224bd8597dSKaiChieh Chuang 			 sizeof(struct mtk_btcvsd_snd_time_buffer_info))) {
12234bd8597dSKaiChieh Chuang 		dev_warn(bt->dev, "%s(), copy_to_user fail", __func__);
12244bd8597dSKaiChieh Chuang 		ret = -EFAULT;
12254bd8597dSKaiChieh Chuang 	}
12264bd8597dSKaiChieh Chuang 
12274bd8597dSKaiChieh Chuang 	return ret;
12284bd8597dSKaiChieh Chuang }
12294bd8597dSKaiChieh Chuang 
12304bd8597dSKaiChieh Chuang static const struct snd_kcontrol_new mtk_btcvsd_snd_controls[] = {
12314bd8597dSKaiChieh Chuang 	SOC_ENUM_EXT("BTCVSD Band", btcvsd_enum[0],
12324bd8597dSKaiChieh Chuang 		     btcvsd_band_get, btcvsd_band_set),
1233f060f46fSKaiChieh Chuang 	SOC_SINGLE_BOOL_EXT("BTCVSD Loopback Switch", 0,
1234f060f46fSKaiChieh Chuang 			    btcvsd_loopback_get, btcvsd_loopback_set),
12354bd8597dSKaiChieh Chuang 	SOC_SINGLE_BOOL_EXT("BTCVSD Tx Mute Switch", 0,
12364bd8597dSKaiChieh Chuang 			    btcvsd_tx_mute_get, btcvsd_tx_mute_set),
12374bd8597dSKaiChieh Chuang 	SOC_SINGLE_BOOL_EXT("BTCVSD Tx Irq Received Switch", 0,
12384bd8597dSKaiChieh Chuang 			    btcvsd_tx_irq_received_get, NULL),
12394bd8597dSKaiChieh Chuang 	SOC_SINGLE_BOOL_EXT("BTCVSD Tx Timeout Switch", 0,
12404bd8597dSKaiChieh Chuang 			    btcvsd_tx_timeout_get, NULL),
12414bd8597dSKaiChieh Chuang 	SOC_SINGLE_BOOL_EXT("BTCVSD Rx Irq Received Switch", 0,
12424bd8597dSKaiChieh Chuang 			    btcvsd_rx_irq_received_get, NULL),
12434bd8597dSKaiChieh Chuang 	SOC_SINGLE_BOOL_EXT("BTCVSD Rx Timeout Switch", 0,
12444bd8597dSKaiChieh Chuang 			    btcvsd_rx_timeout_get, NULL),
12454bd8597dSKaiChieh Chuang 	SND_SOC_BYTES_TLV("BTCVSD Rx Timestamp",
12464bd8597dSKaiChieh Chuang 			  sizeof(struct mtk_btcvsd_snd_time_buffer_info),
12474bd8597dSKaiChieh Chuang 			  btcvsd_rx_timestamp_get, NULL),
12484bd8597dSKaiChieh Chuang 	SND_SOC_BYTES_TLV("BTCVSD Tx Timestamp",
12494bd8597dSKaiChieh Chuang 			  sizeof(struct mtk_btcvsd_snd_time_buffer_info),
12504bd8597dSKaiChieh Chuang 			  btcvsd_tx_timestamp_get, NULL),
12514bd8597dSKaiChieh Chuang };
12524bd8597dSKaiChieh Chuang 
mtk_btcvsd_snd_component_probe(struct snd_soc_component * component)12534bd8597dSKaiChieh Chuang static int mtk_btcvsd_snd_component_probe(struct snd_soc_component *component)
12544bd8597dSKaiChieh Chuang {
12554bd8597dSKaiChieh Chuang 	return snd_soc_add_component_controls(component,
12564bd8597dSKaiChieh Chuang 		mtk_btcvsd_snd_controls,
12574bd8597dSKaiChieh Chuang 		ARRAY_SIZE(mtk_btcvsd_snd_controls));
12584bd8597dSKaiChieh Chuang }
12594bd8597dSKaiChieh Chuang 
12604bd8597dSKaiChieh Chuang static const struct snd_soc_component_driver mtk_btcvsd_snd_platform = {
12614bd8597dSKaiChieh Chuang 	.name		= BTCVSD_SND_NAME,
12624bd8597dSKaiChieh Chuang 	.probe		= mtk_btcvsd_snd_component_probe,
12639a0b72b7SKuninori Morimoto 	.open		= mtk_pcm_btcvsd_open,
12649a0b72b7SKuninori Morimoto 	.close		= mtk_pcm_btcvsd_close,
12659a0b72b7SKuninori Morimoto 	.hw_params	= mtk_pcm_btcvsd_hw_params,
12669a0b72b7SKuninori Morimoto 	.hw_free	= mtk_pcm_btcvsd_hw_free,
12679a0b72b7SKuninori Morimoto 	.prepare	= mtk_pcm_btcvsd_prepare,
12689a0b72b7SKuninori Morimoto 	.trigger	= mtk_pcm_btcvsd_trigger,
12699a0b72b7SKuninori Morimoto 	.pointer	= mtk_pcm_btcvsd_pointer,
127095396d83STakashi Iwai 	.copy		= mtk_pcm_btcvsd_copy,
12714bd8597dSKaiChieh Chuang };
12724bd8597dSKaiChieh Chuang 
mtk_btcvsd_snd_probe(struct platform_device * pdev)12734bd8597dSKaiChieh Chuang static int mtk_btcvsd_snd_probe(struct platform_device *pdev)
12744bd8597dSKaiChieh Chuang {
1275b6052c3cSChristophe JAILLET 	int ret;
12764bd8597dSKaiChieh Chuang 	int irq_id;
12774bd8597dSKaiChieh Chuang 	u32 offset[5] = {0, 0, 0, 0, 0};
12784bd8597dSKaiChieh Chuang 	struct mtk_btcvsd_snd *btcvsd;
12794bd8597dSKaiChieh Chuang 	struct device *dev = &pdev->dev;
12804bd8597dSKaiChieh Chuang 
12814bd8597dSKaiChieh Chuang 	/* init btcvsd private data */
12824bd8597dSKaiChieh Chuang 	btcvsd = devm_kzalloc(dev, sizeof(*btcvsd), GFP_KERNEL);
12834bd8597dSKaiChieh Chuang 	if (!btcvsd)
12844bd8597dSKaiChieh Chuang 		return -ENOMEM;
12854bd8597dSKaiChieh Chuang 	platform_set_drvdata(pdev, btcvsd);
12864bd8597dSKaiChieh Chuang 	btcvsd->dev = dev;
12874bd8597dSKaiChieh Chuang 
12884bd8597dSKaiChieh Chuang 	/* init tx/rx */
12894bd8597dSKaiChieh Chuang 	btcvsd->rx = devm_kzalloc(btcvsd->dev, sizeof(*btcvsd->rx), GFP_KERNEL);
12904bd8597dSKaiChieh Chuang 	if (!btcvsd->rx)
12914bd8597dSKaiChieh Chuang 		return -ENOMEM;
12924bd8597dSKaiChieh Chuang 
12934bd8597dSKaiChieh Chuang 	btcvsd->tx = devm_kzalloc(btcvsd->dev, sizeof(*btcvsd->tx), GFP_KERNEL);
12944bd8597dSKaiChieh Chuang 	if (!btcvsd->tx)
12954bd8597dSKaiChieh Chuang 		return -ENOMEM;
12964bd8597dSKaiChieh Chuang 
12974bd8597dSKaiChieh Chuang 	spin_lock_init(&btcvsd->tx_lock);
12984bd8597dSKaiChieh Chuang 	spin_lock_init(&btcvsd->rx_lock);
12994bd8597dSKaiChieh Chuang 
13004bd8597dSKaiChieh Chuang 	init_waitqueue_head(&btcvsd->tx_wait);
13014bd8597dSKaiChieh Chuang 	init_waitqueue_head(&btcvsd->rx_wait);
13024bd8597dSKaiChieh Chuang 
13034bd8597dSKaiChieh Chuang 	mtk_btcvsd_snd_tx_init(btcvsd);
13044bd8597dSKaiChieh Chuang 	mtk_btcvsd_snd_rx_init(btcvsd);
13054bd8597dSKaiChieh Chuang 
13064bd8597dSKaiChieh Chuang 	/* irq */
13074bd8597dSKaiChieh Chuang 	irq_id = platform_get_irq(pdev, 0);
1308cf9441adSStephen Boyd 	if (irq_id <= 0)
13094bd8597dSKaiChieh Chuang 		return irq_id < 0 ? irq_id : -ENXIO;
13104bd8597dSKaiChieh Chuang 
13114bd8597dSKaiChieh Chuang 	ret = devm_request_irq(dev, irq_id, mtk_btcvsd_snd_irq_handler,
13124bd8597dSKaiChieh Chuang 			       IRQF_TRIGGER_LOW, "BTCVSD_ISR_Handle",
13134bd8597dSKaiChieh Chuang 			       (void *)btcvsd);
13144bd8597dSKaiChieh Chuang 	if (ret) {
13154bd8597dSKaiChieh Chuang 		dev_err(dev, "could not request_irq for BTCVSD_ISR_Handle\n");
13164bd8597dSKaiChieh Chuang 		return ret;
13174bd8597dSKaiChieh Chuang 	}
13184bd8597dSKaiChieh Chuang 
13194bd8597dSKaiChieh Chuang 	btcvsd->irq_id = irq_id;
13204bd8597dSKaiChieh Chuang 
13214bd8597dSKaiChieh Chuang 	/* iomap */
13224bd8597dSKaiChieh Chuang 	btcvsd->bt_pkv_base = of_iomap(dev->of_node, 0);
13234bd8597dSKaiChieh Chuang 	if (!btcvsd->bt_pkv_base) {
13244bd8597dSKaiChieh Chuang 		dev_err(dev, "iomap bt_pkv_base fail\n");
13254bd8597dSKaiChieh Chuang 		return -EIO;
13264bd8597dSKaiChieh Chuang 	}
13274bd8597dSKaiChieh Chuang 
13284bd8597dSKaiChieh Chuang 	btcvsd->bt_sram_bank2_base = of_iomap(dev->of_node, 1);
13294bd8597dSKaiChieh Chuang 	if (!btcvsd->bt_sram_bank2_base) {
13304bd8597dSKaiChieh Chuang 		dev_err(dev, "iomap bt_sram_bank2_base fail\n");
1331b6052c3cSChristophe JAILLET 		ret = -EIO;
1332b6052c3cSChristophe JAILLET 		goto unmap_pkv_err;
13334bd8597dSKaiChieh Chuang 	}
13344bd8597dSKaiChieh Chuang 
13354bd8597dSKaiChieh Chuang 	btcvsd->infra = syscon_regmap_lookup_by_phandle(dev->of_node,
13364bd8597dSKaiChieh Chuang 							"mediatek,infracfg");
13374bd8597dSKaiChieh Chuang 	if (IS_ERR(btcvsd->infra)) {
13384bd8597dSKaiChieh Chuang 		dev_err(dev, "cannot find infra controller: %ld\n",
13394bd8597dSKaiChieh Chuang 			PTR_ERR(btcvsd->infra));
1340b6052c3cSChristophe JAILLET 		ret = PTR_ERR(btcvsd->infra);
1341b6052c3cSChristophe JAILLET 		goto unmap_bank2_err;
13424bd8597dSKaiChieh Chuang 	}
13434bd8597dSKaiChieh Chuang 
13444bd8597dSKaiChieh Chuang 	/* get offset */
13454bd8597dSKaiChieh Chuang 	ret = of_property_read_u32_array(dev->of_node, "mediatek,offset",
13464bd8597dSKaiChieh Chuang 					 offset,
13474bd8597dSKaiChieh Chuang 					 ARRAY_SIZE(offset));
13484bd8597dSKaiChieh Chuang 	if (ret) {
1349766cc496SColin Ian King 		dev_warn(dev, "%s(), get offset fail, ret %d\n", __func__, ret);
1350b6052c3cSChristophe JAILLET 		goto unmap_bank2_err;
13514bd8597dSKaiChieh Chuang 	}
13524bd8597dSKaiChieh Chuang 	btcvsd->infra_misc_offset = offset[0];
13534bd8597dSKaiChieh Chuang 	btcvsd->conn_bt_cvsd_mask = offset[1];
13544bd8597dSKaiChieh Chuang 	btcvsd->cvsd_mcu_read_offset = offset[2];
13554bd8597dSKaiChieh Chuang 	btcvsd->cvsd_mcu_write_offset = offset[3];
13564bd8597dSKaiChieh Chuang 	btcvsd->cvsd_packet_indicator = offset[4];
13574bd8597dSKaiChieh Chuang 
13584bd8597dSKaiChieh Chuang 	btcvsd->bt_reg_pkt_r = btcvsd->bt_pkv_base +
13594bd8597dSKaiChieh Chuang 			       btcvsd->cvsd_mcu_read_offset;
13604bd8597dSKaiChieh Chuang 	btcvsd->bt_reg_pkt_w = btcvsd->bt_pkv_base +
13614bd8597dSKaiChieh Chuang 			       btcvsd->cvsd_mcu_write_offset;
13624bd8597dSKaiChieh Chuang 	btcvsd->bt_reg_ctl = btcvsd->bt_pkv_base +
13634bd8597dSKaiChieh Chuang 			     btcvsd->cvsd_packet_indicator;
13644bd8597dSKaiChieh Chuang 
13654bd8597dSKaiChieh Chuang 	/* init state */
13664bd8597dSKaiChieh Chuang 	mtk_btcvsd_snd_set_state(btcvsd, btcvsd->tx, BT_SCO_STATE_IDLE);
13674bd8597dSKaiChieh Chuang 	mtk_btcvsd_snd_set_state(btcvsd, btcvsd->rx, BT_SCO_STATE_IDLE);
13684bd8597dSKaiChieh Chuang 
1369b6052c3cSChristophe JAILLET 	ret = devm_snd_soc_register_component(dev, &mtk_btcvsd_snd_platform,
13704bd8597dSKaiChieh Chuang 					      NULL, 0);
1371b6052c3cSChristophe JAILLET 	if (ret)
1372b6052c3cSChristophe JAILLET 		goto unmap_bank2_err;
1373b6052c3cSChristophe JAILLET 
1374b6052c3cSChristophe JAILLET 	return 0;
1375b6052c3cSChristophe JAILLET 
1376b6052c3cSChristophe JAILLET unmap_bank2_err:
1377b6052c3cSChristophe JAILLET 	iounmap(btcvsd->bt_sram_bank2_base);
1378b6052c3cSChristophe JAILLET unmap_pkv_err:
1379b6052c3cSChristophe JAILLET 	iounmap(btcvsd->bt_pkv_base);
1380b6052c3cSChristophe JAILLET 	return ret;
13814bd8597dSKaiChieh Chuang }
13824bd8597dSKaiChieh Chuang 
mtk_btcvsd_snd_remove(struct platform_device * pdev)138309f8cf04SUwe Kleine-König static void mtk_btcvsd_snd_remove(struct platform_device *pdev)
13844bd8597dSKaiChieh Chuang {
13854bd8597dSKaiChieh Chuang 	struct mtk_btcvsd_snd *btcvsd = dev_get_drvdata(&pdev->dev);
13864bd8597dSKaiChieh Chuang 
13874bd8597dSKaiChieh Chuang 	iounmap(btcvsd->bt_pkv_base);
13884bd8597dSKaiChieh Chuang 	iounmap(btcvsd->bt_sram_bank2_base);
13894bd8597dSKaiChieh Chuang }
13904bd8597dSKaiChieh Chuang 
13914bd8597dSKaiChieh Chuang static const struct of_device_id mtk_btcvsd_snd_dt_match[] = {
13924bd8597dSKaiChieh Chuang 	{ .compatible = "mediatek,mtk-btcvsd-snd", },
13934bd8597dSKaiChieh Chuang 	{},
13944bd8597dSKaiChieh Chuang };
13954bd8597dSKaiChieh Chuang MODULE_DEVICE_TABLE(of, mtk_btcvsd_snd_dt_match);
13964bd8597dSKaiChieh Chuang 
13974bd8597dSKaiChieh Chuang static struct platform_driver mtk_btcvsd_snd_driver = {
13984bd8597dSKaiChieh Chuang 	.driver = {
13994bd8597dSKaiChieh Chuang 		.name = "mtk-btcvsd-snd",
14004bd8597dSKaiChieh Chuang 		.of_match_table = mtk_btcvsd_snd_dt_match,
14014bd8597dSKaiChieh Chuang 	},
14024bd8597dSKaiChieh Chuang 	.probe = mtk_btcvsd_snd_probe,
1403*130af75bSUwe Kleine-König 	.remove = mtk_btcvsd_snd_remove,
14044bd8597dSKaiChieh Chuang };
14054bd8597dSKaiChieh Chuang 
14064bd8597dSKaiChieh Chuang module_platform_driver(mtk_btcvsd_snd_driver);
14074bd8597dSKaiChieh Chuang 
14084bd8597dSKaiChieh Chuang MODULE_DESCRIPTION("Mediatek ALSA BT SCO CVSD/MSBC Driver");
14094bd8597dSKaiChieh Chuang MODULE_AUTHOR("KaiChieh Chuang <kaichieh.chuang@mediatek.com>");
14104bd8597dSKaiChieh Chuang MODULE_LICENSE("GPL v2");
1411