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