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