// SPDX-License-Identifier: GPL-2.0 // // Mediatek ALSA BT SCO CVSD/MSBC Driver // // Copyright (c) 2019 MediaTek Inc. // Author: KaiChieh Chuang #include #include #include #include #include #include #define BTCVSD_SND_NAME "mtk-btcvsd-snd" #define BT_CVSD_TX_NREADY BIT(21) #define BT_CVSD_RX_READY BIT(22) #define BT_CVSD_TX_UNDERFLOW BIT(23) #define BT_CVSD_RX_OVERFLOW BIT(24) #define BT_CVSD_INTERRUPT BIT(31) #define BT_CVSD_CLEAR \ (BT_CVSD_TX_NREADY | BT_CVSD_RX_READY | BT_CVSD_RX_OVERFLOW |\ BT_CVSD_INTERRUPT) /* TX */ #define SCO_TX_ENCODE_SIZE (60) /* 18 = 6 * 180 / SCO_TX_ENCODE_SIZE */ #define SCO_TX_PACKER_BUF_NUM (18) /* RX */ #define SCO_RX_PLC_SIZE (30) #define SCO_RX_PACKER_BUF_NUM (64) #define SCO_RX_PACKET_MASK (0x3F) #define SCO_CVSD_PACKET_VALID_SIZE 2 #define SCO_PACKET_120 120 #define SCO_PACKET_180 180 #define BTCVSD_RX_PACKET_SIZE (SCO_RX_PLC_SIZE + SCO_CVSD_PACKET_VALID_SIZE) #define BTCVSD_TX_PACKET_SIZE (SCO_TX_ENCODE_SIZE) #define BTCVSD_RX_BUF_SIZE (BTCVSD_RX_PACKET_SIZE * SCO_RX_PACKER_BUF_NUM) #define BTCVSD_TX_BUF_SIZE (BTCVSD_TX_PACKET_SIZE * SCO_TX_PACKER_BUF_NUM) enum bt_sco_state { BT_SCO_STATE_IDLE, BT_SCO_STATE_RUNNING, BT_SCO_STATE_ENDING, BT_SCO_STATE_LOOPBACK, }; enum bt_sco_direct { BT_SCO_DIRECT_BT2ARM, BT_SCO_DIRECT_ARM2BT, }; enum bt_sco_packet_len { BT_SCO_CVSD_30 = 0, BT_SCO_CVSD_60, BT_SCO_CVSD_90, BT_SCO_CVSD_120, BT_SCO_CVSD_10, BT_SCO_CVSD_20, BT_SCO_CVSD_MAX, }; enum BT_SCO_BAND { BT_SCO_NB, BT_SCO_WB, }; struct mtk_btcvsd_snd_hw_info { unsigned int num_valid_addr; unsigned long bt_sram_addr[20]; unsigned int packet_length; unsigned int packet_num; }; struct mtk_btcvsd_snd_stream { struct snd_pcm_substream *substream; int stream; enum bt_sco_state state; unsigned int packet_size; unsigned int buf_size; u8 temp_packet_buf[SCO_PACKET_180]; int packet_w; int packet_r; snd_pcm_uframes_t prev_frame; int prev_packet_idx; unsigned int xrun:1; unsigned int timeout:1; unsigned int mute:1; unsigned int trigger_start:1; unsigned int wait_flag:1; unsigned int rw_cnt; unsigned long long time_stamp; unsigned long long buf_data_equivalent_time; struct mtk_btcvsd_snd_hw_info buffer_info; }; struct mtk_btcvsd_snd { struct device *dev; int irq_id; struct regmap *infra; void __iomem *bt_pkv_base; void __iomem *bt_sram_bank2_base; unsigned int infra_misc_offset; unsigned int conn_bt_cvsd_mask; unsigned int cvsd_mcu_read_offset; unsigned int cvsd_mcu_write_offset; unsigned int cvsd_packet_indicator; u32 *bt_reg_pkt_r; u32 *bt_reg_pkt_w; u32 *bt_reg_ctl; unsigned int irq_disabled:1; unsigned int bypass_bt_access:1; spinlock_t tx_lock; /* spinlock for bt tx stream control */ spinlock_t rx_lock; /* spinlock for bt rx stream control */ wait_queue_head_t tx_wait; wait_queue_head_t rx_wait; struct mtk_btcvsd_snd_stream *tx; struct mtk_btcvsd_snd_stream *rx; u8 tx_packet_buf[BTCVSD_TX_BUF_SIZE]; u8 rx_packet_buf[BTCVSD_RX_BUF_SIZE]; u8 disable_write_silence; u8 write_tx:1; enum BT_SCO_BAND band; }; struct mtk_btcvsd_snd_time_buffer_info { unsigned long long data_count_equi_time; unsigned long long time_stamp_us; }; static const unsigned int btsco_packet_valid_mask[BT_SCO_CVSD_MAX][6] = { {0x1, 0x1 << 1, 0x1 << 2, 0x1 << 3, 0x1 << 4, 0x1 << 5}, {0x1, 0x1, 0x2, 0x2, 0x4, 0x4}, {0x1, 0x1, 0x1, 0x2, 0x2, 0x2}, {0x1, 0x1, 0x1, 0x1, 0x0, 0x0}, {0x7, 0x7 << 3, 0x7 << 6, 0x7 << 9, 0x7 << 12, 0x7 << 15}, {0x3, 0x3 << 1, 0x3 << 3, 0x3 << 4, 0x3 << 6, 0x3 << 7}, }; static const unsigned int btsco_packet_info[BT_SCO_CVSD_MAX][4] = { {30, 6, SCO_PACKET_180 / SCO_TX_ENCODE_SIZE, SCO_PACKET_180 / SCO_RX_PLC_SIZE}, {60, 3, SCO_PACKET_180 / SCO_TX_ENCODE_SIZE, SCO_PACKET_180 / SCO_RX_PLC_SIZE}, {90, 2, SCO_PACKET_180 / SCO_TX_ENCODE_SIZE, SCO_PACKET_180 / SCO_RX_PLC_SIZE}, {120, 1, SCO_PACKET_120 / SCO_TX_ENCODE_SIZE, SCO_PACKET_120 / SCO_RX_PLC_SIZE}, {10, 18, SCO_PACKET_180 / SCO_TX_ENCODE_SIZE, SCO_PACKET_180 / SCO_RX_PLC_SIZE}, {20, 9, SCO_PACKET_180 / SCO_TX_ENCODE_SIZE, SCO_PACKET_180 / SCO_RX_PLC_SIZE}, }; static const u8 table_msbc_silence[SCO_PACKET_180] = { 0x01, 0x38, 0xad, 0x00, 0x00, 0xc5, 0x00, 0x00, 0x00, 0x00, 0x77, 0x6d, 0xb6, 0xdd, 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6d, 0xdd, 0xb6, 0xdb, 0x77, 0x6d, 0xb6, 0xdd, 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6d, 0xdd, 0xb6, 0xdb, 0x77, 0x6d, 0xb6, 0xdd, 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6d, 0xdd, 0xb6, 0xdb, 0x77, 0x6d, 0xb6, 0xdd, 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6c, 0x00, 0x01, 0xc8, 0xad, 0x00, 0x00, 0xc5, 0x00, 0x00, 0x00, 0x00, 0x77, 0x6d, 0xb6, 0xdd, 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6d, 0xdd, 0xb6, 0xdb, 0x77, 0x6d, 0xb6, 0xdd, 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6d, 0xdd, 0xb6, 0xdb, 0x77, 0x6d, 0xb6, 0xdd, 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6d, 0xdd, 0xb6, 0xdb, 0x77, 0x6d, 0xb6, 0xdd, 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6c, 0x00, 0x01, 0xf8, 0xad, 0x00, 0x00, 0xc5, 0x00, 0x00, 0x00, 0x00, 0x77, 0x6d, 0xb6, 0xdd, 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6d, 0xdd, 0xb6, 0xdb, 0x77, 0x6d, 0xb6, 0xdd, 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6d, 0xdd, 0xb6, 0xdb, 0x77, 0x6d, 0xb6, 0xdd, 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6d, 0xdd, 0xb6, 0xdb, 0x77, 0x6d, 0xb6, 0xdd, 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6c, 0x00 }; static void mtk_btcvsd_snd_irq_enable(struct mtk_btcvsd_snd *bt) { regmap_update_bits(bt->infra, bt->infra_misc_offset, bt->conn_bt_cvsd_mask, 0); } static void mtk_btcvsd_snd_irq_disable(struct mtk_btcvsd_snd *bt) { regmap_update_bits(bt->infra, bt->infra_misc_offset, bt->conn_bt_cvsd_mask, bt->conn_bt_cvsd_mask); } static void mtk_btcvsd_snd_set_state(struct mtk_btcvsd_snd *bt, struct mtk_btcvsd_snd_stream *bt_stream, int state) { int pre_state = bt_stream->state; int pre_irq_disabled = bt->irq_disabled; bt_stream->state = state; if (bt->tx->state == BT_SCO_STATE_IDLE && bt->rx->state == BT_SCO_STATE_IDLE) { if (!bt->irq_disabled) { disable_irq(bt->irq_id); mtk_btcvsd_snd_irq_disable(bt); bt->irq_disabled = 1; } } else { if (bt->irq_disabled) { enable_irq(bt->irq_id); mtk_btcvsd_snd_irq_enable(bt); bt->irq_disabled = 0; } } dev_dbg(bt->dev, "%s(), stream %d, state %d->%d, tx->state %d, rx->state %d, irq_disabled %d->%d\n" , __func__, bt_stream->stream, pre_state, state, bt->tx->state, bt->rx->state, pre_irq_disabled, bt->irq_disabled); } static int mtk_btcvsd_snd_tx_init(struct mtk_btcvsd_snd *bt) { memset(bt->tx, 0, sizeof(*bt->tx)); memset(bt->tx_packet_buf, 0, sizeof(bt->tx_packet_buf)); bt->tx->packet_size = BTCVSD_TX_PACKET_SIZE; bt->tx->buf_size = BTCVSD_TX_BUF_SIZE; bt->tx->timeout = 0; bt->tx->rw_cnt = 0; bt->tx->stream = SNDRV_PCM_STREAM_PLAYBACK; return 0; } static int mtk_btcvsd_snd_rx_init(struct mtk_btcvsd_snd *bt) { memset(bt->rx, 0, sizeof(*bt->rx)); memset(bt->rx_packet_buf, 0, sizeof(bt->rx_packet_buf)); bt->rx->packet_size = BTCVSD_RX_PACKET_SIZE; bt->rx->buf_size = BTCVSD_RX_BUF_SIZE; bt->rx->timeout = 0; bt->rx->rw_cnt = 0; bt->rx->stream = SNDRV_PCM_STREAM_CAPTURE; return 0; } static void get_tx_time_stamp(struct mtk_btcvsd_snd *bt, struct mtk_btcvsd_snd_time_buffer_info *ts) { ts->time_stamp_us = bt->tx->time_stamp; ts->data_count_equi_time = bt->tx->buf_data_equivalent_time; } static void get_rx_time_stamp(struct mtk_btcvsd_snd *bt, struct mtk_btcvsd_snd_time_buffer_info *ts) { ts->time_stamp_us = bt->rx->time_stamp; ts->data_count_equi_time = bt->rx->buf_data_equivalent_time; } static int btcvsd_bytes_to_frame(struct snd_pcm_substream *substream, int bytes) { int count = bytes; struct snd_pcm_runtime *runtime = substream->runtime; if (runtime->format == SNDRV_PCM_FORMAT_S32_LE || runtime->format == SNDRV_PCM_FORMAT_U32_LE) count = count >> 2; else count = count >> 1; count = count / runtime->channels; return count; } static void mtk_btcvsd_snd_data_transfer(enum bt_sco_direct dir, u8 *src, u8 *dst, unsigned int blk_size, unsigned int blk_num) { unsigned int i, j; if (blk_size == 60 || blk_size == 120 || blk_size == 20) { u32 *src_32 = (u32 *)src; u32 *dst_32 = (u32 *)dst; for (i = 0; i < (blk_size * blk_num / 4); i++) *dst_32++ = *src_32++; } else { u16 *src_16 = (u16 *)src; u16 *dst_16 = (u16 *)dst; for (j = 0; j < blk_num; j++) { for (i = 0; i < (blk_size / 2); i++) *dst_16++ = *src_16++; if (dir == BT_SCO_DIRECT_BT2ARM) src_16++; else dst_16++; } } } /* write encoded mute data to bt sram */ static int btcvsd_tx_clean_buffer(struct mtk_btcvsd_snd *bt) { void *dst; unsigned long connsys_addr_tx, ap_addr_tx; unsigned int num_valid_addr; unsigned long flags; enum BT_SCO_BAND band = bt->band; if (bt->bypass_bt_access) return -EIO; /* prepare encoded mute data */ if (band == BT_SCO_NB) memset(bt->tx->temp_packet_buf, 170, SCO_PACKET_180); else memcpy(bt->tx->temp_packet_buf, table_msbc_silence, SCO_PACKET_180); /* write mute data to bt tx sram buffer */ spin_lock_irqsave(&bt->tx_lock, flags); num_valid_addr = bt->tx->buffer_info.num_valid_addr; dev_info(bt->dev, "%s(), band %d, num_valid_addr %u\n", __func__, band, num_valid_addr); connsys_addr_tx = *bt->bt_reg_pkt_w; ap_addr_tx = (unsigned long)bt->bt_sram_bank2_base + (connsys_addr_tx & 0xFFFF); if (connsys_addr_tx == 0xdeadfeed) { /* bt return 0xdeadfeed if read register during bt sleep */ dev_warn(bt->dev, "%s(), connsys_addr_tx == 0xdeadfeed\n", __func__); spin_unlock_irqrestore(&bt->tx_lock, flags); return -EIO; } dst = (void *)ap_addr_tx; dev_info(bt->dev, "%s(), clean addr 0x%lx\n", __func__, ap_addr_tx); mtk_btcvsd_snd_data_transfer(BT_SCO_DIRECT_ARM2BT, bt->tx->temp_packet_buf, dst, bt->tx->buffer_info.packet_length, bt->tx->buffer_info.packet_num); spin_unlock_irqrestore(&bt->tx_lock, flags); bt->write_tx = 1; return 0; } static int 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) { unsigned int i; int pv; u8 *src; unsigned int packet_buf_ofs; unsigned long flags; unsigned long connsys_addr_rx, ap_addr_rx; if (bt->bypass_bt_access) return -EIO; connsys_addr_rx = *bt->bt_reg_pkt_r; ap_addr_rx = (unsigned long)bt->bt_sram_bank2_base + (connsys_addr_rx & 0xFFFF); if (connsys_addr_rx == 0xdeadfeed) { /* bt return 0xdeadfeed if read register during bt sleep */ dev_warn(bt->dev, "%s(), connsys_addr_rx == 0xdeadfeed\n", __func__); return -EIO; } src = (u8 *)ap_addr_rx; mtk_btcvsd_snd_data_transfer(BT_SCO_DIRECT_BT2ARM, src, bt->rx->temp_packet_buf, packet_length, packet_num); spin_lock_irqsave(&bt->rx_lock, flags); for (i = 0; i < blk_size; i++) { packet_buf_ofs = (bt->rx->packet_w & SCO_RX_PACKET_MASK) * bt->rx->packet_size; memcpy(bt->rx_packet_buf + packet_buf_ofs, bt->rx->temp_packet_buf + (SCO_RX_PLC_SIZE * i), SCO_RX_PLC_SIZE); if ((control & btsco_packet_valid_mask[packet_type][i]) == btsco_packet_valid_mask[packet_type][i]) pv = 1; else pv = 0; packet_buf_ofs += SCO_RX_PLC_SIZE; memcpy(bt->rx_packet_buf + packet_buf_ofs, (void *)&pv, SCO_CVSD_PACKET_VALID_SIZE); bt->rx->packet_w++; } spin_unlock_irqrestore(&bt->rx_lock, flags); return 0; } int 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) { unsigned int i; unsigned long flags; u8 *dst; unsigned long connsys_addr_tx, ap_addr_tx; bool new_ap_addr_tx = true; unsigned int codec_id; if (bt->bypass_bt_access) return -EIO; connsys_addr_tx = *bt->bt_reg_pkt_w; ap_addr_tx = (unsigned long)bt->bt_sram_bank2_base + (connsys_addr_tx & 0xFFFF); if (connsys_addr_tx == 0xdeadfeed) { /* bt return 0xdeadfeed if read register during bt sleep */ dev_warn(bt->dev, "%s(), connsys_addr_tx == 0xdeadfeed\n", __func__); return -EIO; } spin_lock_irqsave(&bt->tx_lock, flags); for (i = 0; i < blk_size; i++) { memcpy(bt->tx->temp_packet_buf + (bt->tx->packet_size * i), (bt->tx_packet_buf + (bt->tx->packet_r % SCO_TX_PACKER_BUF_NUM) * bt->tx->packet_size), bt->tx->packet_size); bt->tx->packet_r++; } spin_unlock_irqrestore(&bt->tx_lock, flags); dst = (u8 *)ap_addr_tx; //codec_id: 0 default, 1 CVSD codec, 2 MSBC codec codec_id = (*bt->bt_reg_ctl >> 25) & 3; if ((!bt->tx->mute) && ((codec_id == 0) || codec_id == bt->band + 1)) { mtk_btcvsd_snd_data_transfer(BT_SCO_DIRECT_ARM2BT, bt->tx->temp_packet_buf, dst, packet_length, packet_num); bt->write_tx = 1; } /* store bt tx buffer sram info */ bt->tx->buffer_info.packet_length = packet_length; bt->tx->buffer_info.packet_num = packet_num; for (i = 0; i < bt->tx->buffer_info.num_valid_addr; i++) { if (bt->tx->buffer_info.bt_sram_addr[i] == ap_addr_tx) { new_ap_addr_tx = false; break; } } if (new_ap_addr_tx) { unsigned int next_idx; spin_lock_irqsave(&bt->tx_lock, flags); bt->tx->buffer_info.num_valid_addr++; next_idx = bt->tx->buffer_info.num_valid_addr - 1; bt->tx->buffer_info.bt_sram_addr[next_idx] = ap_addr_tx; spin_unlock_irqrestore(&bt->tx_lock, flags); dev_info(bt->dev, "%s(), new ap_addr_tx = 0x%lx, num_valid_addr %d\n", __func__, ap_addr_tx, bt->tx->buffer_info.num_valid_addr); } if (bt->tx->mute) btcvsd_tx_clean_buffer(bt); return 0; } static irqreturn_t mtk_btcvsd_snd_irq_handler(int irq_id, void *dev) { struct mtk_btcvsd_snd *bt = dev; unsigned int packet_type, packet_num, packet_length; unsigned int buf_cnt_tx, buf_cnt_rx, control; static DEFINE_RATELIMIT_STATE(_rs, 2 * HZ, 1); if (__ratelimit(&_rs)) dev_info(bt->dev, "%s(), irq_id=%d\n", __func__, irq_id); bt->write_tx = 0; if (bt->bypass_bt_access) goto irq_handler_exit; if (bt->rx->state != BT_SCO_STATE_RUNNING && bt->rx->state != BT_SCO_STATE_ENDING && bt->tx->state != BT_SCO_STATE_RUNNING && bt->tx->state != BT_SCO_STATE_ENDING && bt->tx->state != BT_SCO_STATE_LOOPBACK) { dev_warn(bt->dev, "%s(), in idle state: rx->state: %d, tx->state: %d\n", __func__, bt->rx->state, bt->tx->state); goto irq_handler_exit; } control = *bt->bt_reg_ctl; packet_type = (control >> 18) & 0x7; if (((control >> 31) & 1) == 0) { dev_warn(bt->dev, "%s(), ((control >> 31) & 1) == 0, control 0x%x\n", __func__, control); goto irq_handler_exit; } if (packet_type >= BT_SCO_CVSD_MAX) { dev_warn(bt->dev, "%s(), invalid packet_type %u, exit\n", __func__, packet_type); goto irq_handler_exit; } packet_length = btsco_packet_info[packet_type][0]; packet_num = btsco_packet_info[packet_type][1]; buf_cnt_tx = btsco_packet_info[packet_type][2]; buf_cnt_rx = btsco_packet_info[packet_type][3]; if (bt->tx->state == BT_SCO_STATE_LOOPBACK) { u8 *src, *dst; unsigned long connsys_addr_rx, ap_addr_rx; unsigned long connsys_addr_tx, ap_addr_tx; connsys_addr_rx = *bt->bt_reg_pkt_r; ap_addr_rx = (unsigned long)bt->bt_sram_bank2_base + (connsys_addr_rx & 0xFFFF); connsys_addr_tx = *bt->bt_reg_pkt_w; ap_addr_tx = (unsigned long)bt->bt_sram_bank2_base + (connsys_addr_tx & 0xFFFF); if (connsys_addr_tx == 0xdeadfeed || connsys_addr_rx == 0xdeadfeed) { /* bt return 0xdeadfeed if read reg during bt sleep */ dev_warn(bt->dev, "%s(), connsys_addr_tx == 0xdeadfeed\n", __func__); goto irq_handler_exit; } src = (u8 *)ap_addr_rx; dst = (u8 *)ap_addr_tx; mtk_btcvsd_snd_data_transfer(BT_SCO_DIRECT_BT2ARM, src, bt->tx->temp_packet_buf, packet_length, packet_num); mtk_btcvsd_snd_data_transfer(BT_SCO_DIRECT_ARM2BT, bt->tx->temp_packet_buf, dst, packet_length, packet_num); bt->rx->rw_cnt++; bt->tx->rw_cnt++; } if (bt->rx->state == BT_SCO_STATE_RUNNING || bt->rx->state == BT_SCO_STATE_ENDING) { if (bt->rx->xrun) { if (bt->rx->packet_w - bt->rx->packet_r <= SCO_RX_PACKER_BUF_NUM - 2 * buf_cnt_rx) { /* * free space is larger then * twice interrupt rx data size */ bt->rx->xrun = 0; dev_warn(bt->dev, "%s(), rx->xrun 0!\n", __func__); } } if (!bt->rx->xrun && (bt->rx->packet_w - bt->rx->packet_r <= SCO_RX_PACKER_BUF_NUM - buf_cnt_rx)) { mtk_btcvsd_read_from_bt(bt, packet_type, packet_length, packet_num, buf_cnt_rx, control); bt->rx->rw_cnt++; } else { bt->rx->xrun = 1; dev_warn(bt->dev, "%s(), rx->xrun 1\n", __func__); } } /* tx */ bt->tx->timeout = 0; if ((bt->tx->state == BT_SCO_STATE_RUNNING || bt->tx->state == BT_SCO_STATE_ENDING) && bt->tx->trigger_start) { if (bt->tx->xrun) { /* prepared data is larger then twice * interrupt tx data size */ if (bt->tx->packet_w - bt->tx->packet_r >= 2 * buf_cnt_tx) { bt->tx->xrun = 0; dev_warn(bt->dev, "%s(), tx->xrun 0\n", __func__); } } if ((!bt->tx->xrun && (bt->tx->packet_w - bt->tx->packet_r >= buf_cnt_tx)) || bt->tx->state == BT_SCO_STATE_ENDING) { mtk_btcvsd_write_to_bt(bt, packet_type, packet_length, packet_num, buf_cnt_tx); bt->tx->rw_cnt++; } else { bt->tx->xrun = 1; dev_warn(bt->dev, "%s(), tx->xrun 1\n", __func__); } } *bt->bt_reg_ctl &= ~BT_CVSD_CLEAR; if (bt->tx->state == BT_SCO_STATE_IDLE || bt->write_tx == 0) { *bt->bt_reg_ctl |= BT_CVSD_TX_UNDERFLOW; dev_info(bt->dev, "%s(), tx underflow, state = %d, write_tx = %d\n", __func__, bt->tx->state, bt->write_tx); } if (bt->rx->state == BT_SCO_STATE_RUNNING || bt->rx->state == BT_SCO_STATE_ENDING) { bt->rx->wait_flag = 1; wake_up_interruptible(&bt->rx_wait); snd_pcm_period_elapsed(bt->rx->substream); } if (bt->tx->state == BT_SCO_STATE_RUNNING || bt->tx->state == BT_SCO_STATE_ENDING) { bt->tx->wait_flag = 1; wake_up_interruptible(&bt->tx_wait); snd_pcm_period_elapsed(bt->tx->substream); } return IRQ_HANDLED; irq_handler_exit: *bt->bt_reg_ctl |= BT_CVSD_TX_UNDERFLOW; *bt->bt_reg_ctl &= ~BT_CVSD_CLEAR; dev_warn(bt->dev, "%s(), irq_handler_exit, bt_reg_ctl = 0x%lx\n", __func__, *bt->bt_reg_ctl); return IRQ_HANDLED; } static int wait_for_bt_irq(struct mtk_btcvsd_snd *bt, struct mtk_btcvsd_snd_stream *bt_stream) { unsigned long long t1, t2; /* one interrupt period = 22.5ms */ unsigned long long timeout_limit = 22500000; int max_timeout_trial = 2; int ret; struct timespec64 ts64; bt_stream->wait_flag = 0; while (max_timeout_trial && !bt_stream->wait_flag) { ktime_get_ts64(&ts64); t1 = timespec64_to_ns(&ts64); if (bt_stream->stream == SNDRV_PCM_STREAM_PLAYBACK) { ret = wait_event_interruptible_timeout(bt->tx_wait, bt_stream->wait_flag, nsecs_to_jiffies(timeout_limit)); } else { ret = wait_event_interruptible_timeout(bt->rx_wait, bt_stream->wait_flag, nsecs_to_jiffies(timeout_limit)); } ktime_get_ts64(&ts64); t2 = timespec64_to_ns(&ts64); t2 = t2 - t1; /* in ns (10^9) */ if (t2 > timeout_limit) { dev_warn(bt->dev, "%s(), stream %d, timeout %llu, limit %llu, ret %d, flag %d\n", __func__, bt_stream->stream, t2, timeout_limit, ret, bt_stream->wait_flag); } if (ret < 0) { /* * error, -ERESTARTSYS if it was interrupted by * a signal */ dev_warn(bt->dev, "%s(), stream %d, error, trial left %d\n", __func__, bt_stream->stream, max_timeout_trial); bt_stream->timeout = 1; return ret; } else if (ret == 0) { /* conidtion is false after timeout */ max_timeout_trial--; dev_warn(bt->dev, "%s(), stream %d, error, timeout, condition is false, trial left %d\n", __func__, bt_stream->stream, max_timeout_trial); if (max_timeout_trial <= 0) { bt_stream->timeout = 1; return -ETIME; } } } return 0; } ssize_t mtk_btcvsd_snd_read(struct mtk_btcvsd_snd *bt, char __user *buf, size_t count) { ssize_t read_size = 0, read_count = 0, cur_read_idx, cont; unsigned int cur_buf_ofs = 0; unsigned long avail; unsigned long flags; unsigned int packet_size = bt->rx->packet_size; struct timespec64 ts64; while (count) { spin_lock_irqsave(&bt->rx_lock, flags); /* available data in RX packet buffer */ avail = (bt->rx->packet_w - bt->rx->packet_r) * packet_size; cur_read_idx = (bt->rx->packet_r & SCO_RX_PACKET_MASK) * packet_size; spin_unlock_irqrestore(&bt->rx_lock, flags); if (!avail) { int ret = wait_for_bt_irq(bt, bt->rx); if (ret) return read_count; continue; } /* count must be multiple of packet_size */ if (count % packet_size != 0 || avail % packet_size != 0) { dev_warn(bt->dev, "%s(), count %zu or d %lu is not multiple of packet_size %dd\n", __func__, count, avail, packet_size); count -= count % packet_size; avail -= avail % packet_size; } if (count > avail) read_size = avail; else read_size = count; /* calculate continue space */ cont = bt->rx->buf_size - cur_read_idx; if (read_size > cont) read_size = cont; if (copy_to_user(buf + cur_buf_ofs, bt->rx_packet_buf + cur_read_idx, read_size)) { dev_warn(bt->dev, "%s(), copy_to_user fail\n", __func__); return -EFAULT; } spin_lock_irqsave(&bt->rx_lock, flags); bt->rx->packet_r += read_size / packet_size; spin_unlock_irqrestore(&bt->rx_lock, flags); read_count += read_size; cur_buf_ofs += read_size; count -= read_size; } /* * save current timestamp & buffer time in times_tamp and * buf_data_equivalent_time */ ktime_get_ts64(&ts64); bt->rx->time_stamp = timespec64_to_ns(&ts64); bt->rx->buf_data_equivalent_time = (unsigned long long)(bt->rx->packet_w - bt->rx->packet_r) * SCO_RX_PLC_SIZE * 16 * 1000 / 2 / 64; bt->rx->buf_data_equivalent_time += read_count * SCO_RX_PLC_SIZE * 16 * 1000 / packet_size / 2 / 64; /* return equivalent time(us) to data count */ bt->rx->buf_data_equivalent_time *= 1000; return read_count; } ssize_t mtk_btcvsd_snd_write(struct mtk_btcvsd_snd *bt, char __user *buf, size_t count) { int written_size = count, avail = 0, cur_write_idx, write_size, cont; unsigned int cur_buf_ofs = 0; unsigned long flags; unsigned int packet_size = bt->tx->packet_size; struct timespec64 ts64; /* * save current timestamp & buffer time in time_stamp and * buf_data_equivalent_time */ ktime_get_ts64(&ts64); bt->tx->time_stamp = timespec64_to_ns(&ts64); bt->tx->buf_data_equivalent_time = (unsigned long long)(bt->tx->packet_w - bt->tx->packet_r) * packet_size * 16 * 1000 / 2 / 64; /* return equivalent time(us) to data count */ bt->tx->buf_data_equivalent_time *= 1000; while (count) { spin_lock_irqsave(&bt->tx_lock, flags); /* free space of TX packet buffer */ avail = bt->tx->buf_size - (bt->tx->packet_w - bt->tx->packet_r) * packet_size; cur_write_idx = (bt->tx->packet_w % SCO_TX_PACKER_BUF_NUM) * packet_size; spin_unlock_irqrestore(&bt->tx_lock, flags); if (!avail) { int ret = wait_for_bt_irq(bt, bt->tx); if (ret) return written_size; continue; } /* count must be multiple of bt->tx->packet_size */ if (count % packet_size != 0 || avail % packet_size != 0) { dev_warn(bt->dev, "%s(), count %zu or avail %d is not multiple of packet_size %d\n", __func__, count, avail, packet_size); count -= count % packet_size; avail -= avail % packet_size; } if (count > avail) write_size = avail; else write_size = count; /* calculate continue space */ cont = bt->tx->buf_size - cur_write_idx; if (write_size > cont) write_size = cont; if (copy_from_user(bt->tx_packet_buf + cur_write_idx, buf + cur_buf_ofs, write_size)) { dev_warn(bt->dev, "%s(), copy_from_user fail\n", __func__); return -EFAULT; } spin_lock_irqsave(&bt->tx_lock, flags); bt->tx->packet_w += write_size / packet_size; spin_unlock_irqrestore(&bt->tx_lock, flags); cur_buf_ofs += write_size; count -= write_size; } return written_size; } static struct mtk_btcvsd_snd_stream *get_bt_stream (struct mtk_btcvsd_snd *bt, struct snd_pcm_substream *substream) { if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) return bt->tx; else return bt->rx; } /* pcm ops */ static const struct snd_pcm_hardware mtk_btcvsd_hardware = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_RESUME), .formats = SNDRV_PCM_FMTBIT_S16_LE, .buffer_bytes_max = 24 * 1024, .period_bytes_max = 24 * 1024, .periods_min = 2, .periods_max = 16, .fifo_size = 0, }; static int mtk_pcm_btcvsd_open(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, BTCVSD_SND_NAME); struct mtk_btcvsd_snd *bt; int ret; if (!component) { dev_warn(rtd->dev, "%s(), component is null\n", __func__); return -EINVAL; } bt = snd_soc_component_get_drvdata(component); dev_dbg(bt->dev, "%s(), stream %d, substream %p\n", __func__, substream->stream, substream); snd_soc_set_runtime_hwparams(substream, &mtk_btcvsd_hardware); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { ret = mtk_btcvsd_snd_tx_init(bt); bt->tx->substream = substream; } else { ret = mtk_btcvsd_snd_rx_init(bt); bt->rx->substream = substream; } return ret; } static int mtk_pcm_btcvsd_close(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, BTCVSD_SND_NAME); struct mtk_btcvsd_snd *bt; struct mtk_btcvsd_snd_stream *bt_stream; if (!component) { dev_warn(rtd->dev, "%s(), component is null\n", __func__); return -EINVAL; } bt = snd_soc_component_get_drvdata(component); bt_stream = get_bt_stream(bt, substream); dev_dbg(bt->dev, "%s(), stream %d\n", __func__, substream->stream); mtk_btcvsd_snd_set_state(bt, bt_stream, BT_SCO_STATE_IDLE); bt_stream->substream = NULL; return 0; } static int mtk_pcm_btcvsd_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, BTCVSD_SND_NAME); struct mtk_btcvsd_snd *bt; if (!component) { dev_warn(rtd->dev, "%s(), component is null\n", __func__); return -EINVAL; } bt = snd_soc_component_get_drvdata(component); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && params_buffer_bytes(hw_params) % bt->tx->packet_size != 0) { dev_warn(bt->dev, "%s(), error, buffer size %d not valid\n", __func__, params_buffer_bytes(hw_params)); return -EINVAL; } substream->runtime->dma_bytes = params_buffer_bytes(hw_params); return 0; } static int mtk_pcm_btcvsd_hw_free(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, BTCVSD_SND_NAME); struct mtk_btcvsd_snd *bt; if (!component) { dev_warn(rtd->dev, "%s(), component is null\n", __func__); return -EINVAL; } bt = snd_soc_component_get_drvdata(component); if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) && (bt->disable_write_silence == 0)) btcvsd_tx_clean_buffer(bt); return 0; } static int mtk_pcm_btcvsd_prepare(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, BTCVSD_SND_NAME); struct mtk_btcvsd_snd *bt; struct mtk_btcvsd_snd_stream *bt_stream; if (!component) { dev_warn(rtd->dev, "%s(), component is null\n", __func__); return -EINVAL; } bt = snd_soc_component_get_drvdata(component); bt_stream = get_bt_stream(bt, substream); dev_dbg(bt->dev, "%s(), stream %d\n", __func__, substream->stream); mtk_btcvsd_snd_set_state(bt, bt_stream, BT_SCO_STATE_RUNNING); return 0; } static int mtk_pcm_btcvsd_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, BTCVSD_SND_NAME); struct mtk_btcvsd_snd *bt; struct mtk_btcvsd_snd_stream *bt_stream; int stream = substream->stream; int hw_packet_ptr; if (!component) { dev_warn(rtd->dev, "%s(), component is null\n", __func__); return -EINVAL; } bt = snd_soc_component_get_drvdata(component); bt_stream = get_bt_stream(bt, substream); dev_dbg(bt->dev, "%s(), stream %d, cmd %d\n", __func__, substream->stream, cmd); switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: hw_packet_ptr = stream == SNDRV_PCM_STREAM_PLAYBACK ? bt_stream->packet_r : bt_stream->packet_w; bt_stream->prev_packet_idx = hw_packet_ptr; bt_stream->prev_frame = 0; bt_stream->trigger_start = 1; return 0; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: bt_stream->trigger_start = 0; mtk_btcvsd_snd_set_state(bt, bt_stream, BT_SCO_STATE_ENDING); return 0; default: return -EINVAL; } } static snd_pcm_uframes_t mtk_pcm_btcvsd_pointer (struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, BTCVSD_SND_NAME); struct mtk_btcvsd_snd *bt; struct mtk_btcvsd_snd_stream *bt_stream; snd_pcm_uframes_t frame = 0; int byte = 0; int hw_packet_ptr; int packet_diff; spinlock_t *lock; /* spinlock for bt stream control */ unsigned long flags; if (!component) { dev_warn(rtd->dev, "%s(), component is null\n", __func__); return -EINVAL; } bt = snd_soc_component_get_drvdata(component); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { lock = &bt->tx_lock; bt_stream = bt->tx; } else { lock = &bt->rx_lock; bt_stream = bt->rx; } spin_lock_irqsave(lock, flags); hw_packet_ptr = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? bt->tx->packet_r : bt->rx->packet_w; /* get packet diff from last time */ if (hw_packet_ptr >= bt_stream->prev_packet_idx) { packet_diff = hw_packet_ptr - bt_stream->prev_packet_idx; } else { /* integer overflow */ packet_diff = (INT_MAX - bt_stream->prev_packet_idx) + (hw_packet_ptr - INT_MIN) + 1; } bt_stream->prev_packet_idx = hw_packet_ptr; /* increased bytes */ byte = packet_diff * bt_stream->packet_size; frame = btcvsd_bytes_to_frame(substream, byte); frame += bt_stream->prev_frame; frame %= substream->runtime->buffer_size; bt_stream->prev_frame = frame; spin_unlock_irqrestore(lock, flags); return frame; } static int mtk_pcm_btcvsd_copy(struct snd_pcm_substream *substream, int channel, unsigned long pos, void __user *buf, unsigned long count) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, BTCVSD_SND_NAME); struct mtk_btcvsd_snd *bt; if (!component) { dev_warn(rtd->dev, "%s(), component is null\n", __func__); return -EINVAL; } bt = snd_soc_component_get_drvdata(component); /* count: bytes */ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) mtk_btcvsd_snd_write(bt, buf, count); else mtk_btcvsd_snd_read(bt, buf, count); return 0; } static struct snd_pcm_ops mtk_btcvsd_ops = { .open = mtk_pcm_btcvsd_open, .close = mtk_pcm_btcvsd_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = mtk_pcm_btcvsd_hw_params, .hw_free = mtk_pcm_btcvsd_hw_free, .prepare = mtk_pcm_btcvsd_prepare, .trigger = mtk_pcm_btcvsd_trigger, .pointer = mtk_pcm_btcvsd_pointer, .copy_user = mtk_pcm_btcvsd_copy, }; /* kcontrol */ static const char *const btsco_band_str[] = {"NB", "WB"}; static const struct soc_enum btcvsd_enum[] = { SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(btsco_band_str), btsco_band_str), }; static int btcvsd_band_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(cmpnt); ucontrol->value.integer.value[0] = bt->band; return 0; } static int btcvsd_band_set(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(cmpnt); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; if (ucontrol->value.enumerated.item[0] >= e->items) return -EINVAL; bt->band = ucontrol->value.integer.value[0]; dev_dbg(bt->dev, "%s(), band %d\n", __func__, bt->band); return 0; } static int btcvsd_loopback_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(cmpnt); bool lpbk_en = bt->tx->state == BT_SCO_STATE_LOOPBACK; ucontrol->value.integer.value[0] = lpbk_en; return 0; } static int btcvsd_loopback_set(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(cmpnt); if (ucontrol->value.integer.value[0]) { mtk_btcvsd_snd_set_state(bt, bt->tx, BT_SCO_STATE_LOOPBACK); mtk_btcvsd_snd_set_state(bt, bt->rx, BT_SCO_STATE_LOOPBACK); } else { mtk_btcvsd_snd_set_state(bt, bt->tx, BT_SCO_STATE_RUNNING); mtk_btcvsd_snd_set_state(bt, bt->rx, BT_SCO_STATE_RUNNING); } return 0; } static int btcvsd_bypass_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(cmpnt); ucontrol->value.integer.value[0] = bt->bypass_bt_access; return 0; } static int btcvsd_bypass_set(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(cmpnt); bt->bypass_bt_access = ucontrol->value.integer.value[0]; dev_dbg(bt->dev, "%s(), bypass_bt_access %d\n", __func__, bt->bypass_bt_access); return 0; } static int btcvsd_tx_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(cmpnt); if (!bt->tx) { ucontrol->value.integer.value[0] = 0; return 0; } ucontrol->value.integer.value[0] = bt->tx->mute; return 0; } static int btcvsd_tx_mute_set(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(cmpnt); if (!bt->tx) return 0; bt->tx->mute = ucontrol->value.integer.value[0]; return 0; } static int btcvsd_rx_irq_received_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(cmpnt); if (!bt->rx) return 0; ucontrol->value.integer.value[0] = bt->rx->rw_cnt ? 1 : 0; return 0; } static int btcvsd_rx_timeout_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(cmpnt); if (!bt->rx) return 0; ucontrol->value.integer.value[0] = bt->rx->timeout; bt->rx->timeout = 0; return 0; } static int btcvsd_rx_timestamp_get(struct snd_kcontrol *kcontrol, unsigned int __user *data, unsigned int size) { struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(cmpnt); int ret = 0; struct mtk_btcvsd_snd_time_buffer_info time_buffer_info_rx; if (size > sizeof(struct mtk_btcvsd_snd_time_buffer_info)) return -EINVAL; get_rx_time_stamp(bt, &time_buffer_info_rx); dev_dbg(bt->dev, "%s(), time_stamp_us %llu, data_count_equi_time %llu", __func__, time_buffer_info_rx.time_stamp_us, time_buffer_info_rx.data_count_equi_time); if (copy_to_user(data, &time_buffer_info_rx, sizeof(struct mtk_btcvsd_snd_time_buffer_info))) { dev_warn(bt->dev, "%s(), copy_to_user fail", __func__); ret = -EFAULT; } return ret; } static int btcvsd_tx_irq_received_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(cmpnt); if (!bt->tx) return 0; ucontrol->value.integer.value[0] = bt->tx->rw_cnt ? 1 : 0; return 0; } static int btcvsd_tx_timeout_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(cmpnt); ucontrol->value.integer.value[0] = bt->tx->timeout; return 0; } static int btcvsd_tx_timestamp_get(struct snd_kcontrol *kcontrol, unsigned int __user *data, unsigned int size) { struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(cmpnt); int ret = 0; struct mtk_btcvsd_snd_time_buffer_info time_buffer_info_tx; if (size > sizeof(struct mtk_btcvsd_snd_time_buffer_info)) return -EINVAL; get_tx_time_stamp(bt, &time_buffer_info_tx); dev_dbg(bt->dev, "%s(), time_stamp_us %llu, data_count_equi_time %llu", __func__, time_buffer_info_tx.time_stamp_us, time_buffer_info_tx.data_count_equi_time); if (copy_to_user(data, &time_buffer_info_tx, sizeof(struct mtk_btcvsd_snd_time_buffer_info))) { dev_warn(bt->dev, "%s(), copy_to_user fail", __func__); ret = -EFAULT; } return ret; } static const struct snd_kcontrol_new mtk_btcvsd_snd_controls[] = { SOC_ENUM_EXT("BTCVSD Band", btcvsd_enum[0], btcvsd_band_get, btcvsd_band_set), SOC_SINGLE_BOOL_EXT("BTCVSD Loopback Switch", 0, btcvsd_loopback_get, btcvsd_loopback_set), SOC_SINGLE_BOOL_EXT("BTCVSD Bypass Switch", 0, btcvsd_bypass_get, btcvsd_bypass_set), SOC_SINGLE_BOOL_EXT("BTCVSD Tx Mute Switch", 0, btcvsd_tx_mute_get, btcvsd_tx_mute_set), SOC_SINGLE_BOOL_EXT("BTCVSD Tx Irq Received Switch", 0, btcvsd_tx_irq_received_get, NULL), SOC_SINGLE_BOOL_EXT("BTCVSD Tx Timeout Switch", 0, btcvsd_tx_timeout_get, NULL), SOC_SINGLE_BOOL_EXT("BTCVSD Rx Irq Received Switch", 0, btcvsd_rx_irq_received_get, NULL), SOC_SINGLE_BOOL_EXT("BTCVSD Rx Timeout Switch", 0, btcvsd_rx_timeout_get, NULL), SND_SOC_BYTES_TLV("BTCVSD Rx Timestamp", sizeof(struct mtk_btcvsd_snd_time_buffer_info), btcvsd_rx_timestamp_get, NULL), SND_SOC_BYTES_TLV("BTCVSD Tx Timestamp", sizeof(struct mtk_btcvsd_snd_time_buffer_info), btcvsd_tx_timestamp_get, NULL), }; static int mtk_btcvsd_snd_component_probe(struct snd_soc_component *component) { return snd_soc_add_component_controls(component, mtk_btcvsd_snd_controls, ARRAY_SIZE(mtk_btcvsd_snd_controls)); } static const struct snd_soc_component_driver mtk_btcvsd_snd_platform = { .name = BTCVSD_SND_NAME, .probe = mtk_btcvsd_snd_component_probe, .ops = &mtk_btcvsd_ops, }; static int mtk_btcvsd_snd_probe(struct platform_device *pdev) { int ret = 0; int irq_id; u32 offset[5] = {0, 0, 0, 0, 0}; struct mtk_btcvsd_snd *btcvsd; struct device *dev = &pdev->dev; u32 disable_write_silence = 0; /* init btcvsd private data */ btcvsd = devm_kzalloc(dev, sizeof(*btcvsd), GFP_KERNEL); if (!btcvsd) return -ENOMEM; platform_set_drvdata(pdev, btcvsd); btcvsd->dev = dev; /* init tx/rx */ btcvsd->rx = devm_kzalloc(btcvsd->dev, sizeof(*btcvsd->rx), GFP_KERNEL); if (!btcvsd->rx) return -ENOMEM; btcvsd->tx = devm_kzalloc(btcvsd->dev, sizeof(*btcvsd->tx), GFP_KERNEL); if (!btcvsd->tx) return -ENOMEM; spin_lock_init(&btcvsd->tx_lock); spin_lock_init(&btcvsd->rx_lock); init_waitqueue_head(&btcvsd->tx_wait); init_waitqueue_head(&btcvsd->rx_wait); mtk_btcvsd_snd_tx_init(btcvsd); mtk_btcvsd_snd_rx_init(btcvsd); /* irq */ irq_id = platform_get_irq(pdev, 0); if (irq_id <= 0) { dev_err(dev, "%pOFn no irq found\n", dev->of_node); return irq_id < 0 ? irq_id : -ENXIO; } ret = devm_request_irq(dev, irq_id, mtk_btcvsd_snd_irq_handler, IRQF_TRIGGER_NONE, "BTCVSD_ISR_Handle", (void *)btcvsd); if (ret) { dev_err(dev, "could not request_irq for BTCVSD_ISR_Handle\n"); return ret; } btcvsd->irq_id = irq_id; /* iomap */ btcvsd->bt_pkv_base = of_iomap(dev->of_node, 0); if (!btcvsd->bt_pkv_base) { dev_err(dev, "iomap bt_pkv_base fail\n"); return -EIO; } btcvsd->bt_sram_bank2_base = of_iomap(dev->of_node, 1); if (!btcvsd->bt_sram_bank2_base) { dev_err(dev, "iomap bt_sram_bank2_base fail\n"); return -EIO; } btcvsd->infra = syscon_regmap_lookup_by_phandle(dev->of_node, "mediatek,infracfg"); if (IS_ERR(btcvsd->infra)) { dev_err(dev, "cannot find infra controller: %ld\n", PTR_ERR(btcvsd->infra)); return PTR_ERR(btcvsd->infra); } /* get offset */ ret = of_property_read_u32_array(dev->of_node, "mediatek,offset", offset, ARRAY_SIZE(offset)); if (ret) { dev_warn(dev, "%s(), get offset fail, ret %d\n", __func__, ret); return ret; } /* get disable_write_silence */ ret = of_property_read_u32(dev->of_node, "disable_write_silence", &disable_write_silence); if (ret) { dev_dbg(dev, "%s(), get disable_write_silence fail %d, set 0\n" , __func__, ret); } btcvsd->infra_misc_offset = offset[0]; btcvsd->conn_bt_cvsd_mask = offset[1]; btcvsd->cvsd_mcu_read_offset = offset[2]; btcvsd->cvsd_mcu_write_offset = offset[3]; btcvsd->cvsd_packet_indicator = offset[4]; btcvsd->bt_reg_pkt_r = btcvsd->bt_pkv_base + btcvsd->cvsd_mcu_read_offset; btcvsd->bt_reg_pkt_w = btcvsd->bt_pkv_base + btcvsd->cvsd_mcu_write_offset; btcvsd->bt_reg_ctl = btcvsd->bt_pkv_base + btcvsd->cvsd_packet_indicator; btcvsd->disable_write_silence = (u8) disable_write_silence; /* init state */ mtk_btcvsd_snd_set_state(btcvsd, btcvsd->tx, BT_SCO_STATE_IDLE); mtk_btcvsd_snd_set_state(btcvsd, btcvsd->rx, BT_SCO_STATE_IDLE); return devm_snd_soc_register_component(dev, &mtk_btcvsd_snd_platform, NULL, 0); } static int mtk_btcvsd_snd_remove(struct platform_device *pdev) { struct mtk_btcvsd_snd *btcvsd = dev_get_drvdata(&pdev->dev); iounmap(btcvsd->bt_pkv_base); iounmap(btcvsd->bt_sram_bank2_base); return 0; } static const struct of_device_id mtk_btcvsd_snd_dt_match[] = { { .compatible = "mediatek,mtk-btcvsd-snd", }, {}, }; MODULE_DEVICE_TABLE(of, mtk_btcvsd_snd_dt_match); static struct platform_driver mtk_btcvsd_snd_driver = { .driver = { .name = "mtk-btcvsd-snd", .of_match_table = mtk_btcvsd_snd_dt_match, }, .probe = mtk_btcvsd_snd_probe, .remove = mtk_btcvsd_snd_remove, }; module_platform_driver(mtk_btcvsd_snd_driver); MODULE_DESCRIPTION("Mediatek ALSA BT SCO CVSD/MSBC Driver"); MODULE_AUTHOR("KaiChieh Chuang "); MODULE_LICENSE("GPL v2");