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