Commit | Line | Data |
---|---|---|
4bd8597d KC |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // | |
3 | // Mediatek ALSA BT SCO CVSD/MSBC Driver | |
4 | // | |
5 | // Copyright (c) 2019 MediaTek Inc. | |
6 | // Author: KaiChieh Chuang <kaichieh.chuang@mediatek.com> | |
7 | ||
8 | #include <linux/mfd/syscon.h> | |
9 | #include <linux/module.h> | |
10 | #include <linux/of_address.h> | |
11 | #include <linux/sched/clock.h> | |
12 | ||
13 | #include <sound/soc.h> | |
14 | ||
15 | #define BTCVSD_SND_NAME "mtk-btcvsd-snd" | |
16 | ||
17 | #define BT_CVSD_TX_NREADY BIT(21) | |
18 | #define BT_CVSD_RX_READY BIT(22) | |
19 | #define BT_CVSD_TX_UNDERFLOW BIT(23) | |
20 | #define BT_CVSD_RX_OVERFLOW BIT(24) | |
21 | #define BT_CVSD_INTERRUPT BIT(31) | |
22 | ||
23 | #define BT_CVSD_CLEAR \ | |
24 | (BT_CVSD_TX_NREADY | BT_CVSD_RX_READY | BT_CVSD_TX_UNDERFLOW |\ | |
25 | BT_CVSD_RX_OVERFLOW | BT_CVSD_INTERRUPT) | |
26 | ||
27 | /* TX */ | |
28 | #define SCO_TX_ENCODE_SIZE (60) | |
29 | /* 18 = 6 * 180 / SCO_TX_ENCODE_SIZE */ | |
30 | #define SCO_TX_PACKER_BUF_NUM (18) | |
31 | ||
32 | /* RX */ | |
33 | #define SCO_RX_PLC_SIZE (30) | |
34 | #define SCO_RX_PACKER_BUF_NUM (64) | |
35 | #define SCO_RX_PACKET_MASK (0x3F) | |
36 | ||
37 | #define SCO_CVSD_PACKET_VALID_SIZE 2 | |
38 | ||
39 | #define SCO_PACKET_120 120 | |
40 | #define SCO_PACKET_180 180 | |
41 | ||
42 | #define BTCVSD_RX_PACKET_SIZE (SCO_RX_PLC_SIZE + SCO_CVSD_PACKET_VALID_SIZE) | |
43 | #define BTCVSD_TX_PACKET_SIZE (SCO_TX_ENCODE_SIZE) | |
44 | ||
45 | #define BTCVSD_RX_BUF_SIZE (BTCVSD_RX_PACKET_SIZE * SCO_RX_PACKER_BUF_NUM) | |
46 | #define BTCVSD_TX_BUF_SIZE (BTCVSD_TX_PACKET_SIZE * SCO_TX_PACKER_BUF_NUM) | |
47 | ||
48 | enum bt_sco_state { | |
49 | BT_SCO_STATE_IDLE, | |
50 | BT_SCO_STATE_RUNNING, | |
51 | BT_SCO_STATE_ENDING, | |
f060f46f | 52 | BT_SCO_STATE_LOOPBACK, |
4bd8597d KC |
53 | }; |
54 | ||
55 | enum bt_sco_direct { | |
56 | BT_SCO_DIRECT_BT2ARM, | |
57 | BT_SCO_DIRECT_ARM2BT, | |
58 | }; | |
59 | ||
60 | enum bt_sco_packet_len { | |
61 | BT_SCO_CVSD_30 = 0, | |
62 | BT_SCO_CVSD_60, | |
63 | BT_SCO_CVSD_90, | |
64 | BT_SCO_CVSD_120, | |
65 | BT_SCO_CVSD_10, | |
66 | BT_SCO_CVSD_20, | |
67 | BT_SCO_CVSD_MAX, | |
68 | }; | |
69 | ||
70 | enum BT_SCO_BAND { | |
71 | BT_SCO_NB, | |
72 | BT_SCO_WB, | |
73 | }; | |
74 | ||
75 | struct mtk_btcvsd_snd_hw_info { | |
76 | unsigned int num_valid_addr; | |
77 | unsigned long bt_sram_addr[20]; | |
78 | unsigned int packet_length; | |
79 | unsigned int packet_num; | |
80 | }; | |
81 | ||
82 | struct mtk_btcvsd_snd_stream { | |
83 | struct snd_pcm_substream *substream; | |
84 | int stream; | |
85 | ||
86 | enum bt_sco_state state; | |
87 | ||
88 | unsigned int packet_size; | |
89 | unsigned int buf_size; | |
90 | u8 temp_packet_buf[SCO_PACKET_180]; | |
91 | ||
92 | int packet_w; | |
93 | int packet_r; | |
94 | snd_pcm_uframes_t prev_frame; | |
95 | int prev_packet_idx; | |
96 | ||
97 | unsigned int xrun:1; | |
98 | unsigned int timeout:1; | |
99 | unsigned int mute:1; | |
100 | unsigned int trigger_start:1; | |
101 | unsigned int wait_flag:1; | |
102 | unsigned int rw_cnt; | |
103 | ||
104 | unsigned long long time_stamp; | |
105 | unsigned long long buf_data_equivalent_time; | |
106 | ||
107 | struct mtk_btcvsd_snd_hw_info buffer_info; | |
108 | }; | |
109 | ||
110 | struct mtk_btcvsd_snd { | |
111 | struct device *dev; | |
112 | int irq_id; | |
113 | ||
114 | struct regmap *infra; | |
115 | void __iomem *bt_pkv_base; | |
116 | void __iomem *bt_sram_bank2_base; | |
117 | ||
118 | unsigned int infra_misc_offset; | |
119 | unsigned int conn_bt_cvsd_mask; | |
120 | unsigned int cvsd_mcu_read_offset; | |
121 | unsigned int cvsd_mcu_write_offset; | |
122 | unsigned int cvsd_packet_indicator; | |
123 | ||
124 | u32 *bt_reg_pkt_r; | |
125 | u32 *bt_reg_pkt_w; | |
126 | u32 *bt_reg_ctl; | |
127 | ||
128 | unsigned int irq_disabled:1; | |
129 | ||
130 | spinlock_t tx_lock; /* spinlock for bt tx stream control */ | |
131 | spinlock_t rx_lock; /* spinlock for bt rx stream control */ | |
132 | wait_queue_head_t tx_wait; | |
133 | wait_queue_head_t rx_wait; | |
134 | ||
135 | struct mtk_btcvsd_snd_stream *tx; | |
136 | struct mtk_btcvsd_snd_stream *rx; | |
137 | u8 tx_packet_buf[BTCVSD_TX_BUF_SIZE]; | |
138 | u8 rx_packet_buf[BTCVSD_RX_BUF_SIZE]; | |
139 | ||
140 | enum BT_SCO_BAND band; | |
141 | }; | |
142 | ||
143 | struct mtk_btcvsd_snd_time_buffer_info { | |
144 | unsigned long long data_count_equi_time; | |
145 | unsigned long long time_stamp_us; | |
146 | }; | |
147 | ||
148 | static const unsigned int btsco_packet_valid_mask[BT_SCO_CVSD_MAX][6] = { | |
149 | {0x1, 0x1 << 1, 0x1 << 2, 0x1 << 3, 0x1 << 4, 0x1 << 5}, | |
150 | {0x1, 0x1, 0x2, 0x2, 0x4, 0x4}, | |
151 | {0x1, 0x1, 0x1, 0x2, 0x2, 0x2}, | |
152 | {0x1, 0x1, 0x1, 0x1, 0x0, 0x0}, | |
153 | {0x7, 0x7 << 3, 0x7 << 6, 0x7 << 9, 0x7 << 12, 0x7 << 15}, | |
154 | {0x3, 0x3 << 1, 0x3 << 3, 0x3 << 4, 0x3 << 6, 0x3 << 7}, | |
155 | }; | |
156 | ||
157 | static const unsigned int btsco_packet_info[BT_SCO_CVSD_MAX][4] = { | |
158 | {30, 6, SCO_PACKET_180 / SCO_TX_ENCODE_SIZE, | |
159 | SCO_PACKET_180 / SCO_RX_PLC_SIZE}, | |
160 | {60, 3, SCO_PACKET_180 / SCO_TX_ENCODE_SIZE, | |
161 | SCO_PACKET_180 / SCO_RX_PLC_SIZE}, | |
162 | {90, 2, SCO_PACKET_180 / SCO_TX_ENCODE_SIZE, | |
163 | SCO_PACKET_180 / SCO_RX_PLC_SIZE}, | |
164 | {120, 1, SCO_PACKET_120 / SCO_TX_ENCODE_SIZE, | |
165 | SCO_PACKET_120 / SCO_RX_PLC_SIZE}, | |
166 | {10, 18, SCO_PACKET_180 / SCO_TX_ENCODE_SIZE, | |
167 | SCO_PACKET_180 / SCO_RX_PLC_SIZE}, | |
168 | {20, 9, SCO_PACKET_180 / SCO_TX_ENCODE_SIZE, | |
169 | SCO_PACKET_180 / SCO_RX_PLC_SIZE}, | |
170 | }; | |
171 | ||
172 | static const u8 table_msbc_silence[SCO_PACKET_180] = { | |
173 | 0x01, 0x38, 0xad, 0x00, 0x00, 0xc5, 0x00, 0x00, 0x00, 0x00, | |
174 | 0x77, 0x6d, 0xb6, 0xdd, 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6d, | |
175 | 0xdd, 0xb6, 0xdb, 0x77, 0x6d, 0xb6, 0xdd, 0xdb, 0x6d, 0xb7, | |
176 | 0x76, 0xdb, 0x6d, 0xdd, 0xb6, 0xdb, 0x77, 0x6d, 0xb6, 0xdd, | |
177 | 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6d, 0xdd, 0xb6, 0xdb, 0x77, | |
178 | 0x6d, 0xb6, 0xdd, 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6c, 0x00, | |
179 | 0x01, 0xc8, 0xad, 0x00, 0x00, 0xc5, 0x00, 0x00, 0x00, 0x00, | |
180 | 0x77, 0x6d, 0xb6, 0xdd, 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6d, | |
181 | 0xdd, 0xb6, 0xdb, 0x77, 0x6d, 0xb6, 0xdd, 0xdb, 0x6d, 0xb7, | |
182 | 0x76, 0xdb, 0x6d, 0xdd, 0xb6, 0xdb, 0x77, 0x6d, 0xb6, 0xdd, | |
183 | 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6d, 0xdd, 0xb6, 0xdb, 0x77, | |
184 | 0x6d, 0xb6, 0xdd, 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6c, 0x00, | |
185 | 0x01, 0xf8, 0xad, 0x00, 0x00, 0xc5, 0x00, 0x00, 0x00, 0x00, | |
186 | 0x77, 0x6d, 0xb6, 0xdd, 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6d, | |
187 | 0xdd, 0xb6, 0xdb, 0x77, 0x6d, 0xb6, 0xdd, 0xdb, 0x6d, 0xb7, | |
188 | 0x76, 0xdb, 0x6d, 0xdd, 0xb6, 0xdb, 0x77, 0x6d, 0xb6, 0xdd, | |
189 | 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6d, 0xdd, 0xb6, 0xdb, 0x77, | |
190 | 0x6d, 0xb6, 0xdd, 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6c, 0x00 | |
191 | }; | |
192 | ||
193 | static void mtk_btcvsd_snd_irq_enable(struct mtk_btcvsd_snd *bt) | |
194 | { | |
195 | regmap_update_bits(bt->infra, bt->infra_misc_offset, | |
196 | bt->conn_bt_cvsd_mask, bt->conn_bt_cvsd_mask); | |
197 | } | |
198 | ||
199 | static void mtk_btcvsd_snd_irq_disable(struct mtk_btcvsd_snd *bt) | |
200 | { | |
201 | regmap_update_bits(bt->infra, bt->infra_misc_offset, | |
202 | bt->conn_bt_cvsd_mask, 0); | |
203 | } | |
204 | ||
205 | static void mtk_btcvsd_snd_set_state(struct mtk_btcvsd_snd *bt, | |
206 | struct mtk_btcvsd_snd_stream *bt_stream, | |
207 | int state) | |
208 | { | |
209 | dev_dbg(bt->dev, "%s(), stream %d, state %d, tx->state %d, rx->state %d, irq_disabled %d\n", | |
210 | __func__, | |
211 | bt_stream->stream, state, | |
212 | bt->tx->state, bt->rx->state, bt->irq_disabled); | |
213 | ||
214 | bt_stream->state = state; | |
215 | ||
216 | if (bt->tx->state == BT_SCO_STATE_IDLE && | |
217 | bt->rx->state == BT_SCO_STATE_IDLE) { | |
218 | if (!bt->irq_disabled) { | |
219 | disable_irq(bt->irq_id); | |
220 | mtk_btcvsd_snd_irq_disable(bt); | |
221 | bt->irq_disabled = 1; | |
222 | } | |
223 | } else { | |
224 | if (bt->irq_disabled) { | |
225 | enable_irq(bt->irq_id); | |
226 | mtk_btcvsd_snd_irq_enable(bt); | |
227 | bt->irq_disabled = 0; | |
228 | } | |
229 | } | |
230 | } | |
231 | ||
232 | static int mtk_btcvsd_snd_tx_init(struct mtk_btcvsd_snd *bt) | |
233 | { | |
234 | memset(bt->tx, 0, sizeof(*bt->tx)); | |
235 | memset(bt->tx_packet_buf, 0, sizeof(bt->tx_packet_buf)); | |
236 | ||
237 | bt->tx->packet_size = BTCVSD_TX_PACKET_SIZE; | |
238 | bt->tx->buf_size = BTCVSD_TX_BUF_SIZE; | |
239 | bt->tx->timeout = 0; | |
240 | bt->tx->rw_cnt = 0; | |
241 | bt->tx->stream = SNDRV_PCM_STREAM_PLAYBACK; | |
242 | return 0; | |
243 | } | |
244 | ||
245 | static int mtk_btcvsd_snd_rx_init(struct mtk_btcvsd_snd *bt) | |
246 | { | |
247 | memset(bt->rx, 0, sizeof(*bt->rx)); | |
248 | memset(bt->rx_packet_buf, 0, sizeof(bt->rx_packet_buf)); | |
249 | ||
250 | bt->rx->packet_size = BTCVSD_RX_PACKET_SIZE; | |
251 | bt->rx->buf_size = BTCVSD_RX_BUF_SIZE; | |
252 | bt->rx->timeout = 0; | |
253 | bt->rx->rw_cnt = 0; | |
fc23af99 | 254 | bt->rx->stream = SNDRV_PCM_STREAM_CAPTURE; |
4bd8597d KC |
255 | return 0; |
256 | } | |
257 | ||
258 | static void get_tx_time_stamp(struct mtk_btcvsd_snd *bt, | |
259 | struct mtk_btcvsd_snd_time_buffer_info *ts) | |
260 | { | |
261 | ts->time_stamp_us = bt->tx->time_stamp; | |
262 | ts->data_count_equi_time = bt->tx->buf_data_equivalent_time; | |
263 | } | |
264 | ||
265 | static void get_rx_time_stamp(struct mtk_btcvsd_snd *bt, | |
266 | struct mtk_btcvsd_snd_time_buffer_info *ts) | |
267 | { | |
268 | ts->time_stamp_us = bt->rx->time_stamp; | |
269 | ts->data_count_equi_time = bt->rx->buf_data_equivalent_time; | |
270 | } | |
271 | ||
272 | static int btcvsd_bytes_to_frame(struct snd_pcm_substream *substream, | |
273 | int bytes) | |
274 | { | |
275 | int count = bytes; | |
276 | struct snd_pcm_runtime *runtime = substream->runtime; | |
277 | ||
278 | if (runtime->format == SNDRV_PCM_FORMAT_S32_LE || | |
279 | runtime->format == SNDRV_PCM_FORMAT_U32_LE) | |
280 | count = count >> 2; | |
281 | else | |
282 | count = count >> 1; | |
283 | ||
284 | count = count / runtime->channels; | |
285 | return count; | |
286 | } | |
287 | ||
288 | static void mtk_btcvsd_snd_data_transfer(enum bt_sco_direct dir, | |
289 | u8 *src, u8 *dst, | |
290 | unsigned int blk_size, | |
291 | unsigned int blk_num) | |
292 | { | |
293 | unsigned int i, j; | |
294 | ||
295 | if (blk_size == 60 || blk_size == 120 || blk_size == 20) { | |
296 | u32 *src_32 = (u32 *)src; | |
297 | u32 *dst_32 = (u32 *)dst; | |
298 | ||
299 | for (i = 0; i < (blk_size * blk_num / 4); i++) | |
300 | *dst_32++ = *src_32++; | |
301 | } else { | |
302 | u16 *src_16 = (u16 *)src; | |
303 | u16 *dst_16 = (u16 *)dst; | |
304 | ||
305 | for (j = 0; j < blk_num; j++) { | |
306 | for (i = 0; i < (blk_size / 2); i++) | |
307 | *dst_16++ = *src_16++; | |
308 | ||
309 | if (dir == BT_SCO_DIRECT_BT2ARM) | |
310 | src_16++; | |
311 | else | |
312 | dst_16++; | |
313 | } | |
314 | } | |
315 | } | |
316 | ||
317 | /* write encoded mute data to bt sram */ | |
318 | static int btcvsd_tx_clean_buffer(struct mtk_btcvsd_snd *bt) | |
319 | { | |
320 | unsigned int i; | |
321 | unsigned int num_valid_addr; | |
322 | unsigned long flags; | |
323 | enum BT_SCO_BAND band = bt->band; | |
324 | ||
325 | /* prepare encoded mute data */ | |
326 | if (band == BT_SCO_NB) | |
327 | memset(bt->tx->temp_packet_buf, 170, SCO_PACKET_180); | |
328 | else | |
329 | memcpy(bt->tx->temp_packet_buf, | |
330 | table_msbc_silence, SCO_PACKET_180); | |
331 | ||
332 | /* write mute data to bt tx sram buffer */ | |
333 | spin_lock_irqsave(&bt->tx_lock, flags); | |
334 | num_valid_addr = bt->tx->buffer_info.num_valid_addr; | |
335 | ||
336 | dev_info(bt->dev, "%s(), band %d, num_valid_addr %u\n", | |
337 | __func__, band, num_valid_addr); | |
338 | ||
339 | for (i = 0; i < num_valid_addr; i++) { | |
340 | void *dst; | |
341 | ||
342 | dev_info(bt->dev, "%s(), clean addr 0x%lx\n", __func__, | |
343 | bt->tx->buffer_info.bt_sram_addr[i]); | |
344 | ||
345 | dst = (void *)bt->tx->buffer_info.bt_sram_addr[i]; | |
346 | ||
347 | mtk_btcvsd_snd_data_transfer(BT_SCO_DIRECT_ARM2BT, | |
348 | bt->tx->temp_packet_buf, dst, | |
349 | bt->tx->buffer_info.packet_length, | |
350 | bt->tx->buffer_info.packet_num); | |
351 | } | |
352 | spin_unlock_irqrestore(&bt->tx_lock, flags); | |
353 | ||
354 | return 0; | |
355 | } | |
356 | ||
357 | static int mtk_btcvsd_read_from_bt(struct mtk_btcvsd_snd *bt, | |
358 | enum bt_sco_packet_len packet_type, | |
359 | unsigned int packet_length, | |
360 | unsigned int packet_num, | |
361 | unsigned int blk_size, | |
362 | unsigned int control) | |
363 | { | |
364 | unsigned int i; | |
365 | int pv; | |
366 | u8 *src; | |
367 | unsigned int packet_buf_ofs; | |
368 | unsigned long flags; | |
369 | unsigned long connsys_addr_rx, ap_addr_rx; | |
370 | ||
371 | connsys_addr_rx = *bt->bt_reg_pkt_r; | |
372 | ap_addr_rx = (unsigned long)bt->bt_sram_bank2_base + | |
373 | (connsys_addr_rx & 0xFFFF); | |
374 | ||
375 | if (connsys_addr_rx == 0xdeadfeed) { | |
376 | /* bt return 0xdeadfeed if read register during bt sleep */ | |
377 | dev_warn(bt->dev, "%s(), connsys_addr_rx == 0xdeadfeed", | |
378 | __func__); | |
379 | return -EIO; | |
380 | } | |
381 | ||
382 | src = (u8 *)ap_addr_rx; | |
383 | ||
384 | mtk_btcvsd_snd_data_transfer(BT_SCO_DIRECT_BT2ARM, src, | |
385 | bt->rx->temp_packet_buf, packet_length, | |
386 | packet_num); | |
387 | ||
388 | spin_lock_irqsave(&bt->rx_lock, flags); | |
389 | for (i = 0; i < blk_size; i++) { | |
390 | packet_buf_ofs = (bt->rx->packet_w & SCO_RX_PACKET_MASK) * | |
391 | bt->rx->packet_size; | |
392 | memcpy(bt->rx_packet_buf + packet_buf_ofs, | |
393 | bt->rx->temp_packet_buf + (SCO_RX_PLC_SIZE * i), | |
394 | SCO_RX_PLC_SIZE); | |
395 | if ((control & btsco_packet_valid_mask[packet_type][i]) == | |
396 | btsco_packet_valid_mask[packet_type][i]) | |
397 | pv = 1; | |
398 | else | |
399 | pv = 0; | |
400 | ||
401 | packet_buf_ofs += SCO_RX_PLC_SIZE; | |
402 | memcpy(bt->rx_packet_buf + packet_buf_ofs, (void *)&pv, | |
403 | SCO_CVSD_PACKET_VALID_SIZE); | |
404 | bt->rx->packet_w++; | |
405 | } | |
406 | spin_unlock_irqrestore(&bt->rx_lock, flags); | |
407 | return 0; | |
408 | } | |
409 | ||
410 | int mtk_btcvsd_write_to_bt(struct mtk_btcvsd_snd *bt, | |
411 | enum bt_sco_packet_len packet_type, | |
412 | unsigned int packet_length, | |
413 | unsigned int packet_num, | |
414 | unsigned int blk_size) | |
415 | { | |
416 | unsigned int i; | |
417 | unsigned long flags; | |
418 | u8 *dst; | |
419 | unsigned long connsys_addr_tx, ap_addr_tx; | |
420 | bool new_ap_addr_tx = true; | |
421 | ||
422 | connsys_addr_tx = *bt->bt_reg_pkt_w; | |
423 | ap_addr_tx = (unsigned long)bt->bt_sram_bank2_base + | |
424 | (connsys_addr_tx & 0xFFFF); | |
425 | ||
426 | if (connsys_addr_tx == 0xdeadfeed) { | |
427 | /* bt return 0xdeadfeed if read register during bt sleep */ | |
428 | dev_warn(bt->dev, "%s(), connsys_addr_tx == 0xdeadfeed\n", | |
429 | __func__); | |
430 | return -EIO; | |
431 | } | |
432 | ||
433 | spin_lock_irqsave(&bt->tx_lock, flags); | |
434 | for (i = 0; i < blk_size; i++) { | |
435 | memcpy(bt->tx->temp_packet_buf + (bt->tx->packet_size * i), | |
436 | (bt->tx_packet_buf + | |
437 | (bt->tx->packet_r % SCO_TX_PACKER_BUF_NUM) * | |
438 | bt->tx->packet_size), | |
439 | bt->tx->packet_size); | |
440 | ||
441 | bt->tx->packet_r++; | |
442 | } | |
443 | spin_unlock_irqrestore(&bt->tx_lock, flags); | |
444 | ||
445 | dst = (u8 *)ap_addr_tx; | |
446 | ||
447 | if (!bt->tx->mute) { | |
448 | mtk_btcvsd_snd_data_transfer(BT_SCO_DIRECT_ARM2BT, | |
449 | bt->tx->temp_packet_buf, dst, | |
450 | packet_length, packet_num); | |
451 | } | |
452 | ||
453 | /* store bt tx buffer sram info */ | |
454 | bt->tx->buffer_info.packet_length = packet_length; | |
455 | bt->tx->buffer_info.packet_num = packet_num; | |
456 | for (i = 0; i < bt->tx->buffer_info.num_valid_addr; i++) { | |
457 | if (bt->tx->buffer_info.bt_sram_addr[i] == ap_addr_tx) { | |
458 | new_ap_addr_tx = false; | |
459 | break; | |
460 | } | |
461 | } | |
462 | if (new_ap_addr_tx) { | |
463 | unsigned int next_idx; | |
464 | ||
465 | spin_lock_irqsave(&bt->tx_lock, flags); | |
466 | bt->tx->buffer_info.num_valid_addr++; | |
467 | next_idx = bt->tx->buffer_info.num_valid_addr - 1; | |
468 | bt->tx->buffer_info.bt_sram_addr[next_idx] = ap_addr_tx; | |
469 | spin_unlock_irqrestore(&bt->tx_lock, flags); | |
470 | dev_info(bt->dev, "%s(), new ap_addr_tx = 0x%lx, num_valid_addr %d\n", | |
471 | __func__, ap_addr_tx, | |
472 | bt->tx->buffer_info.num_valid_addr); | |
473 | } | |
474 | ||
475 | if (bt->tx->mute) | |
476 | btcvsd_tx_clean_buffer(bt); | |
477 | ||
478 | return 0; | |
479 | } | |
480 | ||
481 | static irqreturn_t mtk_btcvsd_snd_irq_handler(int irq_id, void *dev) | |
482 | { | |
483 | struct mtk_btcvsd_snd *bt = dev; | |
484 | unsigned int packet_type, packet_num, packet_length; | |
485 | unsigned int buf_cnt_tx, buf_cnt_rx, control; | |
486 | ||
487 | if (bt->rx->state != BT_SCO_STATE_RUNNING && | |
488 | bt->rx->state != BT_SCO_STATE_ENDING && | |
489 | bt->tx->state != BT_SCO_STATE_RUNNING && | |
f060f46f KC |
490 | bt->tx->state != BT_SCO_STATE_ENDING && |
491 | bt->tx->state != BT_SCO_STATE_LOOPBACK) { | |
4bd8597d KC |
492 | dev_warn(bt->dev, "%s(), in idle state: rx->state: %d, tx->state: %d\n", |
493 | __func__, bt->rx->state, bt->tx->state); | |
494 | goto irq_handler_exit; | |
495 | } | |
496 | ||
497 | control = *bt->bt_reg_ctl; | |
498 | packet_type = (control >> 18) & 0x7; | |
499 | ||
500 | if (((control >> 31) & 1) == 0) { | |
501 | dev_warn(bt->dev, "%s(), ((control >> 31) & 1) == 0, control 0x%x\n", | |
502 | __func__, control); | |
503 | goto irq_handler_exit; | |
504 | } | |
505 | ||
506 | if (packet_type >= BT_SCO_CVSD_MAX) { | |
507 | dev_warn(bt->dev, "%s(), invalid packet_type %u, exit\n", | |
508 | __func__, packet_type); | |
509 | goto irq_handler_exit; | |
510 | } | |
511 | ||
512 | packet_length = btsco_packet_info[packet_type][0]; | |
513 | packet_num = btsco_packet_info[packet_type][1]; | |
514 | buf_cnt_tx = btsco_packet_info[packet_type][2]; | |
515 | buf_cnt_rx = btsco_packet_info[packet_type][3]; | |
516 | ||
f060f46f KC |
517 | if (bt->tx->state == BT_SCO_STATE_LOOPBACK) { |
518 | u8 *src, *dst; | |
519 | unsigned long connsys_addr_rx, ap_addr_rx; | |
520 | unsigned long connsys_addr_tx, ap_addr_tx; | |
521 | ||
522 | connsys_addr_rx = *bt->bt_reg_pkt_r; | |
523 | ap_addr_rx = (unsigned long)bt->bt_sram_bank2_base + | |
524 | (connsys_addr_rx & 0xFFFF); | |
525 | ||
526 | connsys_addr_tx = *bt->bt_reg_pkt_w; | |
527 | ap_addr_tx = (unsigned long)bt->bt_sram_bank2_base + | |
528 | (connsys_addr_tx & 0xFFFF); | |
529 | ||
530 | if (connsys_addr_tx == 0xdeadfeed || | |
531 | connsys_addr_rx == 0xdeadfeed) { | |
532 | /* bt return 0xdeadfeed if read reg during bt sleep */ | |
533 | dev_warn(bt->dev, "%s(), connsys_addr_tx == 0xdeadfeed\n", | |
534 | __func__); | |
535 | goto irq_handler_exit; | |
536 | } | |
537 | ||
538 | src = (u8 *)ap_addr_rx; | |
539 | dst = (u8 *)ap_addr_tx; | |
540 | ||
541 | mtk_btcvsd_snd_data_transfer(BT_SCO_DIRECT_BT2ARM, src, | |
542 | bt->tx->temp_packet_buf, | |
543 | packet_length, | |
544 | packet_num); | |
545 | mtk_btcvsd_snd_data_transfer(BT_SCO_DIRECT_ARM2BT, | |
546 | bt->tx->temp_packet_buf, dst, | |
547 | packet_length, | |
548 | packet_num); | |
549 | bt->rx->rw_cnt++; | |
550 | bt->tx->rw_cnt++; | |
551 | } | |
552 | ||
4bd8597d KC |
553 | if (bt->rx->state == BT_SCO_STATE_RUNNING || |
554 | bt->rx->state == BT_SCO_STATE_ENDING) { | |
555 | if (bt->rx->xrun) { | |
556 | if (bt->rx->packet_w - bt->rx->packet_r <= | |
557 | SCO_RX_PACKER_BUF_NUM - 2 * buf_cnt_rx) { | |
558 | /* | |
559 | * free space is larger then | |
560 | * twice interrupt rx data size | |
561 | */ | |
562 | bt->rx->xrun = 0; | |
563 | dev_warn(bt->dev, "%s(), rx->xrun 0!\n", | |
564 | __func__); | |
565 | } | |
566 | } | |
567 | ||
568 | if (!bt->rx->xrun && | |
569 | (bt->rx->packet_w - bt->rx->packet_r <= | |
570 | SCO_RX_PACKER_BUF_NUM - buf_cnt_rx)) { | |
571 | mtk_btcvsd_read_from_bt(bt, | |
572 | packet_type, | |
573 | packet_length, | |
574 | packet_num, | |
575 | buf_cnt_rx, | |
576 | control); | |
577 | bt->rx->rw_cnt++; | |
578 | } else { | |
579 | bt->rx->xrun = 1; | |
580 | dev_warn(bt->dev, "%s(), rx->xrun 1\n", __func__); | |
581 | } | |
582 | } | |
583 | ||
584 | /* tx */ | |
585 | bt->tx->timeout = 0; | |
586 | if ((bt->tx->state == BT_SCO_STATE_RUNNING || | |
587 | bt->tx->state == BT_SCO_STATE_ENDING) && | |
588 | bt->tx->trigger_start) { | |
589 | if (bt->tx->xrun) { | |
590 | /* prepared data is larger then twice | |
591 | * interrupt tx data size | |
592 | */ | |
593 | if (bt->tx->packet_w - bt->tx->packet_r >= | |
594 | 2 * buf_cnt_tx) { | |
595 | bt->tx->xrun = 0; | |
596 | dev_warn(bt->dev, "%s(), tx->xrun 0\n", | |
597 | __func__); | |
598 | } | |
599 | } | |
600 | ||
601 | if ((!bt->tx->xrun && | |
602 | (bt->tx->packet_w - bt->tx->packet_r >= buf_cnt_tx)) || | |
603 | bt->tx->state == BT_SCO_STATE_ENDING) { | |
604 | mtk_btcvsd_write_to_bt(bt, | |
605 | packet_type, | |
606 | packet_length, | |
607 | packet_num, | |
608 | buf_cnt_tx); | |
609 | bt->tx->rw_cnt++; | |
610 | } else { | |
611 | bt->tx->xrun = 1; | |
612 | dev_warn(bt->dev, "%s(), tx->xrun 1\n", __func__); | |
613 | } | |
614 | } | |
615 | ||
616 | *bt->bt_reg_ctl &= ~BT_CVSD_CLEAR; | |
617 | ||
618 | if (bt->rx->state == BT_SCO_STATE_RUNNING || | |
619 | bt->rx->state == BT_SCO_STATE_ENDING) { | |
620 | bt->rx->wait_flag = 1; | |
621 | wake_up_interruptible(&bt->rx_wait); | |
622 | snd_pcm_period_elapsed(bt->rx->substream); | |
623 | } | |
624 | if (bt->tx->state == BT_SCO_STATE_RUNNING || | |
625 | bt->tx->state == BT_SCO_STATE_ENDING) { | |
626 | bt->tx->wait_flag = 1; | |
627 | wake_up_interruptible(&bt->tx_wait); | |
628 | snd_pcm_period_elapsed(bt->tx->substream); | |
629 | } | |
630 | ||
631 | return IRQ_HANDLED; | |
632 | irq_handler_exit: | |
633 | *bt->bt_reg_ctl &= ~BT_CVSD_CLEAR; | |
634 | return IRQ_HANDLED; | |
635 | } | |
636 | ||
637 | static int wait_for_bt_irq(struct mtk_btcvsd_snd *bt, | |
638 | struct mtk_btcvsd_snd_stream *bt_stream) | |
639 | { | |
640 | unsigned long long t1, t2; | |
641 | /* one interrupt period = 22.5ms */ | |
642 | unsigned long long timeout_limit = 22500000; | |
643 | int max_timeout_trial = 2; | |
644 | int ret; | |
645 | ||
646 | bt_stream->wait_flag = 0; | |
647 | ||
648 | while (max_timeout_trial && !bt_stream->wait_flag) { | |
649 | t1 = sched_clock(); | |
650 | if (bt_stream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | |
651 | ret = wait_event_interruptible_timeout(bt->tx_wait, | |
652 | bt_stream->wait_flag, | |
653 | nsecs_to_jiffies(timeout_limit)); | |
654 | } else { | |
655 | ret = wait_event_interruptible_timeout(bt->rx_wait, | |
656 | bt_stream->wait_flag, | |
657 | nsecs_to_jiffies(timeout_limit)); | |
658 | } | |
659 | ||
660 | t2 = sched_clock(); | |
661 | t2 = t2 - t1; /* in ns (10^9) */ | |
662 | ||
663 | if (t2 > timeout_limit) { | |
664 | dev_warn(bt->dev, "%s(), stream %d, timeout %llu, limit %llu, ret %d, flag %d\n", | |
665 | __func__, bt_stream->stream, | |
666 | t2, timeout_limit, ret, | |
667 | bt_stream->wait_flag); | |
668 | } | |
669 | ||
670 | if (ret < 0) { | |
671 | /* | |
672 | * error, -ERESTARTSYS if it was interrupted by | |
673 | * a signal | |
674 | */ | |
675 | dev_warn(bt->dev, "%s(), stream %d, error, trial left %d\n", | |
676 | __func__, | |
677 | bt_stream->stream, max_timeout_trial); | |
678 | ||
679 | bt_stream->timeout = 1; | |
680 | return ret; | |
681 | } else if (ret == 0) { | |
682 | /* conidtion is false after timeout */ | |
683 | max_timeout_trial--; | |
684 | dev_warn(bt->dev, "%s(), stream %d, error, timeout, condition is false, trial left %d\n", | |
685 | __func__, | |
686 | bt_stream->stream, max_timeout_trial); | |
687 | ||
688 | if (max_timeout_trial <= 0) { | |
689 | bt_stream->timeout = 1; | |
690 | return -ETIME; | |
691 | } | |
692 | } | |
693 | } | |
694 | ||
695 | return 0; | |
696 | } | |
697 | ||
698 | ssize_t mtk_btcvsd_snd_read(struct mtk_btcvsd_snd *bt, | |
699 | char __user *buf, | |
700 | size_t count) | |
701 | { | |
702 | ssize_t read_size = 0, read_count = 0, cur_read_idx, cont; | |
703 | unsigned int cur_buf_ofs = 0; | |
704 | unsigned long avail; | |
705 | unsigned long flags; | |
706 | unsigned int packet_size = bt->rx->packet_size; | |
707 | ||
708 | while (count) { | |
709 | spin_lock_irqsave(&bt->rx_lock, flags); | |
710 | /* available data in RX packet buffer */ | |
711 | avail = (bt->rx->packet_w - bt->rx->packet_r) * packet_size; | |
712 | ||
713 | cur_read_idx = (bt->rx->packet_r & SCO_RX_PACKET_MASK) * | |
714 | packet_size; | |
715 | spin_unlock_irqrestore(&bt->rx_lock, flags); | |
716 | ||
717 | if (!avail) { | |
718 | int ret = wait_for_bt_irq(bt, bt->rx); | |
719 | ||
720 | if (ret) | |
721 | return read_count; | |
722 | ||
723 | continue; | |
724 | } | |
725 | ||
726 | /* count must be multiple of packet_size */ | |
727 | if (count % packet_size != 0 || | |
728 | avail % packet_size != 0) { | |
729 | dev_warn(bt->dev, "%s(), count %zu or d %lu is not multiple of packet_size %dd\n", | |
730 | __func__, count, avail, packet_size); | |
731 | ||
732 | count -= count % packet_size; | |
733 | avail -= avail % packet_size; | |
734 | } | |
735 | ||
736 | if (count > avail) | |
737 | read_size = avail; | |
738 | else | |
739 | read_size = count; | |
740 | ||
741 | /* calculate continue space */ | |
742 | cont = bt->rx->buf_size - cur_read_idx; | |
743 | if (read_size > cont) | |
744 | read_size = cont; | |
745 | ||
746 | if (copy_to_user(buf + cur_buf_ofs, | |
747 | bt->rx_packet_buf + cur_read_idx, | |
748 | read_size)) { | |
749 | dev_warn(bt->dev, "%s(), copy_to_user fail\n", | |
750 | __func__); | |
751 | return -EFAULT; | |
752 | } | |
753 | ||
754 | spin_lock_irqsave(&bt->rx_lock, flags); | |
755 | bt->rx->packet_r += read_size / packet_size; | |
756 | spin_unlock_irqrestore(&bt->rx_lock, flags); | |
757 | ||
758 | read_count += read_size; | |
759 | cur_buf_ofs += read_size; | |
760 | count -= read_size; | |
761 | } | |
762 | ||
763 | /* | |
764 | * save current timestamp & buffer time in times_tamp and | |
765 | * buf_data_equivalent_time | |
766 | */ | |
767 | bt->rx->time_stamp = sched_clock(); | |
768 | bt->rx->buf_data_equivalent_time = | |
769 | (unsigned long long)(bt->rx->packet_w - bt->rx->packet_r) * | |
770 | SCO_RX_PLC_SIZE * 16 * 1000 / 2 / 64; | |
771 | bt->rx->buf_data_equivalent_time += read_count * SCO_RX_PLC_SIZE * | |
772 | 16 * 1000 / packet_size / 2 / 64; | |
773 | /* return equivalent time(us) to data count */ | |
774 | bt->rx->buf_data_equivalent_time *= 1000; | |
775 | ||
776 | return read_count; | |
777 | } | |
778 | ||
779 | ssize_t mtk_btcvsd_snd_write(struct mtk_btcvsd_snd *bt, | |
780 | char __user *buf, | |
781 | size_t count) | |
782 | { | |
783 | int written_size = count, avail = 0, cur_write_idx, write_size, cont; | |
784 | unsigned int cur_buf_ofs = 0; | |
785 | unsigned long flags; | |
786 | unsigned int packet_size = bt->tx->packet_size; | |
787 | ||
788 | /* | |
789 | * save current timestamp & buffer time in time_stamp and | |
790 | * buf_data_equivalent_time | |
791 | */ | |
792 | bt->tx->time_stamp = sched_clock(); | |
793 | bt->tx->buf_data_equivalent_time = | |
794 | (unsigned long long)(bt->tx->packet_w - bt->tx->packet_r) * | |
795 | packet_size * 16 * 1000 / 2 / 64; | |
796 | ||
797 | /* return equivalent time(us) to data count */ | |
798 | bt->tx->buf_data_equivalent_time *= 1000; | |
799 | ||
800 | while (count) { | |
801 | spin_lock_irqsave(&bt->tx_lock, flags); | |
802 | /* free space of TX packet buffer */ | |
803 | avail = bt->tx->buf_size - | |
804 | (bt->tx->packet_w - bt->tx->packet_r) * packet_size; | |
805 | ||
806 | cur_write_idx = (bt->tx->packet_w % SCO_TX_PACKER_BUF_NUM) * | |
807 | packet_size; | |
808 | spin_unlock_irqrestore(&bt->tx_lock, flags); | |
809 | ||
810 | if (!avail) { | |
811 | int ret = wait_for_bt_irq(bt, bt->rx); | |
812 | ||
813 | if (ret) | |
814 | return written_size; | |
815 | ||
816 | continue; | |
817 | } | |
818 | ||
819 | /* count must be multiple of bt->tx->packet_size */ | |
820 | if (count % packet_size != 0 || | |
821 | avail % packet_size != 0) { | |
822 | dev_warn(bt->dev, "%s(), count %zu or avail %d is not multiple of packet_size %d\n", | |
823 | __func__, count, avail, packet_size); | |
824 | count -= count % packet_size; | |
825 | avail -= avail % packet_size; | |
826 | } | |
827 | ||
828 | if (count > avail) | |
829 | write_size = avail; | |
830 | else | |
831 | write_size = count; | |
832 | ||
833 | /* calculate continue space */ | |
834 | cont = bt->tx->buf_size - cur_write_idx; | |
835 | if (write_size > cont) | |
836 | write_size = cont; | |
837 | ||
838 | if (copy_from_user(bt->tx_packet_buf + | |
839 | cur_write_idx, | |
840 | buf + cur_buf_ofs, | |
841 | write_size)) { | |
842 | dev_warn(bt->dev, "%s(), copy_from_user fail\n", | |
843 | __func__); | |
844 | return -EFAULT; | |
845 | } | |
846 | ||
847 | spin_lock_irqsave(&bt->tx_lock, flags); | |
848 | bt->tx->packet_w += write_size / packet_size; | |
849 | spin_unlock_irqrestore(&bt->tx_lock, flags); | |
850 | cur_buf_ofs += write_size; | |
851 | count -= write_size; | |
852 | } | |
853 | ||
854 | return written_size; | |
855 | } | |
856 | ||
857 | static struct mtk_btcvsd_snd_stream *get_bt_stream | |
858 | (struct mtk_btcvsd_snd *bt, struct snd_pcm_substream *substream) | |
859 | { | |
860 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | |
861 | return bt->tx; | |
862 | else | |
863 | return bt->rx; | |
864 | } | |
865 | ||
866 | /* pcm ops */ | |
867 | static const struct snd_pcm_hardware mtk_btcvsd_hardware = { | |
868 | .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | | |
869 | SNDRV_PCM_INFO_RESUME), | |
870 | .formats = SNDRV_PCM_FMTBIT_S16_LE, | |
871 | .buffer_bytes_max = 24 * 1024, | |
872 | .period_bytes_max = 24 * 1024, | |
873 | .periods_min = 2, | |
874 | .periods_max = 16, | |
875 | .fifo_size = 0, | |
876 | }; | |
877 | ||
878 | static int mtk_pcm_btcvsd_open(struct snd_pcm_substream *substream) | |
879 | { | |
880 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | |
881 | struct snd_soc_component *component = | |
882 | snd_soc_rtdcom_lookup(rtd, BTCVSD_SND_NAME); | |
883 | struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(component); | |
884 | int ret; | |
885 | ||
886 | dev_dbg(bt->dev, "%s(), stream %d, substream %p\n", | |
887 | __func__, substream->stream, substream); | |
888 | ||
889 | snd_soc_set_runtime_hwparams(substream, &mtk_btcvsd_hardware); | |
890 | ||
891 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | |
892 | ret = mtk_btcvsd_snd_tx_init(bt); | |
893 | bt->tx->substream = substream; | |
894 | } else { | |
895 | ret = mtk_btcvsd_snd_rx_init(bt); | |
896 | bt->rx->substream = substream; | |
897 | } | |
898 | ||
899 | return ret; | |
900 | } | |
901 | ||
902 | static int mtk_pcm_btcvsd_close(struct snd_pcm_substream *substream) | |
903 | { | |
904 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | |
905 | struct snd_soc_component *component = | |
906 | snd_soc_rtdcom_lookup(rtd, BTCVSD_SND_NAME); | |
907 | struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(component); | |
908 | struct mtk_btcvsd_snd_stream *bt_stream = get_bt_stream(bt, substream); | |
909 | ||
910 | dev_dbg(bt->dev, "%s(), stream %d\n", __func__, substream->stream); | |
911 | ||
912 | mtk_btcvsd_snd_set_state(bt, bt_stream, BT_SCO_STATE_IDLE); | |
913 | bt_stream->substream = NULL; | |
914 | return 0; | |
915 | } | |
916 | ||
917 | static int mtk_pcm_btcvsd_hw_params(struct snd_pcm_substream *substream, | |
918 | struct snd_pcm_hw_params *hw_params) | |
919 | { | |
920 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | |
921 | struct snd_soc_component *component = | |
922 | snd_soc_rtdcom_lookup(rtd, BTCVSD_SND_NAME); | |
923 | struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(component); | |
924 | ||
925 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && | |
926 | params_buffer_bytes(hw_params) % bt->tx->packet_size != 0) { | |
927 | dev_warn(bt->dev, "%s(), error, buffer size %d not valid\n", | |
928 | __func__, | |
929 | params_buffer_bytes(hw_params)); | |
930 | return -EINVAL; | |
931 | } | |
932 | ||
933 | substream->runtime->dma_bytes = params_buffer_bytes(hw_params); | |
934 | return 0; | |
935 | } | |
936 | ||
937 | static int mtk_pcm_btcvsd_hw_free(struct snd_pcm_substream *substream) | |
938 | { | |
939 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | |
940 | struct snd_soc_component *component = | |
941 | snd_soc_rtdcom_lookup(rtd, BTCVSD_SND_NAME); | |
942 | struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(component); | |
943 | ||
944 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | |
945 | btcvsd_tx_clean_buffer(bt); | |
946 | ||
947 | return 0; | |
948 | } | |
949 | ||
950 | static int mtk_pcm_btcvsd_prepare(struct snd_pcm_substream *substream) | |
951 | { | |
952 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | |
953 | struct snd_soc_component *component = | |
954 | snd_soc_rtdcom_lookup(rtd, BTCVSD_SND_NAME); | |
955 | struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(component); | |
956 | struct mtk_btcvsd_snd_stream *bt_stream = get_bt_stream(bt, substream); | |
957 | ||
958 | dev_dbg(bt->dev, "%s(), stream %d\n", __func__, substream->stream); | |
959 | ||
960 | mtk_btcvsd_snd_set_state(bt, bt_stream, BT_SCO_STATE_RUNNING); | |
961 | return 0; | |
962 | } | |
963 | ||
964 | static int mtk_pcm_btcvsd_trigger(struct snd_pcm_substream *substream, int cmd) | |
965 | { | |
966 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | |
967 | struct snd_soc_component *component = | |
968 | snd_soc_rtdcom_lookup(rtd, BTCVSD_SND_NAME); | |
969 | struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(component); | |
970 | struct mtk_btcvsd_snd_stream *bt_stream = get_bt_stream(bt, substream); | |
971 | int stream = substream->stream; | |
972 | int hw_packet_ptr; | |
973 | ||
974 | dev_dbg(bt->dev, "%s(), stream %d, cmd %d\n", | |
975 | __func__, substream->stream, cmd); | |
976 | ||
977 | switch (cmd) { | |
978 | case SNDRV_PCM_TRIGGER_START: | |
979 | case SNDRV_PCM_TRIGGER_RESUME: | |
980 | hw_packet_ptr = stream == SNDRV_PCM_STREAM_PLAYBACK ? | |
981 | bt_stream->packet_r : bt_stream->packet_w; | |
982 | bt_stream->prev_packet_idx = hw_packet_ptr; | |
983 | bt_stream->prev_frame = 0; | |
984 | bt_stream->trigger_start = 1; | |
985 | return 0; | |
986 | case SNDRV_PCM_TRIGGER_STOP: | |
987 | case SNDRV_PCM_TRIGGER_SUSPEND: | |
988 | bt_stream->trigger_start = 0; | |
989 | mtk_btcvsd_snd_set_state(bt, bt_stream, BT_SCO_STATE_ENDING); | |
990 | return 0; | |
991 | default: | |
992 | return -EINVAL; | |
993 | } | |
994 | } | |
995 | ||
996 | static snd_pcm_uframes_t mtk_pcm_btcvsd_pointer | |
997 | (struct snd_pcm_substream *substream) | |
998 | { | |
999 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | |
1000 | struct snd_soc_component *component = | |
1001 | snd_soc_rtdcom_lookup(rtd, BTCVSD_SND_NAME); | |
1002 | struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(component); | |
1003 | struct mtk_btcvsd_snd_stream *bt_stream; | |
1004 | snd_pcm_uframes_t frame = 0; | |
1005 | int byte = 0; | |
1006 | int hw_packet_ptr; | |
1007 | int packet_diff; | |
1008 | spinlock_t *lock; /* spinlock for bt stream control */ | |
1009 | unsigned long flags; | |
1010 | ||
1011 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | |
1012 | lock = &bt->tx_lock; | |
1013 | bt_stream = bt->tx; | |
1014 | } else { | |
1015 | lock = &bt->rx_lock; | |
1016 | bt_stream = bt->rx; | |
1017 | } | |
1018 | ||
1019 | spin_lock_irqsave(lock, flags); | |
1020 | hw_packet_ptr = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? | |
1021 | bt->tx->packet_r : bt->rx->packet_w; | |
1022 | ||
1023 | /* get packet diff from last time */ | |
1024 | if (hw_packet_ptr >= bt_stream->prev_packet_idx) { | |
1025 | packet_diff = hw_packet_ptr - bt_stream->prev_packet_idx; | |
1026 | } else { | |
1027 | /* integer overflow */ | |
1028 | packet_diff = (INT_MAX - bt_stream->prev_packet_idx) + | |
1029 | (hw_packet_ptr - INT_MIN) + 1; | |
1030 | } | |
1031 | bt_stream->prev_packet_idx = hw_packet_ptr; | |
1032 | ||
1033 | /* increased bytes */ | |
1034 | byte = packet_diff * bt_stream->packet_size; | |
1035 | ||
1036 | frame = btcvsd_bytes_to_frame(substream, byte); | |
1037 | frame += bt_stream->prev_frame; | |
1038 | frame %= substream->runtime->buffer_size; | |
1039 | ||
1040 | bt_stream->prev_frame = frame; | |
1041 | ||
1042 | spin_unlock_irqrestore(lock, flags); | |
1043 | ||
1044 | return frame; | |
1045 | } | |
1046 | ||
1047 | static int mtk_pcm_btcvsd_copy(struct snd_pcm_substream *substream, | |
1048 | int channel, unsigned long pos, | |
1049 | void __user *buf, unsigned long count) | |
1050 | { | |
1051 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | |
1052 | struct snd_soc_component *component = | |
1053 | snd_soc_rtdcom_lookup(rtd, BTCVSD_SND_NAME); | |
1054 | struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(component); | |
1055 | ||
1056 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | |
1057 | mtk_btcvsd_snd_write(bt, buf, count); | |
1058 | else | |
1059 | mtk_btcvsd_snd_read(bt, buf, count); | |
1060 | ||
1061 | return 0; | |
1062 | } | |
1063 | ||
1064 | static struct snd_pcm_ops mtk_btcvsd_ops = { | |
1065 | .open = mtk_pcm_btcvsd_open, | |
1066 | .close = mtk_pcm_btcvsd_close, | |
1067 | .ioctl = snd_pcm_lib_ioctl, | |
1068 | .hw_params = mtk_pcm_btcvsd_hw_params, | |
1069 | .hw_free = mtk_pcm_btcvsd_hw_free, | |
1070 | .prepare = mtk_pcm_btcvsd_prepare, | |
1071 | .trigger = mtk_pcm_btcvsd_trigger, | |
1072 | .pointer = mtk_pcm_btcvsd_pointer, | |
1073 | .copy_user = mtk_pcm_btcvsd_copy, | |
1074 | }; | |
1075 | ||
1076 | /* kcontrol */ | |
1077 | static const char *const btsco_band_str[] = {"NB", "WB"}; | |
1078 | ||
1079 | static const struct soc_enum btcvsd_enum[] = { | |
1080 | SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(btsco_band_str), btsco_band_str), | |
1081 | }; | |
1082 | ||
1083 | static int btcvsd_band_get(struct snd_kcontrol *kcontrol, | |
1084 | struct snd_ctl_elem_value *ucontrol) | |
1085 | { | |
1086 | struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); | |
1087 | struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(cmpnt); | |
1088 | ||
1089 | ucontrol->value.integer.value[0] = bt->band; | |
1090 | return 0; | |
1091 | } | |
1092 | ||
1093 | static int btcvsd_band_set(struct snd_kcontrol *kcontrol, | |
1094 | struct snd_ctl_elem_value *ucontrol) | |
1095 | { | |
1096 | struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); | |
1097 | struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(cmpnt); | |
1098 | struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; | |
1099 | ||
1100 | if (ucontrol->value.enumerated.item[0] >= e->items) | |
1101 | return -EINVAL; | |
1102 | ||
1103 | bt->band = ucontrol->value.integer.value[0]; | |
1104 | dev_dbg(bt->dev, "%s(), band %d\n", __func__, bt->band); | |
1105 | return 0; | |
1106 | } | |
1107 | ||
f060f46f KC |
1108 | static int btcvsd_loopback_get(struct snd_kcontrol *kcontrol, |
1109 | struct snd_ctl_elem_value *ucontrol) | |
1110 | { | |
1111 | struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); | |
1112 | struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(cmpnt); | |
1113 | bool lpbk_en = bt->tx->state == BT_SCO_STATE_LOOPBACK; | |
1114 | ||
1115 | ucontrol->value.integer.value[0] = lpbk_en; | |
1116 | return 0; | |
1117 | } | |
1118 | ||
1119 | static int btcvsd_loopback_set(struct snd_kcontrol *kcontrol, | |
1120 | struct snd_ctl_elem_value *ucontrol) | |
1121 | { | |
1122 | struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); | |
1123 | struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(cmpnt); | |
1124 | ||
1125 | if (ucontrol->value.integer.value[0]) { | |
1126 | mtk_btcvsd_snd_set_state(bt, bt->tx, BT_SCO_STATE_LOOPBACK); | |
1127 | mtk_btcvsd_snd_set_state(bt, bt->rx, BT_SCO_STATE_LOOPBACK); | |
1128 | } else { | |
1129 | mtk_btcvsd_snd_set_state(bt, bt->tx, BT_SCO_STATE_RUNNING); | |
1130 | mtk_btcvsd_snd_set_state(bt, bt->rx, BT_SCO_STATE_RUNNING); | |
1131 | } | |
1132 | return 0; | |
1133 | } | |
1134 | ||
4bd8597d KC |
1135 | static int btcvsd_tx_mute_get(struct snd_kcontrol *kcontrol, |
1136 | struct snd_ctl_elem_value *ucontrol) | |
1137 | { | |
1138 | struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); | |
1139 | struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(cmpnt); | |
1140 | ||
1141 | if (!bt->tx) { | |
1142 | ucontrol->value.integer.value[0] = 0; | |
1143 | return 0; | |
1144 | } | |
1145 | ||
1146 | ucontrol->value.integer.value[0] = bt->tx->mute; | |
1147 | return 0; | |
1148 | } | |
1149 | ||
1150 | static int btcvsd_tx_mute_set(struct snd_kcontrol *kcontrol, | |
1151 | struct snd_ctl_elem_value *ucontrol) | |
1152 | { | |
1153 | struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); | |
1154 | struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(cmpnt); | |
1155 | ||
1156 | if (!bt->tx) | |
1157 | return 0; | |
1158 | ||
1159 | bt->tx->mute = ucontrol->value.integer.value[0]; | |
1160 | return 0; | |
1161 | } | |
1162 | ||
1163 | static int btcvsd_rx_irq_received_get(struct snd_kcontrol *kcontrol, | |
1164 | struct snd_ctl_elem_value *ucontrol) | |
1165 | { | |
1166 | struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); | |
1167 | struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(cmpnt); | |
1168 | ||
1169 | if (!bt->rx) | |
1170 | return 0; | |
1171 | ||
1172 | ucontrol->value.integer.value[0] = bt->rx->rw_cnt ? 1 : 0; | |
1173 | return 0; | |
1174 | } | |
1175 | ||
1176 | static int btcvsd_rx_timeout_get(struct snd_kcontrol *kcontrol, | |
1177 | struct snd_ctl_elem_value *ucontrol) | |
1178 | { | |
1179 | struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); | |
1180 | struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(cmpnt); | |
1181 | ||
1182 | if (!bt->rx) | |
1183 | return 0; | |
1184 | ||
1185 | ucontrol->value.integer.value[0] = bt->rx->timeout; | |
1186 | bt->rx->timeout = 0; | |
1187 | return 0; | |
1188 | } | |
1189 | ||
1190 | static int btcvsd_rx_timestamp_get(struct snd_kcontrol *kcontrol, | |
1191 | unsigned int __user *data, unsigned int size) | |
1192 | { | |
1193 | struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); | |
1194 | struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(cmpnt); | |
1195 | int ret = 0; | |
1196 | struct mtk_btcvsd_snd_time_buffer_info time_buffer_info_rx; | |
1197 | ||
1198 | if (size > sizeof(struct mtk_btcvsd_snd_time_buffer_info)) | |
1199 | return -EINVAL; | |
1200 | ||
1201 | get_rx_time_stamp(bt, &time_buffer_info_rx); | |
1202 | ||
1203 | dev_dbg(bt->dev, "%s(), time_stamp_us %llu, data_count_equi_time %llu", | |
1204 | __func__, | |
1205 | time_buffer_info_rx.time_stamp_us, | |
1206 | time_buffer_info_rx.data_count_equi_time); | |
1207 | ||
1208 | if (copy_to_user(data, &time_buffer_info_rx, | |
1209 | sizeof(struct mtk_btcvsd_snd_time_buffer_info))) { | |
1210 | dev_warn(bt->dev, "%s(), copy_to_user fail", __func__); | |
1211 | ret = -EFAULT; | |
1212 | } | |
1213 | ||
1214 | return ret; | |
1215 | } | |
1216 | ||
1217 | static int btcvsd_tx_irq_received_get(struct snd_kcontrol *kcontrol, | |
1218 | struct snd_ctl_elem_value *ucontrol) | |
1219 | { | |
1220 | struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); | |
1221 | struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(cmpnt); | |
1222 | ||
1223 | if (!bt->tx) | |
1224 | return 0; | |
1225 | ||
1226 | ucontrol->value.integer.value[0] = bt->tx->rw_cnt ? 1 : 0; | |
1227 | return 0; | |
1228 | } | |
1229 | ||
1230 | static int btcvsd_tx_timeout_get(struct snd_kcontrol *kcontrol, | |
1231 | struct snd_ctl_elem_value *ucontrol) | |
1232 | { | |
1233 | struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); | |
1234 | struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(cmpnt); | |
1235 | ||
1236 | ucontrol->value.integer.value[0] = bt->tx->timeout; | |
1237 | return 0; | |
1238 | } | |
1239 | ||
1240 | static int btcvsd_tx_timestamp_get(struct snd_kcontrol *kcontrol, | |
1241 | unsigned int __user *data, unsigned int size) | |
1242 | { | |
1243 | struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); | |
1244 | struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(cmpnt); | |
1245 | int ret = 0; | |
1246 | struct mtk_btcvsd_snd_time_buffer_info time_buffer_info_tx; | |
1247 | ||
1248 | if (size > sizeof(struct mtk_btcvsd_snd_time_buffer_info)) | |
1249 | return -EINVAL; | |
1250 | ||
1251 | get_tx_time_stamp(bt, &time_buffer_info_tx); | |
1252 | ||
1253 | dev_dbg(bt->dev, "%s(), time_stamp_us %llu, data_count_equi_time %llu", | |
1254 | __func__, | |
1255 | time_buffer_info_tx.time_stamp_us, | |
1256 | time_buffer_info_tx.data_count_equi_time); | |
1257 | ||
1258 | if (copy_to_user(data, &time_buffer_info_tx, | |
1259 | sizeof(struct mtk_btcvsd_snd_time_buffer_info))) { | |
1260 | dev_warn(bt->dev, "%s(), copy_to_user fail", __func__); | |
1261 | ret = -EFAULT; | |
1262 | } | |
1263 | ||
1264 | return ret; | |
1265 | } | |
1266 | ||
1267 | static const struct snd_kcontrol_new mtk_btcvsd_snd_controls[] = { | |
1268 | SOC_ENUM_EXT("BTCVSD Band", btcvsd_enum[0], | |
1269 | btcvsd_band_get, btcvsd_band_set), | |
f060f46f KC |
1270 | SOC_SINGLE_BOOL_EXT("BTCVSD Loopback Switch", 0, |
1271 | btcvsd_loopback_get, btcvsd_loopback_set), | |
4bd8597d KC |
1272 | SOC_SINGLE_BOOL_EXT("BTCVSD Tx Mute Switch", 0, |
1273 | btcvsd_tx_mute_get, btcvsd_tx_mute_set), | |
1274 | SOC_SINGLE_BOOL_EXT("BTCVSD Tx Irq Received Switch", 0, | |
1275 | btcvsd_tx_irq_received_get, NULL), | |
1276 | SOC_SINGLE_BOOL_EXT("BTCVSD Tx Timeout Switch", 0, | |
1277 | btcvsd_tx_timeout_get, NULL), | |
1278 | SOC_SINGLE_BOOL_EXT("BTCVSD Rx Irq Received Switch", 0, | |
1279 | btcvsd_rx_irq_received_get, NULL), | |
1280 | SOC_SINGLE_BOOL_EXT("BTCVSD Rx Timeout Switch", 0, | |
1281 | btcvsd_rx_timeout_get, NULL), | |
1282 | SND_SOC_BYTES_TLV("BTCVSD Rx Timestamp", | |
1283 | sizeof(struct mtk_btcvsd_snd_time_buffer_info), | |
1284 | btcvsd_rx_timestamp_get, NULL), | |
1285 | SND_SOC_BYTES_TLV("BTCVSD Tx Timestamp", | |
1286 | sizeof(struct mtk_btcvsd_snd_time_buffer_info), | |
1287 | btcvsd_tx_timestamp_get, NULL), | |
1288 | }; | |
1289 | ||
1290 | static int mtk_btcvsd_snd_component_probe(struct snd_soc_component *component) | |
1291 | { | |
1292 | return snd_soc_add_component_controls(component, | |
1293 | mtk_btcvsd_snd_controls, | |
1294 | ARRAY_SIZE(mtk_btcvsd_snd_controls)); | |
1295 | } | |
1296 | ||
1297 | static const struct snd_soc_component_driver mtk_btcvsd_snd_platform = { | |
1298 | .name = BTCVSD_SND_NAME, | |
1299 | .ops = &mtk_btcvsd_ops, | |
1300 | .probe = mtk_btcvsd_snd_component_probe, | |
1301 | }; | |
1302 | ||
1303 | static int mtk_btcvsd_snd_probe(struct platform_device *pdev) | |
1304 | { | |
1305 | int ret = 0; | |
1306 | int irq_id; | |
1307 | u32 offset[5] = {0, 0, 0, 0, 0}; | |
1308 | struct mtk_btcvsd_snd *btcvsd; | |
1309 | struct device *dev = &pdev->dev; | |
1310 | ||
1311 | /* init btcvsd private data */ | |
1312 | btcvsd = devm_kzalloc(dev, sizeof(*btcvsd), GFP_KERNEL); | |
1313 | if (!btcvsd) | |
1314 | return -ENOMEM; | |
1315 | platform_set_drvdata(pdev, btcvsd); | |
1316 | btcvsd->dev = dev; | |
1317 | ||
1318 | /* init tx/rx */ | |
1319 | btcvsd->rx = devm_kzalloc(btcvsd->dev, sizeof(*btcvsd->rx), GFP_KERNEL); | |
1320 | if (!btcvsd->rx) | |
1321 | return -ENOMEM; | |
1322 | ||
1323 | btcvsd->tx = devm_kzalloc(btcvsd->dev, sizeof(*btcvsd->tx), GFP_KERNEL); | |
1324 | if (!btcvsd->tx) | |
1325 | return -ENOMEM; | |
1326 | ||
1327 | spin_lock_init(&btcvsd->tx_lock); | |
1328 | spin_lock_init(&btcvsd->rx_lock); | |
1329 | ||
1330 | init_waitqueue_head(&btcvsd->tx_wait); | |
1331 | init_waitqueue_head(&btcvsd->rx_wait); | |
1332 | ||
1333 | mtk_btcvsd_snd_tx_init(btcvsd); | |
1334 | mtk_btcvsd_snd_rx_init(btcvsd); | |
1335 | ||
1336 | /* irq */ | |
1337 | irq_id = platform_get_irq(pdev, 0); | |
1338 | if (irq_id <= 0) { | |
49ff8cfb | 1339 | dev_err(dev, "%pOFn no irq found\n", dev->of_node); |
4bd8597d KC |
1340 | return irq_id < 0 ? irq_id : -ENXIO; |
1341 | } | |
1342 | ||
1343 | ret = devm_request_irq(dev, irq_id, mtk_btcvsd_snd_irq_handler, | |
1344 | IRQF_TRIGGER_LOW, "BTCVSD_ISR_Handle", | |
1345 | (void *)btcvsd); | |
1346 | if (ret) { | |
1347 | dev_err(dev, "could not request_irq for BTCVSD_ISR_Handle\n"); | |
1348 | return ret; | |
1349 | } | |
1350 | ||
1351 | btcvsd->irq_id = irq_id; | |
1352 | ||
1353 | /* iomap */ | |
1354 | btcvsd->bt_pkv_base = of_iomap(dev->of_node, 0); | |
1355 | if (!btcvsd->bt_pkv_base) { | |
1356 | dev_err(dev, "iomap bt_pkv_base fail\n"); | |
1357 | return -EIO; | |
1358 | } | |
1359 | ||
1360 | btcvsd->bt_sram_bank2_base = of_iomap(dev->of_node, 1); | |
1361 | if (!btcvsd->bt_sram_bank2_base) { | |
1362 | dev_err(dev, "iomap bt_sram_bank2_base fail\n"); | |
1363 | return -EIO; | |
1364 | } | |
1365 | ||
1366 | btcvsd->infra = syscon_regmap_lookup_by_phandle(dev->of_node, | |
1367 | "mediatek,infracfg"); | |
1368 | if (IS_ERR(btcvsd->infra)) { | |
1369 | dev_err(dev, "cannot find infra controller: %ld\n", | |
1370 | PTR_ERR(btcvsd->infra)); | |
1371 | return PTR_ERR(btcvsd->infra); | |
1372 | } | |
1373 | ||
1374 | /* get offset */ | |
1375 | ret = of_property_read_u32_array(dev->of_node, "mediatek,offset", | |
1376 | offset, | |
1377 | ARRAY_SIZE(offset)); | |
1378 | if (ret) { | |
766cc496 | 1379 | dev_warn(dev, "%s(), get offset fail, ret %d\n", __func__, ret); |
4bd8597d KC |
1380 | return ret; |
1381 | } | |
1382 | btcvsd->infra_misc_offset = offset[0]; | |
1383 | btcvsd->conn_bt_cvsd_mask = offset[1]; | |
1384 | btcvsd->cvsd_mcu_read_offset = offset[2]; | |
1385 | btcvsd->cvsd_mcu_write_offset = offset[3]; | |
1386 | btcvsd->cvsd_packet_indicator = offset[4]; | |
1387 | ||
1388 | btcvsd->bt_reg_pkt_r = btcvsd->bt_pkv_base + | |
1389 | btcvsd->cvsd_mcu_read_offset; | |
1390 | btcvsd->bt_reg_pkt_w = btcvsd->bt_pkv_base + | |
1391 | btcvsd->cvsd_mcu_write_offset; | |
1392 | btcvsd->bt_reg_ctl = btcvsd->bt_pkv_base + | |
1393 | btcvsd->cvsd_packet_indicator; | |
1394 | ||
1395 | /* init state */ | |
1396 | mtk_btcvsd_snd_set_state(btcvsd, btcvsd->tx, BT_SCO_STATE_IDLE); | |
1397 | mtk_btcvsd_snd_set_state(btcvsd, btcvsd->rx, BT_SCO_STATE_IDLE); | |
1398 | ||
1399 | return devm_snd_soc_register_component(dev, &mtk_btcvsd_snd_platform, | |
1400 | NULL, 0); | |
1401 | } | |
1402 | ||
1403 | static int mtk_btcvsd_snd_remove(struct platform_device *pdev) | |
1404 | { | |
1405 | struct mtk_btcvsd_snd *btcvsd = dev_get_drvdata(&pdev->dev); | |
1406 | ||
1407 | iounmap(btcvsd->bt_pkv_base); | |
1408 | iounmap(btcvsd->bt_sram_bank2_base); | |
1409 | return 0; | |
1410 | } | |
1411 | ||
1412 | static const struct of_device_id mtk_btcvsd_snd_dt_match[] = { | |
1413 | { .compatible = "mediatek,mtk-btcvsd-snd", }, | |
1414 | {}, | |
1415 | }; | |
1416 | MODULE_DEVICE_TABLE(of, mtk_btcvsd_snd_dt_match); | |
1417 | ||
1418 | static struct platform_driver mtk_btcvsd_snd_driver = { | |
1419 | .driver = { | |
1420 | .name = "mtk-btcvsd-snd", | |
1421 | .of_match_table = mtk_btcvsd_snd_dt_match, | |
1422 | }, | |
1423 | .probe = mtk_btcvsd_snd_probe, | |
1424 | .remove = mtk_btcvsd_snd_remove, | |
1425 | }; | |
1426 | ||
1427 | module_platform_driver(mtk_btcvsd_snd_driver); | |
1428 | ||
1429 | MODULE_DESCRIPTION("Mediatek ALSA BT SCO CVSD/MSBC Driver"); | |
1430 | MODULE_AUTHOR("KaiChieh Chuang <kaichieh.chuang@mediatek.com>"); | |
1431 | MODULE_LICENSE("GPL v2"); |