Blame view

sound/soc/soc-io.c 5.45 KB
d1aaa2e68   Kuninori Morimoto   ASoC: soc-io.c: c...
1
2
3
4
5
6
7
  // SPDX-License-Identifier: GPL-2.0+
  //
  // soc-io.c  --  ASoC register I/O helpers
  //
  // Copyright 2009-2011 Wolfson Microelectronics PLC.
  //
  // Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
5bef44f9b   Mark Brown   ASoC: Move regist...
8
9
10
  
  #include <linux/i2c.h>
  #include <linux/spi/spi.h>
be3ea3b9e   Mark Brown   ASoC: Use new reg...
11
  #include <linux/regmap.h>
d81a6d717   Paul Gortmaker   sound: Add export...
12
  #include <linux/export.h>
5bef44f9b   Mark Brown   ASoC: Move regist...
13
  #include <sound/soc.h>
e2c330b9b   Lars-Peter Clausen   ASoC: Move IO abs...
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
  /**
   * snd_soc_component_read() - Read register value
   * @component: Component to read from
   * @reg: Register to read
   * @val: Pointer to where the read value is stored
   *
   * Return: 0 on success, a negative error code otherwise.
   */
  int snd_soc_component_read(struct snd_soc_component *component,
  	unsigned int reg, unsigned int *val)
  {
  	int ret;
  
  	if (component->regmap)
  		ret = regmap_read(component->regmap, reg, val);
d0ff8ba57   Kuninori Morimoto   ASoC: add Compone...
29
30
31
32
  	else if (component->driver->read) {
  		*val = component->driver->read(component, reg);
  		ret = 0;
  	}
e2c330b9b   Lars-Peter Clausen   ASoC: Move IO abs...
33
34
  	else
  		ret = -EIO;
e2c330b9b   Lars-Peter Clausen   ASoC: Move IO abs...
35
36
37
  	return ret;
  }
  EXPORT_SYMBOL_GPL(snd_soc_component_read);
738b49efe   Kuninori Morimoto   ASoC: add snd_soc...
38
39
40
41
42
43
44
45
46
47
48
49
50
  unsigned int snd_soc_component_read32(struct snd_soc_component *component,
  				      unsigned int reg)
  {
  	unsigned int val;
  	int ret;
  
  	ret = snd_soc_component_read(component, reg, &val);
  	if (ret < 0)
  		return -1;
  
  	return val;
  }
  EXPORT_SYMBOL_GPL(snd_soc_component_read32);
e2c330b9b   Lars-Peter Clausen   ASoC: Move IO abs...
51
52
53
54
55
56
57
58
59
60
  /**
   * snd_soc_component_write() - Write register value
   * @component: Component to write to
   * @reg: Register to write
   * @val: Value to write to the register
   *
   * Return: 0 on success, a negative error code otherwise.
   */
  int snd_soc_component_write(struct snd_soc_component *component,
  	unsigned int reg, unsigned int val)
96241c832   Lars-Peter Clausen   ASoC: Move IO fun...
61
  {
e2c330b9b   Lars-Peter Clausen   ASoC: Move IO abs...
62
63
  	if (component->regmap)
  		return regmap_write(component->regmap, reg, val);
d0ff8ba57   Kuninori Morimoto   ASoC: add Compone...
64
65
  	else if (component->driver->write)
  		return component->driver->write(component, reg, val);
e2c330b9b   Lars-Peter Clausen   ASoC: Move IO abs...
66
67
68
69
70
71
72
73
74
75
76
  	else
  		return -EIO;
  }
  EXPORT_SYMBOL_GPL(snd_soc_component_write);
  
  static int snd_soc_component_update_bits_legacy(
  	struct snd_soc_component *component, unsigned int reg,
  	unsigned int mask, unsigned int val, bool *change)
  {
  	unsigned int old, new;
  	int ret;
e2c330b9b   Lars-Peter Clausen   ASoC: Move IO abs...
77
  	mutex_lock(&component->io_mutex);
dead99e85   Peter Ujfalusi   ASoC: soc-io: Fix...
78
  	ret = snd_soc_component_read(component, reg, &old);
e2c330b9b   Lars-Peter Clausen   ASoC: Move IO abs...
79
80
81
82
83
84
  	if (ret < 0)
  		goto out_unlock;
  
  	new = (old & ~mask) | (val & mask);
  	*change = old != new;
  	if (*change)
dead99e85   Peter Ujfalusi   ASoC: soc-io: Fix...
85
  		ret = snd_soc_component_write(component, reg, new);
e2c330b9b   Lars-Peter Clausen   ASoC: Move IO abs...
86
87
  out_unlock:
  	mutex_unlock(&component->io_mutex);
96241c832   Lars-Peter Clausen   ASoC: Move IO fun...
88
89
90
  
  	return ret;
  }
e2c330b9b   Lars-Peter Clausen   ASoC: Move IO abs...
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
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
  
  /**
   * snd_soc_component_update_bits() - Perform read/modify/write cycle
   * @component: Component to update
   * @reg: Register to update
   * @mask: Mask that specifies which bits to update
   * @val: New value for the bits specified by mask
   *
   * Return: 1 if the operation was successful and the value of the register
   * changed, 0 if the operation was successful, but the value did not change.
   * Returns a negative error code otherwise.
   */
  int snd_soc_component_update_bits(struct snd_soc_component *component,
  	unsigned int reg, unsigned int mask, unsigned int val)
  {
  	bool change;
  	int ret;
  
  	if (component->regmap)
  		ret = regmap_update_bits_check(component->regmap, reg, mask,
  			val, &change);
  	else
  		ret = snd_soc_component_update_bits_legacy(component, reg,
  			mask, val, &change);
  
  	if (ret < 0)
  		return ret;
  	return change;
  }
  EXPORT_SYMBOL_GPL(snd_soc_component_update_bits);
  
  /**
   * snd_soc_component_update_bits_async() - Perform asynchronous
   *  read/modify/write cycle
   * @component: Component to update
   * @reg: Register to update
   * @mask: Mask that specifies which bits to update
   * @val: New value for the bits specified by mask
   *
   * This function is similar to snd_soc_component_update_bits(), but the update
   * operation is scheduled asynchronously. This means it may not be completed
   * when the function returns. To make sure that all scheduled updates have been
   * completed snd_soc_component_async_complete() must be called.
   *
   * Return: 1 if the operation was successful and the value of the register
   * changed, 0 if the operation was successful, but the value did not change.
   * Returns a negative error code otherwise.
   */
  int snd_soc_component_update_bits_async(struct snd_soc_component *component,
  	unsigned int reg, unsigned int mask, unsigned int val)
  {
  	bool change;
  	int ret;
  
  	if (component->regmap)
  		ret = regmap_update_bits_check_async(component->regmap, reg,
  			mask, val, &change);
  	else
  		ret = snd_soc_component_update_bits_legacy(component, reg,
  			mask, val, &change);
  
  	if (ret < 0)
  		return ret;
  	return change;
  }
  EXPORT_SYMBOL_GPL(snd_soc_component_update_bits_async);
  
  /**
   * snd_soc_component_async_complete() - Ensure asynchronous I/O has completed
   * @component: Component for which to wait
   *
   * This function blocks until all asynchronous I/O which has previously been
   * scheduled using snd_soc_component_update_bits_async() has completed.
   */
  void snd_soc_component_async_complete(struct snd_soc_component *component)
  {
  	if (component->regmap)
  		regmap_async_complete(component->regmap);
  }
  EXPORT_SYMBOL_GPL(snd_soc_component_async_complete);
  
  /**
   * snd_soc_component_test_bits - Test register for change
   * @component: component
   * @reg: Register to test
   * @mask: Mask that specifies which bits to test
   * @value: Value to test against
   *
   * Tests a register with a new value and checks if the new value is
   * different from the old value.
   *
   * Return: 1 for change, otherwise 0.
   */
  int snd_soc_component_test_bits(struct snd_soc_component *component,
  	unsigned int reg, unsigned int mask, unsigned int value)
  {
  	unsigned int old, new;
  	int ret;
  
  	ret = snd_soc_component_read(component, reg, &old);
  	if (ret < 0)
  		return ret;
  	new = (old & ~mask) | value;
  	return old != new;
  }
  EXPORT_SYMBOL_GPL(snd_soc_component_test_bits);