| /* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 and |
| * only version 2 as published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| */ |
| #include <linux/slimbus/slimbus.h> |
| #include <btfm_slim.h> |
| #include <btfm_slim_wcn3990.h> |
| |
| /* WCN3990 Port assignment */ |
| struct btfmslim_ch wcn3990_rxport[] = { |
| {.id = BTFM_BT_SCO_A2DP_SLIM_RX, .name = "SCO_A2P_Rx", |
| .port = CHRK_SB_PGD_PORT_RX_SCO}, |
| {.id = BTFM_BT_SPLIT_A2DP_SLIM_RX, .name = "A2P_Rx", |
| .port = CHRK_SB_PGD_PORT_RX_A2P}, |
| {.id = BTFM_SLIM_NUM_CODEC_DAIS, .name = "", |
| .port = BTFM_SLIM_PGD_PORT_LAST}, |
| }; |
| |
| struct btfmslim_ch wcn3990_txport[] = { |
| {.id = BTFM_FM_SLIM_TX, .name = "FM_Tx1", |
| .port = CHRK_SB_PGD_PORT_TX1_FM}, |
| {.id = BTFM_FM_SLIM_TX, .name = "FM_Tx2", |
| .port = CHRK_SB_PGD_PORT_TX2_FM}, |
| {.id = BTFM_BT_SCO_SLIM_TX, .name = "SCO_Tx", |
| .port = CHRK_SB_PGD_PORT_TX_SCO}, |
| {.id = BTFM_SLIM_NUM_CODEC_DAIS, .name = "", |
| .port = BTFM_SLIM_PGD_PORT_LAST}, |
| }; |
| |
| /* Function description */ |
| int btfm_slim_chrk_hw_init(struct btfmslim *btfmslim) |
| { |
| int ret = 0; |
| uint8_t reg_val; |
| uint16_t reg; |
| |
| BTFMSLIM_DBG(""); |
| |
| if (!btfmslim) |
| return -EINVAL; |
| |
| /* Get SB_SLAVE_HW_REV_MSB value*/ |
| reg = CHRK_SB_SLAVE_HW_REV_MSB; |
| ret = btfm_slim_read(btfmslim, reg, 1, ®_val, IFD); |
| if (ret) { |
| BTFMSLIM_ERR("failed to read (%d) reg 0x%x", ret, reg); |
| goto error; |
| } |
| BTFMSLIM_DBG("Major Rev: 0x%x, Minor Rev: 0x%x", |
| (reg_val & 0xF0) >> 4, (reg_val & 0x0F)); |
| |
| /* Get SB_SLAVE_HW_REV_LSB value*/ |
| reg = CHRK_SB_SLAVE_HW_REV_LSB; |
| ret = btfm_slim_read(btfmslim, reg, 1, ®_val, IFD); |
| if (ret) { |
| BTFMSLIM_ERR("failed to read (%d) reg 0x%x", ret, reg); |
| goto error; |
| } |
| BTFMSLIM_DBG("Step Rev: 0x%x", reg_val); |
| |
| error: |
| return ret; |
| } |
| |
| static inline int is_fm_port(uint8_t port_num) |
| { |
| if (port_num == CHRK_SB_PGD_PORT_TX1_FM || |
| port_num == CHRK_SB_PGD_PORT_TX2_FM) |
| return 1; |
| else |
| return 0; |
| } |
| |
| int btfm_slim_chrk_enable_port(struct btfmslim *btfmslim, uint8_t port_num, |
| uint8_t rxport, uint8_t enable) |
| { |
| int ret = 0; |
| uint8_t reg_val = 0, en; |
| uint8_t rxport_num = 0; |
| uint16_t reg; |
| uint8_t prev_reg_val = 0; |
| |
| BTFMSLIM_DBG("port(%d) enable(%d)", port_num, enable); |
| if (rxport) { |
| BTFMSLIM_DBG("sample rate is %d", btfmslim->sample_rate); |
| if (enable) { |
| /* For SCO Rx, A2DP Rx other than 44.1 and 88.2Khz */ |
| if (port_num < 24) { |
| rxport_num = port_num - 16; |
| reg_val = 0x01 << rxport_num; |
| reg = CHRK_SB_PGD_RX_PORTn_MULTI_CHNL_0( |
| rxport_num); |
| } else { |
| rxport_num = port_num - 24; |
| reg_val = 0x01 << rxport_num; |
| reg = CHRK_SB_PGD_RX_PORTn_MULTI_CHNL_1( |
| rxport_num); |
| } |
| |
| if (btfmslim->sample_rate == 44100 || |
| btfmslim->sample_rate == 88200) { |
| BTFMSLIM_DBG("unsetting multichannel bit"); |
| ret = btfm_slim_read(btfmslim, reg, 1, |
| &prev_reg_val, IFD); |
| if (ret < 0) { |
| BTFMSLIM_ERR("error %d reading", ret); |
| prev_reg_val = 0; |
| } |
| BTFMSLIM_DBG("prev_reg_val (%d) from reg(%x)", |
| prev_reg_val, reg); |
| reg_val = prev_reg_val & ~reg_val; |
| } else |
| BTFMSLIM_DBG("setting multichannel bit"); |
| |
| BTFMSLIM_DBG("writing reg_val (%d) to reg(%x)", |
| reg_val, reg); |
| ret = btfm_slim_write(btfmslim, reg, 1, ®_val, IFD); |
| if (ret) { |
| BTFMSLIM_ERR("failed to write (%d) reg 0x%x", |
| ret, reg); |
| goto error; |
| } |
| } |
| /* Port enable */ |
| reg = CHRK_SB_PGD_PORT_RX_CFGN(port_num - 0x10); |
| goto enable_disable_rxport; |
| } |
| if (!enable) |
| goto enable_disable_txport; |
| |
| /* txport */ |
| /* Multiple Channel Setting */ |
| if (is_fm_port(port_num)) { |
| reg_val = (0x1 << CHRK_SB_PGD_PORT_TX1_FM) | |
| (0x1 << CHRK_SB_PGD_PORT_TX2_FM); |
| reg = CHRK_SB_PGD_TX_PORTn_MULTI_CHNL_0(port_num); |
| ret = btfm_slim_write(btfmslim, reg, 1, ®_val, IFD); |
| if (ret) { |
| BTFMSLIM_ERR("failed to write (%d) reg 0x%x", ret, reg); |
| goto error; |
| } |
| } else if (port_num == CHRK_SB_PGD_PORT_TX_SCO) { |
| /* SCO Tx */ |
| reg_val = 0x1 << CHRK_SB_PGD_PORT_TX_SCO; |
| reg = CHRK_SB_PGD_TX_PORTn_MULTI_CHNL_0(port_num); |
| BTFMSLIM_DBG("writing reg_val (%d) to reg(%x)", |
| reg_val, reg); |
| ret = btfm_slim_write(btfmslim, reg, 1, ®_val, IFD); |
| if (ret) { |
| BTFMSLIM_ERR("failed to write (%d) reg 0x%x", |
| ret, reg); |
| goto error; |
| } |
| } |
| |
| /* Enable Tx port hw auto recovery for underrun or overrun error */ |
| reg_val = (CHRK_ENABLE_OVERRUN_AUTO_RECOVERY | |
| CHRK_ENABLE_UNDERRUN_AUTO_RECOVERY); |
| reg = CHRK_SB_PGD_PORT_TX_OR_UR_CFGN(port_num); |
| ret = btfm_slim_write(btfmslim, reg, 1, ®_val, IFD); |
| if (ret) { |
| BTFMSLIM_ERR("failed to write (%d) reg 0x%x", ret, reg); |
| goto error; |
| } |
| |
| enable_disable_txport: |
| /* Port enable */ |
| reg = CHRK_SB_PGD_PORT_TX_CFGN(port_num); |
| |
| enable_disable_rxport: |
| if (enable) |
| en = CHRK_SB_PGD_PORT_ENABLE; |
| else |
| en = CHRK_SB_PGD_PORT_DISABLE; |
| |
| if (is_fm_port(port_num)) |
| reg_val = en | CHRK_SB_PGD_PORT_WM_L8; |
| else if (port_num == CHRK_SB_PGD_PORT_TX_SCO) |
| reg_val = enable ? en | CHRK_SB_PGD_PORT_WM_L1 : en; |
| else |
| reg_val = enable ? en | CHRK_SB_PGD_PORT_WM_LB : en; |
| |
| if (enable && port_num == CHRK_SB_PGD_PORT_TX_SCO) |
| BTFMSLIM_INFO("programming SCO Tx with reg_val %d to reg 0x%x", |
| reg_val, reg); |
| |
| ret = btfm_slim_write(btfmslim, reg, 1, ®_val, IFD); |
| if (ret) |
| BTFMSLIM_ERR("failed to write (%d) reg 0x%x", ret, reg); |
| |
| error: |
| return ret; |
| } |