xref: /linux/sound/soc/sdca/sdca_ump.c (revision 84318277d6334c6981ab326d4acc87c6a6ddc9b8)
1daab1085SCharles Keepax // SPDX-License-Identifier: GPL-2.0
2daab1085SCharles Keepax // Copyright (C) 2025 Cirrus Logic, Inc. and
3daab1085SCharles Keepax //                    Cirrus Logic International Semiconductor Ltd.
4daab1085SCharles Keepax 
5daab1085SCharles Keepax /*
6daab1085SCharles Keepax  * The MIPI SDCA specification is available for public downloads at
7daab1085SCharles Keepax  * https://www.mipi.org/mipi-sdca-v1-0-download
8daab1085SCharles Keepax  */
9daab1085SCharles Keepax 
10daab1085SCharles Keepax #include <linux/dev_printk.h>
11daab1085SCharles Keepax #include <linux/device.h>
12daab1085SCharles Keepax #include <linux/regmap.h>
13daab1085SCharles Keepax #include <sound/sdca.h>
14daab1085SCharles Keepax #include <sound/sdca_function.h>
15daab1085SCharles Keepax #include <sound/sdca_ump.h>
16daab1085SCharles Keepax #include <sound/soc-component.h>
17daab1085SCharles Keepax #include <linux/soundwire/sdw_registers.h>
18daab1085SCharles Keepax 
19daab1085SCharles Keepax /**
20daab1085SCharles Keepax  * sdca_ump_get_owner_host - check a UMP buffer is owned by the host
21daab1085SCharles Keepax  * @dev: Pointer to the struct device used for error messages.
22daab1085SCharles Keepax  * @function_regmap: Pointer to the regmap for the SDCA Function.
23daab1085SCharles Keepax  * @function: Pointer to the Function information.
24daab1085SCharles Keepax  * @entity: Pointer to the SDCA Entity.
25daab1085SCharles Keepax  * @control: Pointer to the SDCA Control for the UMP Owner.
26daab1085SCharles Keepax  *
27daab1085SCharles Keepax  * Return: Returns zero on success, and a negative error code on failure.
28daab1085SCharles Keepax  */
29daab1085SCharles Keepax int sdca_ump_get_owner_host(struct device *dev,
30daab1085SCharles Keepax 			    struct regmap *function_regmap,
31daab1085SCharles Keepax 			    struct sdca_function_data *function,
32daab1085SCharles Keepax 			    struct sdca_entity *entity,
33daab1085SCharles Keepax 			    struct sdca_control *control)
34daab1085SCharles Keepax {
35daab1085SCharles Keepax 	unsigned int reg, owner;
36daab1085SCharles Keepax 	int ret;
37daab1085SCharles Keepax 
38daab1085SCharles Keepax 	reg = SDW_SDCA_CTL(function->desc->adr, entity->id, control->sel, 0);
39daab1085SCharles Keepax 	ret = regmap_read(function_regmap, reg, &owner);
40daab1085SCharles Keepax 	if (ret < 0) {
41daab1085SCharles Keepax 		dev_err(dev, "%s: failed to read UMP owner: %d\n",
42daab1085SCharles Keepax 			entity->label, ret);
43daab1085SCharles Keepax 		return ret;
44daab1085SCharles Keepax 	}
45daab1085SCharles Keepax 
46daab1085SCharles Keepax 	if (owner != SDCA_UMP_OWNER_HOST) {
47daab1085SCharles Keepax 		dev_err(dev, "%s: host is not the UMP owner\n", entity->label);
48daab1085SCharles Keepax 		return -EINVAL;
49daab1085SCharles Keepax 	}
50daab1085SCharles Keepax 
51daab1085SCharles Keepax 	return 0;
52daab1085SCharles Keepax }
53daab1085SCharles Keepax EXPORT_SYMBOL_NS_GPL(sdca_ump_get_owner_host, "SND_SOC_SDCA");
54daab1085SCharles Keepax 
55daab1085SCharles Keepax /**
56daab1085SCharles Keepax  * sdca_ump_set_owner_device - set a UMP buffer's ownership back to the device
57daab1085SCharles Keepax  * @dev: Pointer to the struct device used for error messages.
58daab1085SCharles Keepax  * @function_regmap: Pointer to the regmap for the SDCA Function.
59daab1085SCharles Keepax  * @function: Pointer to the Function information.
60daab1085SCharles Keepax  * @entity: Pointer to the SDCA Entity.
61daab1085SCharles Keepax  * @control: Pointer to the SDCA Control for the UMP Owner.
62daab1085SCharles Keepax  *
63daab1085SCharles Keepax  * Return: Returns zero on success, and a negative error code on failure.
64daab1085SCharles Keepax  */
65daab1085SCharles Keepax int sdca_ump_set_owner_device(struct device *dev,
66daab1085SCharles Keepax 			      struct regmap *function_regmap,
67daab1085SCharles Keepax 			      struct sdca_function_data *function,
68daab1085SCharles Keepax 			      struct sdca_entity *entity,
69daab1085SCharles Keepax 			      struct sdca_control *control)
70daab1085SCharles Keepax {
71daab1085SCharles Keepax 	unsigned int reg;
72daab1085SCharles Keepax 	int ret;
73daab1085SCharles Keepax 
74daab1085SCharles Keepax 	reg = SDW_SDCA_CTL(function->desc->adr, entity->id, control->sel, 0);
75daab1085SCharles Keepax 	ret = regmap_write(function_regmap, reg, SDCA_UMP_OWNER_DEVICE);
76daab1085SCharles Keepax 	if (ret < 0)
77daab1085SCharles Keepax 		dev_err(dev, "%s: failed to write UMP owner: %d\n",
78daab1085SCharles Keepax 			entity->label, ret);
79daab1085SCharles Keepax 
80daab1085SCharles Keepax 	return ret;
81daab1085SCharles Keepax }
82daab1085SCharles Keepax EXPORT_SYMBOL_NS_GPL(sdca_ump_set_owner_device, "SND_SOC_SDCA");
83daab1085SCharles Keepax 
84daab1085SCharles Keepax /**
85daab1085SCharles Keepax  * sdca_ump_read_message - read a UMP message from the device
86daab1085SCharles Keepax  * @dev: Pointer to the struct device used for error messages.
87daab1085SCharles Keepax  * @device_regmap: Pointer to the Device register map.
88daab1085SCharles Keepax  * @function_regmap: Pointer to the regmap for the SDCA Function.
89daab1085SCharles Keepax  * @function: Pointer to the Function information.
90daab1085SCharles Keepax  * @entity: Pointer to the SDCA Entity.
91daab1085SCharles Keepax  * @offset_sel: Control Selector for the UMP Offset Control.
92daab1085SCharles Keepax  * @length_sel: Control Selector for the UMP Length Control.
93daab1085SCharles Keepax  * @msg: Pointer that will be populated with an dynamically buffer
94daab1085SCharles Keepax  * containing the UMP message. Note this needs to be freed by the
95daab1085SCharles Keepax  * caller.
96daab1085SCharles Keepax  *
97daab1085SCharles Keepax  * The caller should first call sdca_ump_get_owner_host() to ensure the host
98daab1085SCharles Keepax  * currently owns the UMP buffer, and then this function can be used to
99daab1085SCharles Keepax  * retrieve a message. It is the callers responsibility to free the
100daab1085SCharles Keepax  * message once it is finished with it. Finally sdca_ump_set_owner_device()
101daab1085SCharles Keepax  * should be called to return the buffer to the device.
102daab1085SCharles Keepax  *
103daab1085SCharles Keepax  * Return: Returns the message length on success, and a negative error
104daab1085SCharles Keepax  * code on failure.
105daab1085SCharles Keepax  */
106daab1085SCharles Keepax int sdca_ump_read_message(struct device *dev,
107daab1085SCharles Keepax 			  struct regmap *device_regmap,
108daab1085SCharles Keepax 			  struct regmap *function_regmap,
109daab1085SCharles Keepax 			  struct sdca_function_data *function,
110daab1085SCharles Keepax 			  struct sdca_entity *entity,
111daab1085SCharles Keepax 			  unsigned int offset_sel, unsigned int length_sel,
112daab1085SCharles Keepax 			  void **msg)
113daab1085SCharles Keepax {
114daab1085SCharles Keepax 	struct sdca_control_range *range;
115daab1085SCharles Keepax 	unsigned int msg_offset, msg_len;
116daab1085SCharles Keepax 	unsigned int buf_addr, buf_len;
117daab1085SCharles Keepax 	unsigned int reg;
118daab1085SCharles Keepax 	int ret;
119daab1085SCharles Keepax 
120daab1085SCharles Keepax 	reg = SDW_SDCA_CTL(function->desc->adr, entity->id, offset_sel, 0);
121daab1085SCharles Keepax 	ret = regmap_read(function_regmap, reg, &msg_offset);
122daab1085SCharles Keepax 	if (ret < 0) {
123daab1085SCharles Keepax 		dev_err(dev, "%s: failed to read UMP offset: %d\n",
124daab1085SCharles Keepax 			entity->label, ret);
125daab1085SCharles Keepax 		return ret;
126daab1085SCharles Keepax 	}
127daab1085SCharles Keepax 
128daab1085SCharles Keepax 	range = sdca_selector_find_range(dev, entity, offset_sel,
129daab1085SCharles Keepax 					 SDCA_MESSAGEOFFSET_NCOLS, 1);
130daab1085SCharles Keepax 	if (!range)
131daab1085SCharles Keepax 		return -ENOENT;
132daab1085SCharles Keepax 
133daab1085SCharles Keepax 	buf_addr = sdca_range(range, SDCA_MESSAGEOFFSET_BUFFER_START_ADDRESS, 0);
134daab1085SCharles Keepax 	buf_len = sdca_range(range, SDCA_MESSAGEOFFSET_BUFFER_LENGTH, 0);
135daab1085SCharles Keepax 
136daab1085SCharles Keepax 	reg = SDW_SDCA_CTL(function->desc->adr, entity->id, length_sel, 0);
137daab1085SCharles Keepax 	ret = regmap_read(function_regmap, reg, &msg_len);
138daab1085SCharles Keepax 	if (ret < 0) {
139daab1085SCharles Keepax 		dev_err(dev, "%s: failed to read UMP length: %d\n",
140daab1085SCharles Keepax 			entity->label, ret);
141daab1085SCharles Keepax 		return ret;
142daab1085SCharles Keepax 	}
143daab1085SCharles Keepax 
144daab1085SCharles Keepax 	if (msg_len > buf_len - msg_offset) {
145daab1085SCharles Keepax 		dev_err(dev, "%s: message too big for UMP buffer: %d\n",
146daab1085SCharles Keepax 			entity->label, msg_len);
147daab1085SCharles Keepax 		return -EINVAL;
148daab1085SCharles Keepax 	}
149daab1085SCharles Keepax 
150daab1085SCharles Keepax 	*msg = kmalloc(msg_len, GFP_KERNEL);
151daab1085SCharles Keepax 	if (!*msg)
152daab1085SCharles Keepax 		return -ENOMEM;
153daab1085SCharles Keepax 
154daab1085SCharles Keepax 	ret = regmap_raw_read(device_regmap, buf_addr + msg_offset, *msg, msg_len);
155daab1085SCharles Keepax 	if (ret < 0) {
156daab1085SCharles Keepax 		dev_err(dev, "%s: failed to read UMP message: %d\n",
157daab1085SCharles Keepax 			entity->label, ret);
158daab1085SCharles Keepax 		return ret;
159daab1085SCharles Keepax 	}
160daab1085SCharles Keepax 
161daab1085SCharles Keepax 	return msg_len;
162daab1085SCharles Keepax }
163daab1085SCharles Keepax EXPORT_SYMBOL_NS_GPL(sdca_ump_read_message, "SND_SOC_SDCA");
164daab1085SCharles Keepax 
165daab1085SCharles Keepax /**
166daab1085SCharles Keepax  * sdca_ump_write_message - write a UMP message to the device
167daab1085SCharles Keepax  * @dev: Pointer to the struct device used for error messages.
168daab1085SCharles Keepax  * @device_regmap: Pointer to the Device register map.
169daab1085SCharles Keepax  * @function_regmap: Pointer to the regmap for the SDCA Function.
170daab1085SCharles Keepax  * @function: Pointer to the Function information.
171daab1085SCharles Keepax  * @entity: Pointer to the SDCA Entity.
172daab1085SCharles Keepax  * @offset_sel: Control Selector for the UMP Offset Control.
173daab1085SCharles Keepax  * @msg_offset: Offset within the UMP buffer at which the message should
174daab1085SCharles Keepax  * be written.
175daab1085SCharles Keepax  * @length_sel: Control Selector for the UMP Length Control.
176daab1085SCharles Keepax  * @msg: Pointer to the data that should be written to the UMP buffer.
177daab1085SCharles Keepax  * @msg_len: Length of the message data in bytes.
178daab1085SCharles Keepax  *
179daab1085SCharles Keepax  * The caller should first call sdca_ump_get_owner_host() to ensure the host
180daab1085SCharles Keepax  * currently owns the UMP buffer, and then this function can be used to
181daab1085SCharles Keepax  * write a message. Finally sdca_ump_set_owner_device() should be called to
182daab1085SCharles Keepax  * return the buffer to the device, allowing the device to access the
183daab1085SCharles Keepax  * message.
184daab1085SCharles Keepax  *
185daab1085SCharles Keepax  * Return: Returns zero on success, and a negative error code on failure.
186daab1085SCharles Keepax  */
187daab1085SCharles Keepax int sdca_ump_write_message(struct device *dev,
188daab1085SCharles Keepax 			   struct regmap *device_regmap,
189daab1085SCharles Keepax 			   struct regmap *function_regmap,
190daab1085SCharles Keepax 			   struct sdca_function_data *function,
191daab1085SCharles Keepax 			   struct sdca_entity *entity,
192daab1085SCharles Keepax 			   unsigned int offset_sel, unsigned int msg_offset,
193daab1085SCharles Keepax 			   unsigned int length_sel,
194daab1085SCharles Keepax 			   void *msg, int msg_len)
195daab1085SCharles Keepax {
196daab1085SCharles Keepax 	struct sdca_control_range *range;
197daab1085SCharles Keepax 	unsigned int buf_addr, buf_len, ump_mode;
198daab1085SCharles Keepax 	unsigned int reg;
199daab1085SCharles Keepax 	int ret;
200daab1085SCharles Keepax 
201daab1085SCharles Keepax 	range = sdca_selector_find_range(dev, entity, offset_sel,
202daab1085SCharles Keepax 					 SDCA_MESSAGEOFFSET_NCOLS, 1);
203daab1085SCharles Keepax 	if (!range)
204daab1085SCharles Keepax 		return -ENOENT;
205daab1085SCharles Keepax 
206daab1085SCharles Keepax 	buf_addr = sdca_range(range, SDCA_MESSAGEOFFSET_BUFFER_START_ADDRESS, 0);
207daab1085SCharles Keepax 	buf_len = sdca_range(range, SDCA_MESSAGEOFFSET_BUFFER_LENGTH, 0);
208daab1085SCharles Keepax 	ump_mode = sdca_range(range, SDCA_MESSAGEOFFSET_UMP_MODE, 0);
209daab1085SCharles Keepax 
210daab1085SCharles Keepax 	if (msg_len > buf_len - msg_offset) {
211daab1085SCharles Keepax 		dev_err(dev, "%s: message too big for UMP buffer: %d\n",
212daab1085SCharles Keepax 			entity->label, msg_len);
213daab1085SCharles Keepax 		return -EINVAL;
214daab1085SCharles Keepax 	}
215daab1085SCharles Keepax 
216daab1085SCharles Keepax 	if (ump_mode != SDCA_UMP_MODE_DIRECT) {
217daab1085SCharles Keepax 		dev_err(dev, "%s: only direct mode currently supported\n",
218daab1085SCharles Keepax 			entity->label);
219daab1085SCharles Keepax 		return -EINVAL;
220daab1085SCharles Keepax 	}
221daab1085SCharles Keepax 
222daab1085SCharles Keepax 	ret = regmap_raw_write(device_regmap, buf_addr + msg_offset, msg, msg_len);
223daab1085SCharles Keepax 	if (ret) {
224daab1085SCharles Keepax 		dev_err(dev, "%s: failed to write UMP message: %d\n",
225daab1085SCharles Keepax 			entity->label, ret);
226daab1085SCharles Keepax 		return ret;
227daab1085SCharles Keepax 	}
228daab1085SCharles Keepax 
229daab1085SCharles Keepax 	reg = SDW_SDCA_CTL(function->desc->adr, entity->id, offset_sel, 0);
230daab1085SCharles Keepax 	ret = regmap_write(function_regmap, reg, msg_offset);
231daab1085SCharles Keepax 	if (ret < 0) {
232daab1085SCharles Keepax 		dev_err(dev, "%s: failed to write UMP offset: %d\n",
233daab1085SCharles Keepax 			entity->label, ret);
234daab1085SCharles Keepax 		return ret;
235daab1085SCharles Keepax 	}
236daab1085SCharles Keepax 
237daab1085SCharles Keepax 	reg = SDW_SDCA_CTL(function->desc->adr, entity->id, length_sel, 0);
238daab1085SCharles Keepax 	ret = regmap_write(function_regmap, reg, msg_len);
239daab1085SCharles Keepax 	if (ret < 0) {
240daab1085SCharles Keepax 		dev_err(dev, "%s: failed to write UMP length: %d\n",
241daab1085SCharles Keepax 			entity->label, ret);
242daab1085SCharles Keepax 		return ret;
243daab1085SCharles Keepax 	}
244daab1085SCharles Keepax 
245daab1085SCharles Keepax 	return 0;
246daab1085SCharles Keepax }
247daab1085SCharles Keepax EXPORT_SYMBOL_NS_GPL(sdca_ump_write_message, "SND_SOC_SDCA");
248*e92e25f7SCharles Keepax 
249*e92e25f7SCharles Keepax void sdca_ump_cancel_timeout(struct delayed_work *work)
250*e92e25f7SCharles Keepax {
251*e92e25f7SCharles Keepax 	cancel_delayed_work_sync(work);
252*e92e25f7SCharles Keepax }
253*e92e25f7SCharles Keepax EXPORT_SYMBOL_NS_GPL(sdca_ump_cancel_timeout, "SND_SOC_SDCA");
254*e92e25f7SCharles Keepax 
255*e92e25f7SCharles Keepax void sdca_ump_schedule_timeout(struct delayed_work *work, unsigned int timeout_us)
256*e92e25f7SCharles Keepax {
257*e92e25f7SCharles Keepax 	if (!timeout_us)
258*e92e25f7SCharles Keepax 		return;
259*e92e25f7SCharles Keepax 
260*e92e25f7SCharles Keepax 	queue_delayed_work(system_wq, work, usecs_to_jiffies(timeout_us));
261*e92e25f7SCharles Keepax }
262*e92e25f7SCharles Keepax EXPORT_SYMBOL_NS_GPL(sdca_ump_schedule_timeout, "SND_SOC_SDCA");
263