summaryrefslogtreecommitdiff
path: root/drivers/bluetooth/btfm_slim_wcn3990.c
blob: 7abd5598c47bc4dae73ba50180aef012e1af2ac9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
/* 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, &reg_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, &reg_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 port_bit = 0;
	uint16_t reg;

	BTFMSLIM_DBG("port(%d) enable(%d)", port_num, enable);

	if (rxport) {
		if (enable) {
			/* For SCO Rx, A2DP Rx */
			reg_val = 0x1;
			port_bit = port_num - 0x10;
			reg = CHRK_SB_PGD_RX_PORTn_MULTI_CHNL_0(port_bit);
			BTFMSLIM_DBG("writing reg_val (%d) to reg(%x)",
					reg_val, reg);
			ret = btfm_slim_write(btfmslim, reg, 1, &reg_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, &reg_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, &reg_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, &reg_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, &reg_val, IFD);
	if (ret)
		BTFMSLIM_ERR("failed to write (%d) reg 0x%x", ret, reg);

error:
	return ret;
}