aboutsummaryrefslogtreecommitdiff
path: root/sound/soc/codecs/tfa9890_init.c
blob: 7b787276c82c3443591c9089e0f8d0c9241e3c08 (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
177
/*
* Copyright (c) 2012-2015, 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 "tfa_dsp_fw.h"
#include "tfa_service.h"
#include "tfa_internal.h"

#include "tfa98xx_tfafieldnames.h"

static enum Tfa98xx_Error tfa9890_specific(Tfa98xx_handle_t handle)
{
	enum Tfa98xx_Error error = Tfa98xx_Error_Ok;
	unsigned short regRead = 0;

	if (!tfa98xx_handle_is_open(handle))
		return Tfa98xx_Error_NotOpen;

	/* all i2C registers are already set to default for N1C2 */

	/* some PLL registers must be set optimal for amplifier behaviour
	 */
	error = tfa98xx_write_register16(handle, 0x40, 0x5a6b);
	if (error)
		return error;
	tfa98xx_read_register16(handle, 0x59, &regRead);
	regRead |= 0x3;
	tfa98xx_write_register16(handle, 0x59, regRead);
	error = tfa98xx_write_register16(handle, 0x40, 0x0000);

	error = tfa98xx_write_register16(handle, 0x47, 0x7BE1);

	return error;
}

/*
 * Tfa9890_DspSystemStable will compensate for the wrong behavior of CLKS
 * to determine if the DSP subsystem is ready for patch and config loading.
 *
 * A MTP calibration register is checked for non-zero.
 *
 * Note: This only works after i2c reset as this will clear the MTP contents.
 * When we are configured then the DSP communication will synchronize access.
 *
 */
static enum Tfa98xx_Error tfa9890_dsp_system_stable(Tfa98xx_handle_t handle, int *ready)
{
	enum Tfa98xx_Error error = Tfa98xx_Error_Ok;
	unsigned short status, mtp0;
	int result, tries;

	/* check the contents of the STATUS register */
	result = TFA_READ_REG(handle, AREFS);
	if (result < 0) {
		error = -result;
		goto errorExit;
	}
	status = (unsigned short)result;

	/* if AMPS is set then we were already configured and running
	 *   no need to check further
	 */
	*ready = (TFA_GET_BF_VALUE(handle, AMPS, status) == 1);
	if (*ready)		/* if  ready go back */
		return error;	/* will be Tfa98xx_Error_Ok */

	/* check AREFS and CLKS: not ready if either is clear */
	*ready = !((TFA_GET_BF_VALUE(handle, AREFS, status) == 0)
		   || (TFA_GET_BF_VALUE(handle, CLKS, status) == 0));
	if (!*ready)		/* if not ready go back */
		return error;	/* will be Tfa98xx_Error_Ok */

	/* check MTPB
	 *   mtpbusy will be active when the subsys copies MTP to I2C
	 *   2 times retry avoids catching this short mtpbusy active period
	 */
	for (tries = 2; tries > 0; tries--) {
		result = TFA_GET_BF(handle, MTPB);
		if (result < 0) {
			error = -result;
			goto errorExit;
		}
		status = (unsigned short)result;

		/* check the contents of the STATUS register */
		*ready = (result == 0);
		if (*ready)	/* if ready go on */
			break;
	}
	if (tries == 0)		/* ready will be 0 if retries exausted */
		return Tfa98xx_Error_Ok;

	/* check the contents of  MTP register for non-zero,
	 *  this indicates that the subsys is ready  */

	error = tfa98xx_read_register16(handle, 0x84, &mtp0);
	if (error)
		goto errorExit;

	*ready = (mtp0 != 0);	/* The MTP register written? */

	return error;

errorExit:
	*ready = 0;
	return error;
}

/*
 * The CurrentSense4 register is not in the datasheet, define local
 */
#define TFA98XX_CURRENTSENSE4_CTRL_CLKGATECFOFF (1<<2)
#define TFA98XX_CURRENTSENSE4 0x49
/*
 * Disable clock gating
 */
static enum Tfa98xx_Error tfa9890_clockgating(Tfa98xx_handle_t handle, int on)
{
	enum Tfa98xx_Error error;
	unsigned short value;

	/* for TFA9890 temporarily disable clock gating when dsp reset is used */
	error = tfa98xx_read_register16(handle, TFA98XX_CURRENTSENSE4, &value);
	if (error)
		return error;

	if (Tfa98xx_Error_Ok == error) {
		if (on)  /* clock gating on - clear the bit */
			value &= ~TFA98XX_CURRENTSENSE4_CTRL_CLKGATECFOFF;
		else  /* clock gating off - set the bit */
			value |= TFA98XX_CURRENTSENSE4_CTRL_CLKGATECFOFF;

		error = tfa98xx_write_register16(handle, TFA98XX_CURRENTSENSE4, value);
	}

	return error;
}

/*
 * Tfa9890_DspReset will deal with clock gating control in order
 * to reset the DSP for warm state restart
 */
static enum Tfa98xx_Error tfa9890_dsp_reset(Tfa98xx_handle_t handle, int state)
{
	enum Tfa98xx_Error error;

	/* for TFA9890 temporarily disable clock gating
	   when dsp reset is used */
	tfa9890_clockgating(handle, 0);

	TFA_SET_BF(handle, RST, (uint16_t)state);

	/* clock gating restore */
	error = tfa9890_clockgating(handle, 1);

	return error;
}

/*
 * register device specifics functions
 */
void tfa9890_ops(struct tfa_device_ops *ops)
{
	ops->tfa_init = tfa9890_specific;
	ops->tfa_dsp_reset = tfa9890_dsp_reset;
	ops->tfa_dsp_system_stable = tfa9890_dsp_system_stable;
}