Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
KaiChieh Chuang | 6975 | 98.48% | 4 | 33.33% |
Kuninori Morimoto | 45 | 0.64% | 1 | 8.33% |
Christophe Jaillet | 34 | 0.48% | 1 | 8.33% |
Takashi Iwai | 19 | 0.27% | 1 | 8.33% |
Yue haibing | 3 | 0.04% | 1 | 8.33% |
Jiasheng Jiang | 3 | 0.04% | 1 | 8.33% |
Uwe Kleine-König | 2 | 0.03% | 1 | 8.33% |
Lumi Lee | 1 | 0.01% | 1 | 8.33% |
Colin Ian King | 1 | 0.01% | 1 | 8.33% |
Total | 7083 | 12 |
// SPDX-License-Identifier: GPL-2.0 // // Mediatek ALSA BT SCO CVSD/MSBC Driver // // Copyright (c) 2019 MediaTek Inc. // Author: KaiChieh Chuang <kaichieh.chuang@mediatek.com> #include <linux/mfd/syscon.h> #include <linux/module.h> #include <linux/of_address.h> #include <linux/sched/clock.h> #include <sound/soc.h> #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_TX_UNDERFLOW |\ 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; 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]; 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) { dev_dbg(bt->dev, "%s(), stream %d, state %d, tx->state %d, rx->state %d, irq_disabled %d\n", __func__, bt_stream->stream, state, bt->tx->state, bt->rx->state, 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; } } } 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) { unsigned int i; unsigned int num_valid_addr; unsigned long flags; enum BT_SCO_BAND band = bt->band; /* 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); for (i = 0; i < num_valid_addr; i++) { void *dst; dev_info(bt->dev, "%s(), clean addr 0x%lx\n", __func__, bt->tx->buffer_info.bt_sram_addr[i]); dst = (void *)bt->tx->buffer_info.bt_sram_addr[i]; 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); 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; 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", __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; } static 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; 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; if (!bt->tx->mute) { mtk_btcvsd_snd_data_transfer(BT_SCO_DIRECT_ARM2BT, bt->tx->temp_packet_buf, dst, packet_length, packet_num); } /* 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; 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->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_CLEAR; 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; bt_stream->wait_flag = 0; while (max_timeout_trial && !bt_stream->wait_flag) { t1 = sched_clock(); 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)); } t2 = sched_clock(); 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; } static ssize_t mtk_btcvsd_snd_read(struct mtk_btcvsd_snd *bt, struct iov_iter *buf, size_t count) { ssize_t read_size = 0, read_count = 0, cur_read_idx, cont; unsigned long avail; unsigned long flags; unsigned int packet_size = bt->rx->packet_size; 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_iter(bt->rx_packet_buf + cur_read_idx, read_size, buf) != read_size) { dev_warn(bt->dev, "%s(), copy_to_iter 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; count -= read_size; } /* * save current timestamp & buffer time in times_tamp and * buf_data_equivalent_time */ bt->rx->time_stamp = sched_clock(); 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; } static ssize_t mtk_btcvsd_snd_write(struct mtk_btcvsd_snd *bt, struct iov_iter *buf, size_t count) { int written_size = count, avail, cur_write_idx, write_size, cont; unsigned long flags; unsigned int packet_size = bt->tx->packet_size; /* * save current timestamp & buffer time in time_stamp and * buf_data_equivalent_time */ bt->tx->time_stamp = sched_clock(); 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_iter(bt->tx_packet_buf + cur_write_idx, write_size, buf) != write_size) { dev_warn(bt->dev, "%s(), copy_from_iter 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); 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_soc_component *component, struct snd_pcm_substream *substream) { struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(component); int ret; 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_soc_component *component, struct snd_pcm_substream *substream) { struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(component); struct mtk_btcvsd_snd_stream *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_soc_component *component, struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct mtk_btcvsd_snd *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_soc_component *component, struct snd_pcm_substream *substream) { struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(component); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) btcvsd_tx_clean_buffer(bt); return 0; } static int mtk_pcm_btcvsd_prepare(struct snd_soc_component *component, struct snd_pcm_substream *substream) { struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(component); struct mtk_btcvsd_snd_stream *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_soc_component *component, struct snd_pcm_substream *substream, int cmd) { struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(component); struct mtk_btcvsd_snd_stream *bt_stream = get_bt_stream(bt, substream); int stream = substream->stream; int hw_packet_ptr; 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_soc_component *component, struct snd_pcm_substream *substream) { struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(component); 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 (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_soc_component *component, struct snd_pcm_substream *substream, int channel, unsigned long pos, struct iov_iter *buf, unsigned long count) { struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(component); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) return mtk_btcvsd_snd_write(bt, buf, count); else return mtk_btcvsd_snd_read(bt, buf, count); } /* 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_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 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, .open = mtk_pcm_btcvsd_open, .close = mtk_pcm_btcvsd_close, .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 = mtk_pcm_btcvsd_copy, }; static int mtk_btcvsd_snd_probe(struct platform_device *pdev) { int ret; int irq_id; u32 offset[5] = {0, 0, 0, 0, 0}; struct mtk_btcvsd_snd *btcvsd; struct device *dev = &pdev->dev; /* 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) return irq_id < 0 ? irq_id : -ENXIO; ret = devm_request_irq(dev, irq_id, mtk_btcvsd_snd_irq_handler, IRQF_TRIGGER_LOW, "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"); ret = -EIO; goto unmap_pkv_err; } 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)); ret = PTR_ERR(btcvsd->infra); goto unmap_bank2_err; } /* 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); goto unmap_bank2_err; } 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; /* 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); ret = devm_snd_soc_register_component(dev, &mtk_btcvsd_snd_platform, NULL, 0); if (ret) goto unmap_bank2_err; return 0; unmap_bank2_err: iounmap(btcvsd->bt_sram_bank2_base); unmap_pkv_err: iounmap(btcvsd->bt_pkv_base); return ret; } static void 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); } 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_new = mtk_btcvsd_snd_remove, }; module_platform_driver(mtk_btcvsd_snd_driver); MODULE_DESCRIPTION("Mediatek ALSA BT SCO CVSD/MSBC Driver"); MODULE_AUTHOR("KaiChieh Chuang <kaichieh.chuang@mediatek.com>"); MODULE_LICENSE("GPL v2");
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1