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