Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Add SDCA UMP/FDL support

Merge series from Charles Keepax <ckeepax@opensource.cirrus.com>:

Next installment of the SDCA changes, hopefully the next series after
this should be the full class driver. It is worth noting this series has
a build dependency on a patch working its way through the PM/ACPI tree:

commit ac46f5b6c661 ("ACPICA: Add SoundWire File Table (SWFT) signature")
git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm.git

But we can probably worry about that later, as normally there is a
reasonable amount of review on these SDCA series'.

This series broadly breaks down into 3 chunks, first there are several
changes to remove the assumption that the struct device used for SDCA
purposes represents the SoundWire slave. This is because the SDCA class
driver will be made of an auxiliary driver for each SDCA Function, thus
the SoundWire slave will be on the parent device for each individual
driver. Then there are patches to add support for UMP/FDL. And then
finally since the rest of the HID support is there and UMP was the last
missing part required a small patch to add a function to allow reporting
of HID events from SDCA devices.

+1573 -107
+12 -11
drivers/base/regmap/regmap-sdw-mbq.c
··· 15 15 16 16 struct regmap_mbq_context { 17 17 struct device *dev; 18 + struct sdw_slave *sdw; 18 19 19 20 struct regmap_sdw_mbq_cfg cfg; 20 21 ··· 47 46 static int regmap_sdw_mbq_poll_busy(struct sdw_slave *slave, unsigned int reg, 48 47 struct regmap_mbq_context *ctx) 49 48 { 50 - struct device *dev = &slave->dev; 49 + struct device *dev = ctx->dev; 51 50 int val, ret = 0; 52 51 53 52 dev_dbg(dev, "Deferring transaction for 0x%x\n", reg); ··· 97 96 static int regmap_sdw_mbq_write(void *context, unsigned int reg, unsigned int val) 98 97 { 99 98 struct regmap_mbq_context *ctx = context; 100 - struct device *dev = ctx->dev; 101 - struct sdw_slave *slave = dev_to_sdw_dev(dev); 99 + struct sdw_slave *slave = ctx->sdw; 102 100 bool deferrable = regmap_sdw_mbq_deferrable(ctx, reg); 103 101 int mbq_size = regmap_sdw_mbq_size(ctx, reg); 104 102 int ret; ··· 156 156 static int regmap_sdw_mbq_read(void *context, unsigned int reg, unsigned int *val) 157 157 { 158 158 struct regmap_mbq_context *ctx = context; 159 - struct device *dev = ctx->dev; 160 - struct sdw_slave *slave = dev_to_sdw_dev(dev); 159 + struct sdw_slave *slave = ctx->sdw; 161 160 bool deferrable = regmap_sdw_mbq_deferrable(ctx, reg); 162 161 int mbq_size = regmap_sdw_mbq_size(ctx, reg); 163 162 int ret; ··· 207 208 208 209 static struct regmap_mbq_context * 209 210 regmap_sdw_mbq_gen_context(struct device *dev, 211 + struct sdw_slave *sdw, 210 212 const struct regmap_config *config, 211 213 const struct regmap_sdw_mbq_cfg *mbq_config) 212 214 { ··· 218 218 return ERR_PTR(-ENOMEM); 219 219 220 220 ctx->dev = dev; 221 + ctx->sdw = sdw; 221 222 222 223 if (mbq_config) 223 224 ctx->cfg = *mbq_config; ··· 229 228 return ctx; 230 229 } 231 230 232 - struct regmap *__regmap_init_sdw_mbq(struct sdw_slave *sdw, 231 + struct regmap *__regmap_init_sdw_mbq(struct device *dev, struct sdw_slave *sdw, 233 232 const struct regmap_config *config, 234 233 const struct regmap_sdw_mbq_cfg *mbq_config, 235 234 struct lock_class_key *lock_key, ··· 242 241 if (ret) 243 242 return ERR_PTR(ret); 244 243 245 - ctx = regmap_sdw_mbq_gen_context(&sdw->dev, config, mbq_config); 244 + ctx = regmap_sdw_mbq_gen_context(dev, sdw, config, mbq_config); 246 245 if (IS_ERR(ctx)) 247 246 return ERR_CAST(ctx); 248 247 249 - return __regmap_init(&sdw->dev, &regmap_sdw_mbq, ctx, 248 + return __regmap_init(dev, &regmap_sdw_mbq, ctx, 250 249 config, lock_key, lock_name); 251 250 } 252 251 EXPORT_SYMBOL_GPL(__regmap_init_sdw_mbq); 253 252 254 - struct regmap *__devm_regmap_init_sdw_mbq(struct sdw_slave *sdw, 253 + struct regmap *__devm_regmap_init_sdw_mbq(struct device *dev, struct sdw_slave *sdw, 255 254 const struct regmap_config *config, 256 255 const struct regmap_sdw_mbq_cfg *mbq_config, 257 256 struct lock_class_key *lock_key, ··· 264 263 if (ret) 265 264 return ERR_PTR(ret); 266 265 267 - ctx = regmap_sdw_mbq_gen_context(&sdw->dev, config, mbq_config); 266 + ctx = regmap_sdw_mbq_gen_context(dev, sdw, config, mbq_config); 268 267 if (IS_ERR(ctx)) 269 268 return ERR_CAST(ctx); 270 269 271 - return __devm_regmap_init(&sdw->dev, &regmap_sdw_mbq, ctx, 270 + return __devm_regmap_init(dev, &regmap_sdw_mbq, ctx, 272 271 config, lock_key, lock_name); 273 272 } 274 273 EXPORT_SYMBOL_GPL(__devm_regmap_init_sdw_mbq);
+11 -10
include/linux/regmap.h
··· 676 676 const struct regmap_config *config, 677 677 struct lock_class_key *lock_key, 678 678 const char *lock_name); 679 - struct regmap *__regmap_init_sdw_mbq(struct sdw_slave *sdw, 679 + struct regmap *__regmap_init_sdw_mbq(struct device *dev, struct sdw_slave *sdw, 680 680 const struct regmap_config *config, 681 681 const struct regmap_sdw_mbq_cfg *mbq_config, 682 682 struct lock_class_key *lock_key, ··· 738 738 const struct regmap_config *config, 739 739 struct lock_class_key *lock_key, 740 740 const char *lock_name); 741 - struct regmap *__devm_regmap_init_sdw_mbq(struct sdw_slave *sdw, 741 + struct regmap *__devm_regmap_init_sdw_mbq(struct device *dev, struct sdw_slave *sdw, 742 742 const struct regmap_config *config, 743 743 const struct regmap_sdw_mbq_cfg *mbq_config, 744 744 struct lock_class_key *lock_key, ··· 970 970 */ 971 971 #define regmap_init_sdw_mbq(sdw, config) \ 972 972 __regmap_lockdep_wrapper(__regmap_init_sdw_mbq, #config, \ 973 - sdw, config, NULL) 973 + &sdw->dev, sdw, config, NULL) 974 974 975 975 /** 976 976 * regmap_init_sdw_mbq_cfg() - Initialise MBQ SDW register map with config ··· 983 983 * to a struct regmap. The regmap will be automatically freed by the 984 984 * device management code. 985 985 */ 986 - #define regmap_init_sdw_mbq_cfg(sdw, config, mbq_config) \ 986 + #define regmap_init_sdw_mbq_cfg(dev, sdw, config, mbq_config) \ 987 987 __regmap_lockdep_wrapper(__regmap_init_sdw_mbq, #config, \ 988 - sdw, config, mbq_config) 988 + dev, sdw, config, mbq_config) 989 989 990 990 /** 991 991 * regmap_init_spi_avmm() - Initialize register map for Intel SPI Slave ··· 1198 1198 */ 1199 1199 #define devm_regmap_init_sdw_mbq(sdw, config) \ 1200 1200 __regmap_lockdep_wrapper(__devm_regmap_init_sdw_mbq, #config, \ 1201 - sdw, config, NULL) 1201 + &sdw->dev, sdw, config, NULL) 1202 1202 1203 1203 /** 1204 1204 * devm_regmap_init_sdw_mbq_cfg() - Initialise managed MBQ SDW register map with config 1205 1205 * 1206 - * @sdw: Device that will be interacted with 1206 + * @dev: Device that will be interacted with 1207 + * @sdw: SoundWire Device that will be interacted with 1207 1208 * @config: Configuration for register map 1208 1209 * @mbq_config: Properties for the MBQ registers 1209 1210 * ··· 1212 1211 * to a struct regmap. The regmap will be automatically freed by the 1213 1212 * device management code. 1214 1213 */ 1215 - #define devm_regmap_init_sdw_mbq_cfg(sdw, config, mbq_config) \ 1216 - __regmap_lockdep_wrapper(__devm_regmap_init_sdw_mbq, \ 1217 - #config, sdw, config, mbq_config) 1214 + #define devm_regmap_init_sdw_mbq_cfg(dev, sdw, config, mbq_config) \ 1215 + __regmap_lockdep_wrapper(__devm_regmap_init_sdw_mbq, \ 1216 + #config, dev, sdw, config, mbq_config) 1218 1217 1219 1218 /** 1220 1219 * devm_regmap_init_slimbus() - Initialise managed register map
+5
include/sound/sdca.h
··· 12 12 #include <linux/types.h> 13 13 #include <linux/kconfig.h> 14 14 15 + struct acpi_table_swft; 15 16 struct sdw_slave; 16 17 17 18 #define SDCA_MAX_FUNCTION_COUNT 8 ··· 38 37 * @num_functions: Total number of supported SDCA functions. Invalid/unsupported 39 38 * functions will be skipped. 40 39 * @function: Array of function descriptors. 40 + * @swft: Pointer to the SWFT table, if available. 41 41 */ 42 42 struct sdca_device_data { 43 43 u32 interface_revision; 44 44 int num_functions; 45 45 struct sdca_function_desc function[SDCA_MAX_FUNCTION_COUNT]; 46 + struct acpi_table_swft *swft; 46 47 }; 47 48 48 49 enum sdca_quirk { ··· 55 52 #if IS_ENABLED(CONFIG_ACPI) && IS_ENABLED(CONFIG_SND_SOC_SDCA) 56 53 57 54 void sdca_lookup_functions(struct sdw_slave *slave); 55 + void sdca_lookup_swft(struct sdw_slave *slave); 58 56 void sdca_lookup_interface_revision(struct sdw_slave *slave); 59 57 bool sdca_device_quirk_match(struct sdw_slave *slave, enum sdca_quirk quirk); 60 58 61 59 #else 62 60 63 61 static inline void sdca_lookup_functions(struct sdw_slave *slave) {} 62 + static inline void sdca_lookup_swft(struct sdw_slave *slave) {} 64 63 static inline void sdca_lookup_interface_revision(struct sdw_slave *slave) {} 65 64 static inline bool sdca_device_quirk_match(struct sdw_slave *slave, enum sdca_quirk quirk) 66 65 {
+75
include/sound/sdca_fdl.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + /* 3 + * The MIPI SDCA specification is available for public downloads at 4 + * https://www.mipi.org/mipi-sdca-v1-0-download 5 + * 6 + * Copyright (C) 2025 Cirrus Logic, Inc. and 7 + * Cirrus Logic International Semiconductor Ltd. 8 + */ 9 + 10 + #ifndef __SDCA_FDL_H__ 11 + #define __SDCA_FDL_H__ 12 + 13 + #include <linux/completion.h> 14 + #include <linux/workqueue.h> 15 + 16 + struct device; 17 + struct regmap; 18 + struct sdca_fdl_set; 19 + struct sdca_function_data; 20 + struct sdca_interrupt; 21 + struct sdca_interrupt_info; 22 + 23 + /** 24 + * struct fdl_state - FDL state structure to keep data between interrupts 25 + * @begin: Completion indicating the start of an FDL download cycle. 26 + * @done: Completion indicating the end of an FDL download cycle. 27 + * @timeout: Delayed work used for timing out UMP transactions. 28 + * @lock: Mutex to protect between the timeout work and IRQ handlers. 29 + * @interrupt: Pointer to the interrupt struct to which this FDL is attached. 30 + * @set: Pointer to the FDL set currently being downloaded. 31 + * @file_index: Index of the current file being processed. 32 + */ 33 + struct fdl_state { 34 + struct completion begin; 35 + struct completion done; 36 + struct delayed_work timeout; 37 + struct mutex lock; 38 + 39 + struct sdca_interrupt *interrupt; 40 + struct sdca_fdl_set *set; 41 + int file_index; 42 + }; 43 + 44 + #define SDCA_CTL_XU_FDLH_COMPLETE 0 45 + #define SDCA_CTL_XU_FDLH_MORE_FILES SDCA_CTL_XU_FDLH_SET_IN_PROGRESS 46 + #define SDCA_CTL_XU_FDLH_FILE_AVAILABLE (SDCA_CTL_XU_FDLH_TRANSFERRED_FILE | \ 47 + SDCA_CTL_XU_FDLH_SET_IN_PROGRESS) 48 + #define SDCA_CTL_XU_FDLH_MASK (SDCA_CTL_XU_FDLH_TRANSFERRED_CHUNK | \ 49 + SDCA_CTL_XU_FDLH_TRANSFERRED_FILE | \ 50 + SDCA_CTL_XU_FDLH_SET_IN_PROGRESS | \ 51 + SDCA_CTL_XU_FDLH_RESET_ACK | \ 52 + SDCA_CTL_XU_FDLH_REQ_ABORT) 53 + 54 + #define SDCA_CTL_XU_FDLD_COMPLETE 0 55 + #define SDCA_CTL_XU_FDLD_FILE_OK (SDCA_CTL_XU_FDLH_TRANSFERRED_FILE | \ 56 + SDCA_CTL_XU_FDLH_SET_IN_PROGRESS | \ 57 + SDCA_CTL_XU_FDLD_ACK_TRANSFER | \ 58 + SDCA_CTL_XU_FDLD_NEEDS_SET) 59 + #define SDCA_CTL_XU_FDLD_MORE_FILES_OK (SDCA_CTL_XU_FDLH_SET_IN_PROGRESS | \ 60 + SDCA_CTL_XU_FDLD_ACK_TRANSFER | \ 61 + SDCA_CTL_XU_FDLD_NEEDS_SET) 62 + #define SDCA_CTL_XU_FDLD_MASK (SDCA_CTL_XU_FDLD_REQ_RESET | \ 63 + SDCA_CTL_XU_FDLD_REQ_ABORT | \ 64 + SDCA_CTL_XU_FDLD_ACK_TRANSFER | \ 65 + SDCA_CTL_XU_FDLD_NEEDS_SET) 66 + 67 + int sdca_fdl_alloc_state(struct sdca_interrupt *interrupt); 68 + int sdca_fdl_process(struct sdca_interrupt *interrupt); 69 + int sdca_fdl_sync(struct device *dev, struct sdca_function_data *function, 70 + struct sdca_interrupt_info *info); 71 + 72 + int sdca_reset_function(struct device *dev, struct sdca_function_data *function, 73 + struct regmap *regmap); 74 + 75 + #endif // __SDCA_FDL_H__
+118 -1
include/sound/sdca_function.h
··· 13 13 #include <linux/types.h> 14 14 #include <linux/hid.h> 15 15 16 + struct acpi_table_swft; 16 17 struct device; 17 18 struct sdca_entity; 18 19 struct sdca_function_desc; ··· 133 132 */ 134 133 #define SDCA_CTL_TYPE_S(ent, sel) SDCA_CTL_TYPE(SDCA_ENTITY_TYPE_##ent, \ 135 134 SDCA_CTL_##ent##_##sel) 135 + 136 + /** 137 + * enum sdca_messageoffset_range - Column definitions UMP MessageOffset 138 + */ 139 + enum sdca_messageoffset_range { 140 + SDCA_MESSAGEOFFSET_BUFFER_START_ADDRESS = 0, 141 + SDCA_MESSAGEOFFSET_BUFFER_LENGTH = 1, 142 + SDCA_MESSAGEOFFSET_UMP_MODE = 2, 143 + SDCA_MESSAGEOFFSET_NCOLS = 3, 144 + }; 145 + 146 + /** 147 + * enum sdca_ump_mode - SDCA UMP Mode 148 + */ 149 + enum sdca_ump_mode { 150 + SDCA_UMP_MODE_DIRECT = 0x00, 151 + SDCA_UMP_MODE_INDIRECT = 0x01, 152 + }; 153 + 154 + /** 155 + * enum sdca_ump_owner - SDCA UMP Owner 156 + */ 157 + enum sdca_ump_owner { 158 + SDCA_UMP_OWNER_HOST = 0x00, 159 + SDCA_UMP_OWNER_DEVICE = 0x01, 160 + }; 136 161 137 162 /** 138 163 * enum sdca_it_controls - SDCA Controls for Input Terminal ··· 285 258 SDCA_CTL_XU_FDL_STATUS = 0x14, 286 259 SDCA_CTL_XU_FDL_SET_INDEX = 0x15, 287 260 SDCA_CTL_XU_FDL_HOST_REQUEST = 0x16, 261 + 262 + /* FDL Status Host->Device bit definitions */ 263 + SDCA_CTL_XU_FDLH_TRANSFERRED_CHUNK = BIT(0), 264 + SDCA_CTL_XU_FDLH_TRANSFERRED_FILE = BIT(1), 265 + SDCA_CTL_XU_FDLH_SET_IN_PROGRESS = BIT(2), 266 + SDCA_CTL_XU_FDLH_RESET_ACK = BIT(4), 267 + SDCA_CTL_XU_FDLH_REQ_ABORT = BIT(5), 268 + /* FDL Status Device->Host bit definitions */ 269 + SDCA_CTL_XU_FDLD_REQ_RESET = BIT(4), 270 + SDCA_CTL_XU_FDLD_REQ_ABORT = BIT(5), 271 + SDCA_CTL_XU_FDLD_ACK_TRANSFER = BIT(6), 272 + SDCA_CTL_XU_FDLD_NEEDS_SET = BIT(7), 273 + }; 274 + 275 + /** 276 + * enum sdca_set_index_range - Column definitions UMP SetIndex 277 + */ 278 + enum sdca_fdl_set_index_range { 279 + SDCA_FDL_SET_INDEX_SET_NUMBER = 0, 280 + SDCA_FDL_SET_INDEX_FILE_SET_ID = 1, 281 + SDCA_FDL_SET_INDEX_NCOLS = 2, 288 282 }; 289 283 290 284 /** ··· 590 542 SDCA_CTL_ENTITY_0_FUNCTION_NEEDS_INITIALIZATION = BIT(5), 591 543 SDCA_CTL_ENTITY_0_FUNCTION_HAS_BEEN_RESET = BIT(6), 592 544 SDCA_CTL_ENTITY_0_FUNCTION_BUSY = BIT(7), 545 + 546 + /* Function Action Bits */ 547 + SDCA_CTL_ENTITY_0_RESET_FUNCTION_NOW = BIT(0), 593 548 }; 594 549 595 550 #define SDCA_CTL_MIC_BIAS_NAME "Mic Bias" ··· 822 771 u8 layers; 823 772 824 773 bool deferrable; 774 + bool is_volatile; 825 775 bool has_default; 826 776 bool has_fixed; 827 777 }; ··· 1142 1090 }; 1143 1091 1144 1092 /** 1093 + * enum sdca_xu_reset_machanism - SDCA FDL Resets 1094 + */ 1095 + enum sdca_xu_reset_mechanism { 1096 + SDCA_XU_RESET_FUNCTION = 0x0, 1097 + SDCA_XU_RESET_DEVICE = 0x1, 1098 + SDCA_XU_RESET_BUS = 0x2, 1099 + }; 1100 + 1101 + /** 1102 + * struct sdca_entity_xu - information specific to XU Entities 1103 + * @max_delay: the maximum time in microseconds allowed for the Device 1104 + * to change the ownership from Device to Host 1105 + * @reset_mechanism: indicates the type of reset that can be requested 1106 + * the end of an FDL. 1107 + */ 1108 + struct sdca_entity_xu { 1109 + unsigned int max_delay; 1110 + enum sdca_xu_reset_mechanism reset_mechanism; 1111 + }; 1112 + 1113 + /** 1145 1114 * struct sdca_entity - information for one SDCA Entity 1146 1115 * @label: String such as "OT 12". 1147 1116 * @id: Identifier used for addressing. ··· 1178 1105 * @pde: Power Domain Entity specific Entity properties. 1179 1106 * @ge: Group Entity specific Entity properties. 1180 1107 * @hide: HIDE Entity specific Entity properties. 1108 + * @xu: XU Entity specific Entity properties. 1181 1109 */ 1182 1110 struct sdca_entity { 1183 1111 const char *label; ··· 1196 1122 struct sdca_entity_pde pde; 1197 1123 struct sdca_entity_ge ge; 1198 1124 struct sdca_entity_hide hide; 1125 + struct sdca_entity_xu xu; 1199 1126 }; 1200 1127 }; 1201 1128 ··· 1364 1289 }; 1365 1290 1366 1291 /** 1292 + * struct sdca_fdl_file - information about a file from a fileset used in FDL 1293 + * @vendor_id: Vendor ID of the file. 1294 + * @file_id: File ID of the file. 1295 + * @fdl_offset: Offset information for FDL. 1296 + */ 1297 + struct sdca_fdl_file { 1298 + u16 vendor_id; 1299 + u32 file_id; 1300 + u32 fdl_offset; 1301 + }; 1302 + 1303 + /** 1304 + * struct sdca_fdl_set - information about a set of files used in FDL 1305 + * @files: Array of files in this FDL set. 1306 + * @num_files: Number of files in this FDL set. 1307 + * @id: ID of the FDL set. 1308 + */ 1309 + struct sdca_fdl_set { 1310 + struct sdca_fdl_file *files; 1311 + int num_files; 1312 + u32 id; 1313 + }; 1314 + 1315 + /** 1316 + * struct sdca_fdl_data - information about a function's FDL data 1317 + * @swft: Pointer to the SoundWire File Table. 1318 + * @sets: Array of FDL sets used by this function. 1319 + * @num_sets: Number of FDL sets used by this function. 1320 + */ 1321 + struct sdca_fdl_data { 1322 + struct acpi_table_swft *swft; 1323 + struct sdca_fdl_set *sets; 1324 + int num_sets; 1325 + }; 1326 + 1327 + /** 1367 1328 * struct sdca_function_data - top-level information for one SDCA function 1368 1329 * @desc: Pointer to short descriptor from initial parsing. 1369 1330 * @init_table: Pointer to a table of initialization writes. ··· 1410 1299 * @num_clusters: Number of Channel Clusters reported in this Function. 1411 1300 * @busy_max_delay: Maximum Function busy delay in microseconds, before an 1412 1301 * error should be reported. 1302 + * @reset_max_delay: Maximum Function reset delay in microseconds, before an 1303 + * error should be reported. 1304 + * @fdl_data: FDL data for this Function, if available. 1413 1305 */ 1414 1306 struct sdca_function_data { 1415 1307 struct sdca_function_desc *desc; ··· 1425 1311 int num_clusters; 1426 1312 1427 1313 unsigned int busy_max_delay; 1314 + unsigned int reset_max_delay; 1315 + 1316 + struct sdca_fdl_data fdl_data; 1428 1317 }; 1429 1318 1430 1319 static inline u32 sdca_range(struct sdca_control_range *range, ··· 1449 1332 return 0; 1450 1333 } 1451 1334 1452 - int sdca_parse_function(struct device *dev, 1335 + int sdca_parse_function(struct device *dev, struct sdw_slave *sdw, 1453 1336 struct sdca_function_desc *desc, 1454 1337 struct sdca_function_data *function); 1455 1338
+17 -4
include/sound/sdca_hid.h
··· 8 8 #ifndef __SDCA_HID_H__ 9 9 #define __SDCA_HID_H__ 10 10 11 - #include <linux/types.h> 12 - #include <linux/hid.h> 11 + struct device; 12 + struct sdw_slave; 13 + 14 + struct sdca_entity; 15 + struct sdca_interrupt; 13 16 14 17 #if IS_ENABLED(CONFIG_SND_SOC_SDCA_HID) 15 - int sdca_add_hid_device(struct device *dev, struct sdca_entity *entity); 18 + 19 + int sdca_add_hid_device(struct device *dev, struct sdw_slave *sdw, 20 + struct sdca_entity *entity); 21 + int sdca_hid_process_report(struct sdca_interrupt *interrupt); 16 22 17 23 #else 18 - static inline int sdca_add_hid_device(struct device *dev, struct sdca_entity *entity) 24 + 25 + static inline int sdca_add_hid_device(struct device *dev, struct sdw_slave *sdw, 26 + struct sdca_entity *entity) 27 + { 28 + return 0; 29 + } 30 + 31 + static inline int sdca_hid_process_report(struct sdca_interrupt *interrupt) 19 32 { 20 33 return 0; 21 34 }
+14 -5
include/sound/sdca_interrupts.h
··· 23 23 /** 24 24 * struct sdca_interrupt - contains information about a single SDCA interrupt 25 25 * @name: The name of the interrupt. 26 + * @dev: Pointer to the Function device. 27 + * @device_regmap: Pointer to the IRQ regmap. 28 + * @function_regmap: Pointer to the SDCA Function regmap. 26 29 * @component: Pointer to the ASoC component owns the interrupt. 27 30 * @function: Pointer to the Function that the interrupt is associated with. 28 31 * @entity: Pointer to the Entity that the interrupt is associated with. 29 32 * @control: Pointer to the Control that the interrupt is associated with. 30 33 * @priv: Pointer to private data for use by the handler. 31 - * @externally_requested: Internal flag used to check if a client driver has 32 - * already requested the interrupt, for custom handling, allowing the core to 33 - * skip handling this interrupt. 34 + * @irq: IRQ number allocated to this interrupt, also used internally to track 35 + * the IRQ being assigned. 34 36 */ 35 37 struct sdca_interrupt { 36 38 const char *name; 37 39 40 + struct device *dev; 41 + struct regmap *device_regmap; 42 + struct regmap *function_regmap; 38 43 struct snd_soc_component *component; 39 44 struct sdca_function_data *function; 40 45 struct sdca_entity *entity; ··· 47 42 48 43 void *priv; 49 44 50 - bool externally_requested; 45 + int irq; 51 46 }; 52 47 53 48 /** ··· 69 64 int sdca_irq_request(struct device *dev, struct sdca_interrupt_info *interrupt_info, 70 65 int sdca_irq, const char *name, irq_handler_t handler, 71 66 void *data); 72 - int sdca_irq_data_populate(struct snd_soc_component *component, 67 + int sdca_irq_data_populate(struct device *dev, struct regmap *function_regmap, 68 + struct snd_soc_component *component, 73 69 struct sdca_function_data *function, 74 70 struct sdca_entity *entity, 75 71 struct sdca_control *control, 76 72 struct sdca_interrupt *interrupt); 73 + int sdca_irq_populate_early(struct device *dev, struct regmap *function_regmap, 74 + struct sdca_function_data *function, 75 + struct sdca_interrupt_info *info); 77 76 int sdca_irq_populate(struct sdca_function_data *function, 78 77 struct snd_soc_component *component, 79 78 struct sdca_interrupt_info *info);
+50
include/sound/sdca_ump.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + /* 3 + * The MIPI SDCA specification is available for public downloads at 4 + * https://www.mipi.org/mipi-sdca-v1-0-download 5 + * 6 + * Copyright (C) 2025 Cirrus Logic, Inc. and 7 + * Cirrus Logic International Semiconductor Ltd. 8 + */ 9 + 10 + #ifndef __SDCA_UMP_H__ 11 + #define __SDCA_UMP_H__ 12 + 13 + struct regmap; 14 + struct sdca_control; 15 + struct sdca_entity; 16 + struct sdca_function_data; 17 + struct snd_soc_component; 18 + struct delayed_work; 19 + 20 + int sdca_ump_get_owner_host(struct device *dev, 21 + struct regmap *function_regmap, 22 + struct sdca_function_data *function, 23 + struct sdca_entity *entity, 24 + struct sdca_control *control); 25 + int sdca_ump_set_owner_device(struct device *dev, 26 + struct regmap *function_regmap, 27 + struct sdca_function_data *function, 28 + struct sdca_entity *entity, 29 + struct sdca_control *control); 30 + int sdca_ump_read_message(struct device *dev, 31 + struct regmap *device_regmap, 32 + struct regmap *function_regmap, 33 + struct sdca_function_data *function, 34 + struct sdca_entity *entity, 35 + unsigned int offset_sel, unsigned int length_sel, 36 + void **msg); 37 + int sdca_ump_write_message(struct device *dev, 38 + struct regmap *device_regmap, 39 + struct regmap *function_regmap, 40 + struct sdca_function_data *function, 41 + struct sdca_entity *entity, 42 + unsigned int offset_sel, unsigned int msg_offset, 43 + unsigned int length_sel, 44 + void *msg, int msg_len); 45 + 46 + void sdca_ump_cancel_timeout(struct delayed_work *work); 47 + void sdca_ump_schedule_timeout(struct delayed_work *work, 48 + unsigned int timeout_us); 49 + 50 + #endif // __SDCA_UMP_H__
+3 -1
sound/soc/codecs/rt722-sdca-sdw.c
··· 419 419 struct regmap *regmap; 420 420 421 421 /* Regmap Initialization */ 422 - regmap = devm_regmap_init_sdw_mbq_cfg(slave, &rt722_sdca_regmap, &rt722_mbq_config); 422 + regmap = devm_regmap_init_sdw_mbq_cfg(&slave->dev, slave, 423 + &rt722_sdca_regmap, 424 + &rt722_mbq_config); 423 425 if (IS_ERR(regmap)) 424 426 return PTR_ERR(regmap); 425 427
+8
sound/soc/sdca/Kconfig
··· 25 25 help 26 26 This option enables support for SDCA IRQs. 27 27 28 + config SND_SOC_SDCA_FDL 29 + bool "SDCA FDL (File DownLoad) support" 30 + depends on SND_SOC_SDCA 31 + default y 32 + help 33 + This option enables support for the File Download using UMP, 34 + typically used for downloading firmware to devices. 35 + 28 36 config SND_SOC_SDCA_OPTIONAL 29 37 def_tristate SND_SOC_SDCA || !SND_SOC_SDCA 30 38
+3 -1
sound/soc/sdca/Makefile
··· 1 1 # SPDX-License-Identifier: GPL-2.0-only 2 2 3 - snd-soc-sdca-y := sdca_functions.o sdca_device.o sdca_regmap.o sdca_asoc.o 3 + snd-soc-sdca-y := sdca_functions.o sdca_device.o sdca_regmap.o sdca_asoc.o \ 4 + sdca_ump.o 4 5 snd-soc-sdca-$(CONFIG_SND_SOC_SDCA_HID) += sdca_hid.o 5 6 snd-soc-sdca-$(CONFIG_SND_SOC_SDCA_IRQ) += sdca_interrupts.o 7 + snd-soc-sdca-$(CONFIG_SND_SOC_SDCA_FDL) += sdca_fdl.o 6 8 7 9 obj-$(CONFIG_SND_SOC_SDCA) += snd-soc-sdca.o
+20
sound/soc/sdca/sdca_device.c
··· 7 7 */ 8 8 9 9 #include <linux/acpi.h> 10 + #include <linux/device.h> 10 11 #include <linux/dmi.h> 11 12 #include <linux/module.h> 12 13 #include <linux/property.h> ··· 27 26 &slave->sdca_data.interface_revision); 28 27 } 29 28 EXPORT_SYMBOL_NS(sdca_lookup_interface_revision, "SND_SOC_SDCA"); 29 + 30 + static void devm_acpi_table_put(void *ptr) 31 + { 32 + acpi_put_table((struct acpi_table_header *)ptr); 33 + } 34 + 35 + void sdca_lookup_swft(struct sdw_slave *slave) 36 + { 37 + acpi_status status; 38 + 39 + status = acpi_get_table(ACPI_SIG_SWFT, 0, 40 + (struct acpi_table_header **)&slave->sdca_data.swft); 41 + if (ACPI_FAILURE(status)) 42 + dev_info(&slave->dev, "SWFT not available\n"); 43 + else 44 + devm_add_action_or_reset(&slave->dev, devm_acpi_table_put, 45 + &slave->sdca_data.swft); 46 + } 47 + EXPORT_SYMBOL_NS(sdca_lookup_swft, "SND_SOC_SDCA"); 30 48 31 49 static bool sdca_device_quirk_rt712_vb(struct sdw_slave *slave) 32 50 {
+499
sound/soc/sdca/sdca_fdl.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + // Copyright (C) 2025 Cirrus Logic, Inc. and 3 + // Cirrus Logic International Semiconductor Ltd. 4 + 5 + /* 6 + * The MIPI SDCA specification is available for public downloads at 7 + * https://www.mipi.org/mipi-sdca-v1-0-download 8 + */ 9 + 10 + #include <linux/acpi.h> 11 + #include <linux/device.h> 12 + #include <linux/dev_printk.h> 13 + #include <linux/dmi.h> 14 + #include <linux/firmware.h> 15 + #include <linux/module.h> 16 + #include <linux/pci.h> 17 + #include <linux/pm_runtime.h> 18 + #include <linux/regmap.h> 19 + #include <linux/sprintf.h> 20 + #include <linux/soundwire/sdw.h> 21 + #include <linux/soundwire/sdw_registers.h> 22 + #include <sound/sdca.h> 23 + #include <sound/sdca_fdl.h> 24 + #include <sound/sdca_function.h> 25 + #include <sound/sdca_interrupts.h> 26 + #include <sound/sdca_ump.h> 27 + 28 + /** 29 + * sdca_reset_function - send an SDCA function reset 30 + * @dev: Device pointer for error messages. 31 + * @function: Pointer to the SDCA Function. 32 + * @regmap: Pointer to the SDCA Function regmap. 33 + * 34 + * Return: Zero on success or a negative error code. 35 + */ 36 + int sdca_reset_function(struct device *dev, struct sdca_function_data *function, 37 + struct regmap *regmap) 38 + { 39 + unsigned int reg = SDW_SDCA_CTL(function->desc->adr, 40 + SDCA_ENTITY_TYPE_ENTITY_0, 41 + SDCA_CTL_ENTITY_0_FUNCTION_ACTION, 0); 42 + unsigned int val, poll_us; 43 + int ret; 44 + 45 + ret = regmap_write(regmap, reg, SDCA_CTL_ENTITY_0_RESET_FUNCTION_NOW); 46 + if (ret) // Allowed for function reset to not be implemented 47 + return 0; 48 + 49 + if (!function->reset_max_delay) { 50 + dev_err(dev, "No reset delay specified in DisCo\n"); 51 + return -EINVAL; 52 + } 53 + 54 + poll_us = umin(function->reset_max_delay >> 4, 1000); 55 + 56 + ret = regmap_read_poll_timeout(regmap, reg, val, !val, poll_us, 57 + function->reset_max_delay); 58 + if (ret) { 59 + dev_err(dev, "Failed waiting for function reset: %d\n", ret); 60 + return ret; 61 + } 62 + 63 + return 0; 64 + } 65 + EXPORT_SYMBOL_NS(sdca_reset_function, "SND_SOC_SDCA"); 66 + 67 + /** 68 + * sdca_fdl_sync - wait for a function to finish FDL 69 + * @dev: Device pointer for error messages. 70 + * @function: Pointer to the SDCA Function. 71 + * @info: Pointer to the SDCA interrupt info for this device. 72 + * 73 + * Return: Zero on success or a negative error code. 74 + */ 75 + int sdca_fdl_sync(struct device *dev, struct sdca_function_data *function, 76 + struct sdca_interrupt_info *info) 77 + { 78 + static const int fdl_retries = 6; 79 + unsigned long begin_timeout = msecs_to_jiffies(100); 80 + unsigned long done_timeout = msecs_to_jiffies(4000); 81 + int nfdl; 82 + int i, j; 83 + 84 + for (i = 0; i < fdl_retries; i++) { 85 + nfdl = 0; 86 + 87 + for (j = 0; j < SDCA_MAX_INTERRUPTS; j++) { 88 + struct sdca_interrupt *interrupt = &info->irqs[j]; 89 + struct fdl_state *fdl_state; 90 + unsigned long time; 91 + 92 + if (interrupt->function != function || 93 + !interrupt->entity || !interrupt->control || 94 + interrupt->entity->type != SDCA_ENTITY_TYPE_XU || 95 + interrupt->control->sel != SDCA_CTL_XU_FDL_CURRENTOWNER) 96 + continue; 97 + 98 + fdl_state = interrupt->priv; 99 + nfdl++; 100 + 101 + /* 102 + * Looking for timeout without any new FDL requests 103 + * to imply the device has completed initial 104 + * firmware setup. Alas the specification doesn't 105 + * have any mechanism to detect this. 106 + */ 107 + time = wait_for_completion_timeout(&fdl_state->begin, 108 + begin_timeout); 109 + if (!time) { 110 + dev_dbg(dev, "no new FDL starts\n"); 111 + nfdl--; 112 + continue; 113 + } 114 + 115 + time = wait_for_completion_timeout(&fdl_state->done, 116 + done_timeout); 117 + if (!time) { 118 + dev_err(dev, "timed out waiting for FDL to complete\n"); 119 + goto error; 120 + } 121 + } 122 + 123 + if (!nfdl) 124 + return 0; 125 + } 126 + 127 + dev_err(dev, "too many FDL requests\n"); 128 + 129 + error: 130 + for (j = 0; j < SDCA_MAX_INTERRUPTS; j++) { 131 + struct sdca_interrupt *interrupt = &info->irqs[j]; 132 + struct fdl_state *fdl_state; 133 + 134 + if (interrupt->function != function || 135 + !interrupt->entity || !interrupt->control || 136 + interrupt->entity->type != SDCA_ENTITY_TYPE_XU || 137 + interrupt->control->sel != SDCA_CTL_XU_FDL_CURRENTOWNER) 138 + continue; 139 + 140 + disable_irq(interrupt->irq); 141 + 142 + fdl_state = interrupt->priv; 143 + 144 + sdca_ump_cancel_timeout(&fdl_state->timeout); 145 + } 146 + 147 + return -ETIMEDOUT; 148 + } 149 + EXPORT_SYMBOL_NS_GPL(sdca_fdl_sync, "SND_SOC_SDCA"); 150 + 151 + static char *fdl_get_sku_filename(struct device *dev, 152 + struct sdca_fdl_file *fdl_file) 153 + { 154 + struct device *parent = dev; 155 + const char *product_vendor; 156 + const char *product_sku; 157 + 158 + /* 159 + * Try to find pci_dev manually because the card may not be ready to be 160 + * used for snd_soc_card_get_pci_ssid yet 161 + */ 162 + while (parent) { 163 + if (dev_is_pci(parent)) { 164 + struct pci_dev *pci_dev = to_pci_dev(parent); 165 + 166 + return kasprintf(GFP_KERNEL, "sdca/%x/%x/%x/%x.bin", 167 + fdl_file->vendor_id, 168 + pci_dev->subsystem_vendor, 169 + pci_dev->subsystem_device, 170 + fdl_file->file_id); 171 + } else { 172 + parent = parent->parent; 173 + } 174 + } 175 + 176 + product_vendor = dmi_get_system_info(DMI_SYS_VENDOR); 177 + if (!product_vendor || !strcmp(product_vendor, "Default string")) 178 + product_vendor = dmi_get_system_info(DMI_BOARD_VENDOR); 179 + if (!product_vendor || !strcmp(product_vendor, "Default string")) 180 + product_vendor = dmi_get_system_info(DMI_CHASSIS_VENDOR); 181 + if (!product_vendor) 182 + product_vendor = "unknown"; 183 + 184 + product_sku = dmi_get_system_info(DMI_PRODUCT_SKU); 185 + if (!product_sku || !strcmp(product_sku, "Default string")) 186 + product_sku = dmi_get_system_info(DMI_PRODUCT_NAME); 187 + if (!product_sku) 188 + product_sku = "unknown"; 189 + 190 + return kasprintf(GFP_KERNEL, "sdca/%x/%s/%s/%x.bin", fdl_file->vendor_id, 191 + product_vendor, product_sku, fdl_file->file_id); 192 + } 193 + 194 + static int fdl_load_file(struct sdca_interrupt *interrupt, 195 + struct sdca_fdl_set *set, int file_index) 196 + { 197 + struct device *dev = interrupt->dev; 198 + struct sdca_fdl_data *fdl_data = &interrupt->function->fdl_data; 199 + const struct firmware *firmware = NULL; 200 + struct acpi_sw_file *swf = NULL, *tmp; 201 + struct sdca_fdl_file *fdl_file; 202 + char *disk_filename; 203 + int ret; 204 + int i; 205 + 206 + if (!set) { 207 + dev_err(dev, "request to load SWF with no set\n"); 208 + return -EINVAL; 209 + } 210 + 211 + fdl_file = &set->files[file_index]; 212 + 213 + if (fdl_data->swft) { 214 + tmp = fdl_data->swft->files; 215 + for (i = 0; i < fdl_data->swft->header.length; i += tmp->file_length, 216 + tmp = ACPI_ADD_PTR(struct acpi_sw_file, tmp, tmp->file_length)) { 217 + if (tmp->vendor_id == fdl_file->vendor_id && 218 + tmp->file_id == fdl_file->file_id) { 219 + dev_dbg(dev, "located SWF in ACPI: %x-%x-%x\n", 220 + tmp->vendor_id, tmp->file_id, 221 + tmp->file_version); 222 + swf = tmp; 223 + break; 224 + } 225 + } 226 + } 227 + 228 + disk_filename = fdl_get_sku_filename(dev, fdl_file); 229 + if (!disk_filename) 230 + return -ENOMEM; 231 + 232 + dev_dbg(dev, "FDL disk filename: %s\n", disk_filename); 233 + 234 + ret = firmware_request_nowarn(&firmware, disk_filename, dev); 235 + kfree(disk_filename); 236 + if (ret) { 237 + disk_filename = kasprintf(GFP_KERNEL, "sdca/%x/%x.bin", 238 + fdl_file->vendor_id, fdl_file->file_id); 239 + if (!disk_filename) 240 + return -ENOMEM; 241 + 242 + dev_dbg(dev, "FDL disk filename: %s\n", disk_filename); 243 + 244 + ret = firmware_request_nowarn(&firmware, disk_filename, dev); 245 + kfree(disk_filename); 246 + } 247 + 248 + if (!ret) { 249 + tmp = (struct acpi_sw_file *)&firmware->data[0]; 250 + 251 + if (firmware->size < sizeof(*tmp) || 252 + tmp->file_length != firmware->size) { 253 + dev_err(dev, "bad disk SWF size\n"); 254 + } else if (!swf || swf->file_version <= tmp->file_version) { 255 + dev_dbg(dev, "using SWF from disk: %x-%x-%x\n", 256 + tmp->vendor_id, tmp->file_id, tmp->file_version); 257 + swf = tmp; 258 + } 259 + } 260 + 261 + if (!swf) { 262 + dev_err(dev, "failed to locate SWF\n"); 263 + return -ENOENT; 264 + } 265 + 266 + ret = sdca_ump_write_message(dev, interrupt->device_regmap, 267 + interrupt->function_regmap, 268 + interrupt->function, interrupt->entity, 269 + SDCA_CTL_XU_FDL_MESSAGEOFFSET, fdl_file->fdl_offset, 270 + SDCA_CTL_XU_FDL_MESSAGELENGTH, swf->data, 271 + swf->file_length - offsetof(struct acpi_sw_file, data)); 272 + release_firmware(firmware); 273 + return ret; 274 + } 275 + 276 + static struct sdca_fdl_set *fdl_get_set(struct sdca_interrupt *interrupt) 277 + { 278 + struct device *dev = interrupt->dev; 279 + struct sdca_fdl_data *fdl_data = &interrupt->function->fdl_data; 280 + struct sdca_entity *xu = interrupt->entity; 281 + struct sdca_control_range *range; 282 + unsigned int val; 283 + int i, ret; 284 + 285 + ret = regmap_read(interrupt->function_regmap, 286 + SDW_SDCA_CTL(interrupt->function->desc->adr, xu->id, 287 + SDCA_CTL_XU_FDL_SET_INDEX, 0), 288 + &val); 289 + if (ret < 0) { 290 + dev_err(dev, "failed to read FDL set index: %d\n", ret); 291 + return NULL; 292 + } 293 + 294 + range = sdca_selector_find_range(dev, xu, SDCA_CTL_XU_FDL_SET_INDEX, 295 + SDCA_FDL_SET_INDEX_NCOLS, 0); 296 + 297 + val = sdca_range_search(range, SDCA_FDL_SET_INDEX_SET_NUMBER, 298 + val, SDCA_FDL_SET_INDEX_FILE_SET_ID); 299 + 300 + for (i = 0; i < fdl_data->num_sets; i++) { 301 + if (fdl_data->sets[i].id == val) 302 + return &fdl_data->sets[i]; 303 + } 304 + 305 + dev_err(dev, "invalid fileset id: %d\n", val); 306 + return NULL; 307 + } 308 + 309 + static void fdl_end(struct sdca_interrupt *interrupt) 310 + { 311 + struct fdl_state *fdl_state = interrupt->priv; 312 + 313 + if (!fdl_state->set) 314 + return; 315 + 316 + fdl_state->set = NULL; 317 + 318 + pm_runtime_put(interrupt->dev); 319 + complete(&fdl_state->done); 320 + 321 + dev_dbg(interrupt->dev, "completed FDL process\n"); 322 + } 323 + 324 + static void sdca_fdl_timeout_work(struct work_struct *work) 325 + { 326 + struct fdl_state *fdl_state = container_of(work, struct fdl_state, 327 + timeout.work); 328 + struct sdca_interrupt *interrupt = fdl_state->interrupt; 329 + struct device *dev = interrupt->dev; 330 + 331 + dev_err(dev, "FDL transaction timed out\n"); 332 + 333 + guard(mutex)(&fdl_state->lock); 334 + 335 + fdl_end(interrupt); 336 + sdca_reset_function(dev, interrupt->function, interrupt->function_regmap); 337 + } 338 + 339 + static int fdl_status_process(struct sdca_interrupt *interrupt, unsigned int status) 340 + { 341 + struct fdl_state *fdl_state = interrupt->priv; 342 + int ret; 343 + 344 + switch (status) { 345 + case SDCA_CTL_XU_FDLD_NEEDS_SET: 346 + dev_dbg(interrupt->dev, "starting FDL process...\n"); 347 + 348 + pm_runtime_get(interrupt->dev); 349 + complete(&fdl_state->begin); 350 + 351 + fdl_state->file_index = 0; 352 + fdl_state->set = fdl_get_set(interrupt); 353 + fallthrough; 354 + case SDCA_CTL_XU_FDLD_MORE_FILES_OK: 355 + ret = fdl_load_file(interrupt, fdl_state->set, fdl_state->file_index); 356 + if (ret) { 357 + fdl_end(interrupt); 358 + return SDCA_CTL_XU_FDLH_REQ_ABORT; 359 + } 360 + 361 + return SDCA_CTL_XU_FDLH_FILE_AVAILABLE; 362 + case SDCA_CTL_XU_FDLD_FILE_OK: 363 + if (!fdl_state->set) { 364 + fdl_end(interrupt); 365 + return SDCA_CTL_XU_FDLH_REQ_ABORT; 366 + } 367 + 368 + fdl_state->file_index++; 369 + 370 + if (fdl_state->file_index < fdl_state->set->num_files) 371 + return SDCA_CTL_XU_FDLH_MORE_FILES; 372 + fallthrough; 373 + case SDCA_CTL_XU_FDLD_COMPLETE: 374 + fdl_end(interrupt); 375 + return SDCA_CTL_XU_FDLH_COMPLETE; 376 + default: 377 + fdl_end(interrupt); 378 + 379 + if (status & SDCA_CTL_XU_FDLD_REQ_RESET) 380 + return SDCA_CTL_XU_FDLH_RESET_ACK; 381 + else if (status & SDCA_CTL_XU_FDLD_REQ_ABORT) 382 + return SDCA_CTL_XU_FDLH_COMPLETE; 383 + 384 + dev_err(interrupt->dev, "invalid FDL status: %x\n", status); 385 + return -EINVAL; 386 + } 387 + } 388 + 389 + /** 390 + * sdca_fdl_process - Process the FDL state machine 391 + * @interrupt: SDCA interrupt structure 392 + * 393 + * Based on section 13.2.5 Flow Diagram for File Download, Host side. 394 + * 395 + * Return: Zero on success or a negative error code. 396 + */ 397 + int sdca_fdl_process(struct sdca_interrupt *interrupt) 398 + { 399 + struct device *dev = interrupt->dev; 400 + struct sdca_entity_xu *xu = &interrupt->entity->xu; 401 + struct fdl_state *fdl_state = interrupt->priv; 402 + unsigned int reg, status; 403 + int response, ret; 404 + 405 + guard(mutex)(&fdl_state->lock); 406 + 407 + ret = sdca_ump_get_owner_host(dev, interrupt->function_regmap, 408 + interrupt->function, interrupt->entity, 409 + interrupt->control); 410 + if (ret) 411 + goto reset_function; 412 + 413 + sdca_ump_cancel_timeout(&fdl_state->timeout); 414 + 415 + reg = SDW_SDCA_CTL(interrupt->function->desc->adr, interrupt->entity->id, 416 + SDCA_CTL_XU_FDL_STATUS, 0); 417 + ret = regmap_read(interrupt->function_regmap, reg, &status); 418 + if (ret < 0) { 419 + dev_err(dev, "failed to read FDL status: %d\n", ret); 420 + return ret; 421 + } 422 + 423 + dev_dbg(dev, "FDL status: %#x\n", status); 424 + 425 + ret = fdl_status_process(interrupt, status); 426 + if (ret < 0) 427 + goto reset_function; 428 + 429 + response = ret; 430 + 431 + dev_dbg(dev, "FDL response: %#x\n", response); 432 + 433 + ret = regmap_write(interrupt->function_regmap, reg, 434 + response | (status & ~SDCA_CTL_XU_FDLH_MASK)); 435 + if (ret < 0) { 436 + dev_err(dev, "failed to set FDL status signal: %d\n", ret); 437 + return ret; 438 + } 439 + 440 + ret = sdca_ump_set_owner_device(dev, interrupt->function_regmap, 441 + interrupt->function, interrupt->entity, 442 + interrupt->control); 443 + if (ret) 444 + return ret; 445 + 446 + switch (response) { 447 + case SDCA_CTL_XU_FDLH_RESET_ACK: 448 + dev_dbg(dev, "FDL request reset\n"); 449 + 450 + switch (xu->reset_mechanism) { 451 + default: 452 + dev_warn(dev, "Requested reset mechanism not implemented\n"); 453 + fallthrough; 454 + case SDCA_XU_RESET_FUNCTION: 455 + goto reset_function; 456 + } 457 + case SDCA_CTL_XU_FDLH_COMPLETE: 458 + if (status & SDCA_CTL_XU_FDLD_REQ_ABORT || 459 + status == SDCA_CTL_XU_FDLD_COMPLETE) 460 + return 0; 461 + fallthrough; 462 + default: 463 + sdca_ump_schedule_timeout(&fdl_state->timeout, xu->max_delay); 464 + return 0; 465 + } 466 + 467 + reset_function: 468 + sdca_reset_function(dev, interrupt->function, interrupt->function_regmap); 469 + 470 + return ret; 471 + } 472 + EXPORT_SYMBOL_NS_GPL(sdca_fdl_process, "SND_SOC_SDCA"); 473 + 474 + /** 475 + * sdca_fdl_alloc_state - allocate state for an FDL interrupt 476 + * @interrupt: SDCA interrupt structure. 477 + * 478 + * Return: Zero on success or a negative error code. 479 + */ 480 + int sdca_fdl_alloc_state(struct sdca_interrupt *interrupt) 481 + { 482 + struct device *dev = interrupt->dev; 483 + struct fdl_state *fdl_state; 484 + 485 + fdl_state = devm_kzalloc(dev, sizeof(struct fdl_state), GFP_KERNEL); 486 + if (!fdl_state) 487 + return -ENOMEM; 488 + 489 + INIT_DELAYED_WORK(&fdl_state->timeout, sdca_fdl_timeout_work); 490 + init_completion(&fdl_state->begin); 491 + init_completion(&fdl_state->done); 492 + mutex_init(&fdl_state->lock); 493 + fdl_state->interrupt = interrupt; 494 + 495 + interrupt->priv = fdl_state; 496 + 497 + return 0; 498 + } 499 + EXPORT_SYMBOL_NS_GPL(sdca_fdl_alloc_state, "SND_SOC_SDCA");
+199 -13
sound/soc/sdca/sdca_functions.c
··· 179 179 */ 180 180 void sdca_lookup_functions(struct sdw_slave *slave) 181 181 { 182 - struct device *dev = &slave->dev; 183 - struct acpi_device *adev = to_acpi_device_node(dev->fwnode); 182 + struct device *sdev = &slave->dev; 183 + struct acpi_device *adev = to_acpi_device_node(sdev->fwnode); 184 184 185 185 if (!adev) { 186 - dev_info(dev, "no matching ACPI device found, ignoring peripheral\n"); 186 + dev_info(sdev, "no matching ACPI device found, ignoring peripheral\n"); 187 187 return; 188 188 } 189 189 ··· 779 779 } 780 780 } 781 781 782 + static bool find_sdca_control_volatile(const struct sdca_entity *entity, 783 + const struct sdca_control *control) 784 + { 785 + switch (control->mode) { 786 + case SDCA_ACCESS_MODE_DC: 787 + return false; 788 + case SDCA_ACCESS_MODE_RO: 789 + case SDCA_ACCESS_MODE_RW1S: 790 + case SDCA_ACCESS_MODE_RW1C: 791 + return true; 792 + default: 793 + break; 794 + } 795 + 796 + switch (SDCA_CTL_TYPE(entity->type, control->sel)) { 797 + case SDCA_CTL_TYPE_S(XU, FDL_CURRENTOWNER): 798 + case SDCA_CTL_TYPE_S(XU, FDL_MESSAGEOFFSET): 799 + case SDCA_CTL_TYPE_S(XU, FDL_MESSAGELENGTH): 800 + case SDCA_CTL_TYPE_S(XU, FDL_STATUS): 801 + case SDCA_CTL_TYPE_S(XU, FDL_HOST_REQUEST): 802 + case SDCA_CTL_TYPE_S(SPE, AUTHTX_CURRENTOWNER): 803 + case SDCA_CTL_TYPE_S(SPE, AUTHTX_MESSAGEOFFSET): 804 + case SDCA_CTL_TYPE_S(SPE, AUTHTX_MESSAGELENGTH): 805 + case SDCA_CTL_TYPE_S(SPE, AUTHRX_CURRENTOWNER): 806 + case SDCA_CTL_TYPE_S(SPE, AUTHRX_MESSAGEOFFSET): 807 + case SDCA_CTL_TYPE_S(SPE, AUTHRX_MESSAGELENGTH): 808 + case SDCA_CTL_TYPE_S(MFPU, AE_CURRENTOWNER): 809 + case SDCA_CTL_TYPE_S(MFPU, AE_MESSAGEOFFSET): 810 + case SDCA_CTL_TYPE_S(MFPU, AE_MESSAGELENGTH): 811 + case SDCA_CTL_TYPE_S(SMPU, HIST_CURRENTOWNER): 812 + case SDCA_CTL_TYPE_S(SMPU, HIST_MESSAGEOFFSET): 813 + case SDCA_CTL_TYPE_S(SMPU, HIST_MESSAGELENGTH): 814 + case SDCA_CTL_TYPE_S(SMPU, DTODTX_CURRENTOWNER): 815 + case SDCA_CTL_TYPE_S(SMPU, DTODTX_MESSAGEOFFSET): 816 + case SDCA_CTL_TYPE_S(SMPU, DTODTX_MESSAGELENGTH): 817 + case SDCA_CTL_TYPE_S(SMPU, DTODRX_CURRENTOWNER): 818 + case SDCA_CTL_TYPE_S(SMPU, DTODRX_MESSAGEOFFSET): 819 + case SDCA_CTL_TYPE_S(SMPU, DTODRX_MESSAGELENGTH): 820 + case SDCA_CTL_TYPE_S(SAPU, DTODTX_CURRENTOWNER): 821 + case SDCA_CTL_TYPE_S(SAPU, DTODTX_MESSAGEOFFSET): 822 + case SDCA_CTL_TYPE_S(SAPU, DTODTX_MESSAGELENGTH): 823 + case SDCA_CTL_TYPE_S(SAPU, DTODRX_CURRENTOWNER): 824 + case SDCA_CTL_TYPE_S(SAPU, DTODRX_MESSAGEOFFSET): 825 + case SDCA_CTL_TYPE_S(SAPU, DTODRX_MESSAGELENGTH): 826 + case SDCA_CTL_TYPE_S(HIDE, HIDTX_CURRENTOWNER): 827 + case SDCA_CTL_TYPE_S(HIDE, HIDTX_MESSAGEOFFSET): 828 + case SDCA_CTL_TYPE_S(HIDE, HIDTX_MESSAGELENGTH): 829 + case SDCA_CTL_TYPE_S(HIDE, HIDRX_CURRENTOWNER): 830 + case SDCA_CTL_TYPE_S(HIDE, HIDRX_MESSAGEOFFSET): 831 + case SDCA_CTL_TYPE_S(HIDE, HIDRX_MESSAGELENGTH): 832 + return true; 833 + default: 834 + return false; 835 + } 836 + } 837 + 782 838 static int find_sdca_control_range(struct device *dev, 783 839 struct fwnode_handle *control_node, 784 840 struct sdca_control_range *range) ··· 985 929 default: 986 930 break; 987 931 } 932 + 933 + control->is_volatile = find_sdca_control_volatile(entity, control); 988 934 989 935 ret = find_sdca_control_range(dev, control_node, &control->range); 990 936 if (ret) { ··· 1311 1253 } 1312 1254 1313 1255 static int 1314 - find_sdca_entity_hide(struct device *dev, struct fwnode_handle *function_node, 1256 + find_sdca_entity_hide(struct device *dev, struct sdw_slave *sdw, 1257 + struct fwnode_handle *function_node, 1315 1258 struct fwnode_handle *entity_node, struct sdca_entity *entity) 1316 1259 { 1317 1260 struct sdca_entity_hide *hide = &entity->hide; ··· 1387 1328 report_desc, nval); 1388 1329 1389 1330 /* add HID device */ 1390 - ret = sdca_add_hid_device(dev, entity); 1331 + ret = sdca_add_hid_device(dev, sdw, entity); 1391 1332 if (ret) { 1392 1333 dev_err(dev, "%pfwP: failed to add HID device: %d\n", entity_node, ret); 1393 1334 return ret; ··· 1398 1339 return 0; 1399 1340 } 1400 1341 1401 - static int find_sdca_entity(struct device *dev, 1342 + static int find_sdca_entity_xu(struct device *dev, 1343 + struct fwnode_handle *entity_node, 1344 + struct sdca_entity *entity) 1345 + { 1346 + struct sdca_entity_xu *xu = &entity->xu; 1347 + u32 tmp; 1348 + int ret; 1349 + 1350 + ret = fwnode_property_read_u32(entity_node, 1351 + "mipi-sdca-RxUMP-ownership-transition-max-delay", 1352 + &tmp); 1353 + if (!ret) 1354 + xu->max_delay = tmp; 1355 + 1356 + ret = fwnode_property_read_u32(entity_node, "mipi-sdca-FDL-reset-mechanism", 1357 + &tmp); 1358 + if (!ret) 1359 + xu->reset_mechanism = tmp; 1360 + 1361 + return 0; 1362 + } 1363 + 1364 + static int find_sdca_entity(struct device *dev, struct sdw_slave *sdw, 1402 1365 struct fwnode_handle *function_node, 1403 1366 struct fwnode_handle *entity_node, 1404 1367 struct sdca_entity *entity) ··· 1452 1371 case SDCA_ENTITY_TYPE_OT: 1453 1372 ret = find_sdca_entity_iot(dev, entity_node, entity); 1454 1373 break; 1374 + case SDCA_ENTITY_TYPE_XU: 1375 + ret = find_sdca_entity_xu(dev, entity_node, entity); 1376 + break; 1455 1377 case SDCA_ENTITY_TYPE_CS: 1456 1378 ret = find_sdca_entity_cs(dev, entity_node, entity); 1457 1379 break; ··· 1465 1381 ret = find_sdca_entity_ge(dev, entity_node, entity); 1466 1382 break; 1467 1383 case SDCA_ENTITY_TYPE_HIDE: 1468 - ret = find_sdca_entity_hide(dev, function_node, entity_node, entity); 1384 + ret = find_sdca_entity_hide(dev, sdw, function_node, 1385 + entity_node, entity); 1469 1386 break; 1470 1387 default: 1471 1388 break; ··· 1481 1396 return 0; 1482 1397 } 1483 1398 1484 - static int find_sdca_entities(struct device *dev, 1399 + static int find_sdca_entities(struct device *dev, struct sdw_slave *sdw, 1485 1400 struct fwnode_handle *function_node, 1486 1401 struct sdca_function_data *function) 1487 1402 { ··· 1533 1448 return -EINVAL; 1534 1449 } 1535 1450 1536 - ret = find_sdca_entity(dev, function_node, entity_node, &entities[i]); 1451 + ret = find_sdca_entity(dev, sdw, function_node, 1452 + entity_node, &entities[i]); 1537 1453 fwnode_handle_put(entity_node); 1538 1454 if (ret) 1539 1455 return ret; ··· 2010 1924 return 0; 2011 1925 } 2012 1926 1927 + static int find_sdca_filesets(struct device *dev, struct sdw_slave *sdw, 1928 + struct fwnode_handle *function_node, 1929 + struct sdca_function_data *function) 1930 + { 1931 + static const int mult_fileset = 3; 1932 + char fileset_name[SDCA_PROPERTY_LENGTH]; 1933 + u32 *filesets_list __free(kfree) = NULL; 1934 + struct sdca_fdl_set *sets; 1935 + int num_sets; 1936 + int i, j; 1937 + 1938 + num_sets = fwnode_property_count_u32(function_node, 1939 + "mipi-sdca-file-set-id-list"); 1940 + if (num_sets == 0 || num_sets == -EINVAL) { 1941 + return 0; 1942 + } else if (num_sets < 0) { 1943 + dev_err(dev, "%pfwP: failed to read file set list: %d\n", 1944 + function_node, num_sets); 1945 + return num_sets; 1946 + } 1947 + 1948 + filesets_list = kcalloc(num_sets, sizeof(u32), GFP_KERNEL); 1949 + if (!filesets_list) 1950 + return -ENOMEM; 1951 + 1952 + fwnode_property_read_u32_array(function_node, "mipi-sdca-file-set-id-list", 1953 + filesets_list, num_sets); 1954 + 1955 + sets = devm_kcalloc(dev, num_sets, sizeof(struct sdca_fdl_set), GFP_KERNEL); 1956 + if (!sets) 1957 + return -ENOMEM; 1958 + 1959 + for (i = 0; i < num_sets; i++) { 1960 + u32 *fileset_entries __free(kfree) = NULL; 1961 + struct sdca_fdl_set *set = &sets[i]; 1962 + struct sdca_fdl_file *files; 1963 + int num_files, num_entries; 1964 + 1965 + snprintf(fileset_name, sizeof(fileset_name), 1966 + "mipi-sdca-file-set-id-0x%X", filesets_list[i]); 1967 + 1968 + num_entries = fwnode_property_count_u32(function_node, fileset_name); 1969 + if (num_entries <= 0) { 1970 + dev_err(dev, "%pfwP: file set %d missing entries: %d\n", 1971 + function_node, filesets_list[i], num_entries); 1972 + return -EINVAL; 1973 + } else if (num_entries % mult_fileset != 0) { 1974 + dev_err(dev, "%pfwP: file set %d files not multiple of %d\n", 1975 + function_node, filesets_list[i], mult_fileset); 1976 + return -EINVAL; 1977 + } 1978 + 1979 + dev_info(dev, "fileset: %#x\n", filesets_list[i]); 1980 + 1981 + files = devm_kcalloc(dev, num_entries / mult_fileset, 1982 + sizeof(struct sdca_fdl_file), GFP_KERNEL); 1983 + if (!files) 1984 + return -ENOMEM; 1985 + 1986 + fileset_entries = kcalloc(num_entries, sizeof(u32), GFP_KERNEL); 1987 + if (!fileset_entries) 1988 + return -ENOMEM; 1989 + 1990 + fwnode_property_read_u32_array(function_node, fileset_name, 1991 + fileset_entries, num_entries); 1992 + 1993 + for (j = 0, num_files = 0; j < num_entries; num_files++) { 1994 + struct sdca_fdl_file *file = &files[num_files]; 1995 + 1996 + file->vendor_id = fileset_entries[j++]; 1997 + file->file_id = fileset_entries[j++]; 1998 + file->fdl_offset = fileset_entries[j++]; 1999 + 2000 + dev_info(dev, "file: %#x, vendor: %#x, offset: %#x\n", 2001 + file->file_id, file->vendor_id, file->fdl_offset); 2002 + } 2003 + 2004 + set->id = filesets_list[i]; 2005 + set->num_files = num_files; 2006 + set->files = files; 2007 + } 2008 + 2009 + function->fdl_data.swft = sdw->sdca_data.swft; 2010 + function->fdl_data.num_sets = num_sets; 2011 + function->fdl_data.sets = sets; 2012 + 2013 + return 0; 2014 + } 2015 + 2013 2016 /** 2014 2017 * sdca_parse_function - parse ACPI DisCo for a Function 2015 2018 * @dev: Pointer to device against which function data will be allocated. 2019 + * @sdw: SoundWire slave device to be processed. 2016 2020 * @function_desc: Pointer to the Function short descriptor. 2017 2021 * @function: Pointer to the Function information, to be populated. 2018 2022 * 2019 2023 * Return: Returns 0 for success. 2020 2024 */ 2021 - int sdca_parse_function(struct device *dev, 2025 + int sdca_parse_function(struct device *dev, struct sdw_slave *sdw, 2022 2026 struct sdca_function_desc *function_desc, 2023 2027 struct sdca_function_data *function) 2024 2028 { ··· 2122 1946 if (!ret) 2123 1947 function->busy_max_delay = tmp; 2124 1948 2125 - dev_info(dev, "%pfwP: name %s delay %dus\n", function->desc->node, 2126 - function->desc->name, function->busy_max_delay); 1949 + ret = fwnode_property_read_u32(function_desc->node, 1950 + "mipi-sdca-function-reset-max-delay", &tmp); 1951 + if (!ret) 1952 + function->reset_max_delay = tmp; 1953 + 1954 + dev_info(dev, "%pfwP: name %s busy delay %dus reset delay %dus\n", 1955 + function->desc->node, function->desc->name, 1956 + function->busy_max_delay, function->reset_max_delay); 2127 1957 2128 1958 ret = find_sdca_init_table(dev, function_desc->node, function); 2129 1959 if (ret) 2130 1960 return ret; 2131 1961 2132 - ret = find_sdca_entities(dev, function_desc->node, function); 1962 + ret = find_sdca_entities(dev, sdw, function_desc->node, function); 2133 1963 if (ret) 2134 1964 return ret; 2135 1965 ··· 2145 1963 2146 1964 ret = find_sdca_clusters(dev, function_desc->node, function); 2147 1965 if (ret < 0) 1966 + return ret; 1967 + 1968 + ret = find_sdca_filesets(dev, sdw, function_desc->node, function); 1969 + if (ret) 2148 1970 return ret; 2149 1971 2150 1972 return 0;
+51 -7
sound/soc/sdca/sdca_hid.c
··· 10 10 #include <linux/cleanup.h> 11 11 #include <linux/device.h> 12 12 #include <linux/dev_printk.h> 13 + #include <linux/hid.h> 13 14 #include <linux/module.h> 14 15 #include <linux/property.h> 15 16 #include <linux/soundwire/sdw.h> ··· 18 17 #include <sound/sdca.h> 19 18 #include <sound/sdca_function.h> 20 19 #include <sound/sdca_hid.h> 20 + #include <sound/sdca_interrupts.h> 21 + #include <sound/sdca_ump.h> 21 22 22 23 static int sdwhid_parse(struct hid_device *hid) 23 24 { ··· 85 82 .raw_request = sdwhid_raw_request, 86 83 }; 87 84 88 - int sdca_add_hid_device(struct device *dev, struct sdca_entity *entity) 85 + int sdca_add_hid_device(struct device *dev, struct sdw_slave *sdw, 86 + struct sdca_entity *entity) 89 87 { 90 - struct sdw_bus *bus; 88 + struct sdw_bus *bus = sdw->bus; 91 89 struct hid_device *hid; 92 - struct sdw_slave *slave = dev_to_sdw_dev(dev); 93 90 int ret; 94 - 95 - bus = slave->bus; 96 91 97 92 hid = hid_allocate_device(); 98 93 if (IS_ERR(hid)) ··· 104 103 105 104 snprintf(hid->name, sizeof(hid->name), 106 105 "HID sdw:%01x:%01x:%04x:%04x:%02x", 107 - bus->controller_id, bus->link_id, slave->id.mfg_id, 108 - slave->id.part_id, slave->id.class_id); 106 + bus->controller_id, bus->link_id, sdw->id.mfg_id, 107 + sdw->id.part_id, sdw->id.class_id); 109 108 110 109 snprintf(hid->phys, sizeof(hid->phys), "%s", dev->bus->name); 111 110 ··· 123 122 return 0; 124 123 } 125 124 EXPORT_SYMBOL_NS(sdca_add_hid_device, "SND_SOC_SDCA"); 125 + 126 + /** 127 + * sdca_hid_process_report - read a HID event from the device and report 128 + * @interrupt: Pointer to the SDCA interrupt information structure. 129 + * 130 + * Return: Zero on success, and a negative error code on failure. 131 + */ 132 + int sdca_hid_process_report(struct sdca_interrupt *interrupt) 133 + { 134 + struct device *dev = interrupt->dev; 135 + struct hid_device *hid = interrupt->entity->hide.hid; 136 + void *val __free(kfree) = NULL; 137 + int len, ret; 138 + 139 + ret = sdca_ump_get_owner_host(dev, interrupt->function_regmap, 140 + interrupt->function, interrupt->entity, 141 + interrupt->control); 142 + if (ret) 143 + return ret; 144 + 145 + len = sdca_ump_read_message(dev, interrupt->device_regmap, 146 + interrupt->function_regmap, 147 + interrupt->function, interrupt->entity, 148 + SDCA_CTL_HIDE_HIDTX_MESSAGEOFFSET, 149 + SDCA_CTL_HIDE_HIDTX_MESSAGELENGTH, &val); 150 + if (len < 0) 151 + return len; 152 + 153 + ret = sdca_ump_set_owner_device(dev, interrupt->function_regmap, 154 + interrupt->function, interrupt->entity, 155 + interrupt->control); 156 + if (ret) 157 + return ret; 158 + 159 + ret = hid_input_report(hid, HID_INPUT_REPORT, val, len, true); 160 + if (ret < 0) { 161 + dev_err(dev, "failed to report hid event: %d\n", ret); 162 + return ret; 163 + } 164 + 165 + return 0; 166 + } 167 + EXPORT_SYMBOL_NS(sdca_hid_process_report, "SND_SOC_SDCA"); 126 168 127 169 MODULE_LICENSE("Dual BSD/GPL"); 128 170 MODULE_DESCRIPTION("SDCA HID library");
+225 -46
sound/soc/sdca/sdca_interrupts.c
··· 11 11 #include <linux/bits.h> 12 12 #include <linux/cleanup.h> 13 13 #include <linux/device.h> 14 + #include <linux/dev_printk.h> 14 15 #include <linux/interrupt.h> 16 + #include <linux/pm_runtime.h> 15 17 #include <linux/regmap.h> 16 18 #include <linux/soundwire/sdw.h> 17 19 #include <linux/soundwire/sdw_registers.h> 18 20 #include <sound/sdca.h> 21 + #include <sound/sdca_fdl.h> 19 22 #include <sound/sdca_function.h> 23 + #include <sound/sdca_hid.h> 20 24 #include <sound/sdca_interrupts.h> 25 + #include <sound/sdca_ump.h> 21 26 #include <sound/soc-component.h> 22 27 #include <sound/soc.h> 23 28 ··· 80 75 static irqreturn_t base_handler(int irq, void *data) 81 76 { 82 77 struct sdca_interrupt *interrupt = data; 83 - struct device *dev = interrupt->component->dev; 78 + struct device *dev = interrupt->dev; 84 79 85 80 dev_info(dev, "%s irq without full handling\n", interrupt->name); 86 81 ··· 90 85 static irqreturn_t function_status_handler(int irq, void *data) 91 86 { 92 87 struct sdca_interrupt *interrupt = data; 93 - struct device *dev = interrupt->component->dev; 88 + struct device *dev = interrupt->dev; 89 + irqreturn_t irqret = IRQ_NONE; 94 90 unsigned int reg, val; 95 91 unsigned long status; 96 92 unsigned int mask; 97 93 int ret; 98 94 95 + ret = pm_runtime_get_sync(dev); 96 + if (ret < 0) { 97 + dev_err(dev, "failed to resume for function status: %d\n", ret); 98 + goto error; 99 + } 100 + 99 101 reg = SDW_SDCA_CTL(interrupt->function->desc->adr, interrupt->entity->id, 100 102 interrupt->control->sel, 0); 101 103 102 - ret = regmap_read(interrupt->component->regmap, reg, &val); 104 + ret = regmap_read(interrupt->function_regmap, reg, &val); 103 105 if (ret < 0) { 104 106 dev_err(dev, "failed to read function status: %d\n", ret); 105 - return IRQ_NONE; 107 + goto error; 106 108 } 107 109 108 110 dev_dbg(dev, "function status: %#x\n", val); ··· 139 127 } 140 128 } 141 129 142 - ret = regmap_write(interrupt->component->regmap, reg, val); 130 + ret = regmap_write(interrupt->function_regmap, reg, val); 143 131 if (ret < 0) { 144 132 dev_err(dev, "failed to clear function status: %d\n", ret); 145 - return IRQ_NONE; 133 + goto error; 146 134 } 147 135 148 - return IRQ_HANDLED; 136 + irqret = IRQ_HANDLED; 137 + error: 138 + pm_runtime_put(dev); 139 + return irqret; 149 140 } 150 141 151 142 static irqreturn_t detected_mode_handler(int irq, void *data) 152 143 { 153 144 struct sdca_interrupt *interrupt = data; 145 + struct device *dev = interrupt->dev; 154 146 struct snd_soc_component *component = interrupt->component; 155 - struct device *dev = component->dev; 156 147 struct snd_soc_card *card = component->card; 157 148 struct rw_semaphore *rwsem = &card->snd_card->controls_rwsem; 158 149 struct snd_kcontrol *kctl = interrupt->priv; 159 150 struct snd_ctl_elem_value *ucontrol __free(kfree) = NULL; 160 151 struct soc_enum *soc_enum; 152 + irqreturn_t irqret = IRQ_NONE; 161 153 unsigned int reg, val; 162 154 int ret; 155 + 156 + ret = pm_runtime_get_sync(dev); 157 + if (ret < 0) { 158 + dev_err(dev, "failed to resume for detected mode: %d\n", ret); 159 + goto error; 160 + } 163 161 164 162 if (!kctl) { 165 163 const char *name __free(kfree) = kasprintf(GFP_KERNEL, "%s %s", ··· 177 155 SDCA_CTL_SELECTED_MODE_NAME); 178 156 179 157 if (!name) 180 - return IRQ_NONE; 158 + goto error; 181 159 182 160 kctl = snd_soc_component_get_kcontrol(component, name); 183 161 if (!kctl) { 184 162 dev_dbg(dev, "control not found: %s\n", name); 185 - return IRQ_NONE; 163 + goto error; 186 164 } 187 165 188 166 interrupt->priv = kctl; ··· 193 171 reg = SDW_SDCA_CTL(interrupt->function->desc->adr, interrupt->entity->id, 194 172 interrupt->control->sel, 0); 195 173 196 - ret = regmap_read(component->regmap, reg, &val); 174 + ret = regmap_read(interrupt->function_regmap, reg, &val); 197 175 if (ret < 0) { 198 176 dev_err(dev, "failed to read detected mode: %d\n", ret); 199 - return IRQ_NONE; 177 + goto error; 200 178 } 201 179 202 180 switch (val) { ··· 212 190 * detected mode is unknown we need to see what the device 213 191 * selected as a "safe" option. 214 192 */ 215 - regcache_drop_region(component->regmap, reg, reg); 193 + regcache_drop_region(interrupt->function_regmap, reg, reg); 216 194 217 - ret = regmap_read(component->regmap, reg, &val); 195 + ret = regmap_read(interrupt->function_regmap, reg, &val); 218 196 if (ret) { 219 197 dev_err(dev, "failed to re-check selected mode: %d\n", ret); 220 - return IRQ_NONE; 198 + goto error; 221 199 } 222 200 break; 223 201 default: ··· 228 206 229 207 ucontrol = kzalloc(sizeof(*ucontrol), GFP_KERNEL); 230 208 if (!ucontrol) 231 - return IRQ_NONE; 209 + goto error; 232 210 233 211 ucontrol->value.enumerated.item[0] = snd_soc_enum_val_to_item(soc_enum, val); 234 212 ··· 237 215 up_write(rwsem); 238 216 if (ret < 0) { 239 217 dev_err(dev, "failed to update selected mode: %d\n", ret); 240 - return IRQ_NONE; 218 + goto error; 241 219 } 242 220 243 221 snd_ctl_notify(card->snd_card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id); 244 222 245 - return IRQ_HANDLED; 223 + irqret = IRQ_HANDLED; 224 + error: 225 + pm_runtime_put(dev); 226 + return irqret; 227 + } 228 + 229 + static irqreturn_t hid_handler(int irq, void *data) 230 + { 231 + struct sdca_interrupt *interrupt = data; 232 + struct device *dev = interrupt->dev; 233 + irqreturn_t irqret = IRQ_NONE; 234 + int ret; 235 + 236 + ret = pm_runtime_get_sync(dev); 237 + if (ret < 0) { 238 + dev_err(dev, "failed to resume for hid: %d\n", ret); 239 + goto error; 240 + } 241 + 242 + ret = sdca_hid_process_report(interrupt); 243 + if (ret) 244 + goto error; 245 + 246 + irqret = IRQ_HANDLED; 247 + error: 248 + pm_runtime_put(dev); 249 + return irqret; 250 + } 251 + 252 + static irqreturn_t fdl_owner_handler(int irq, void *data) 253 + { 254 + struct sdca_interrupt *interrupt = data; 255 + struct device *dev = interrupt->dev; 256 + irqreturn_t irqret = IRQ_NONE; 257 + int ret; 258 + 259 + ret = pm_runtime_get_sync(dev); 260 + if (ret < 0) { 261 + dev_err(dev, "failed to resume for fdl: %d\n", ret); 262 + goto error; 263 + } 264 + 265 + ret = sdca_fdl_process(interrupt); 266 + if (ret) 267 + goto error; 268 + 269 + irqret = IRQ_HANDLED; 270 + error: 271 + pm_runtime_put(dev); 272 + return irqret; 246 273 } 247 274 248 275 static int sdca_irq_request_locked(struct device *dev, ··· 310 239 IRQF_ONESHOT, name, data); 311 240 if (ret) 312 241 return ret; 242 + 243 + info->irqs[sdca_irq].irq = irq; 313 244 314 245 dev_dbg(dev, "requested irq %d for %s\n", irq, name); 315 246 ··· 352 279 return ret; 353 280 } 354 281 355 - info->irqs[sdca_irq].externally_requested = true; 356 - 357 282 return 0; 358 283 } 359 284 EXPORT_SYMBOL_NS_GPL(sdca_irq_request, "SND_SOC_SDCA"); 360 285 361 286 /** 362 287 * sdca_irq_data_populate - Populate common interrupt data 288 + * @dev: Pointer to the Function device. 289 + * @regmap: Pointer to the Function regmap. 363 290 * @component: Pointer to the ASoC component for the Function. 364 291 * @function: Pointer to the SDCA Function. 365 292 * @entity: Pointer to the SDCA Entity. ··· 368 295 * 369 296 * Return: Zero on success, and a negative error code on failure. 370 297 */ 371 - int sdca_irq_data_populate(struct snd_soc_component *component, 298 + int sdca_irq_data_populate(struct device *dev, struct regmap *regmap, 299 + struct snd_soc_component *component, 372 300 struct sdca_function_data *function, 373 301 struct sdca_entity *entity, 374 302 struct sdca_control *control, 375 303 struct sdca_interrupt *interrupt) 376 304 { 377 - struct device *dev = component->dev; 378 305 const char *name; 306 + 307 + if (!dev && component) 308 + dev = component->dev; 309 + if (!dev) 310 + return -ENODEV; 379 311 380 312 name = devm_kasprintf(dev, GFP_KERNEL, "%s %s %s", function->desc->name, 381 313 entity->label, control->label); ··· 388 310 return -ENOMEM; 389 311 390 312 interrupt->name = name; 313 + interrupt->dev = dev; 314 + if (!regmap && component) 315 + interrupt->function_regmap = component->regmap; 316 + else 317 + interrupt->function_regmap = regmap; 391 318 interrupt->component = component; 392 319 interrupt->function = function; 393 320 interrupt->entity = entity; ··· 401 318 return 0; 402 319 } 403 320 EXPORT_SYMBOL_NS_GPL(sdca_irq_data_populate, "SND_SOC_SDCA"); 321 + 322 + static struct sdca_interrupt *get_interrupt_data(struct device *dev, int irq, 323 + struct sdca_interrupt_info *info) 324 + { 325 + if (irq == SDCA_NO_INTERRUPT) { 326 + return NULL; 327 + } else if (irq < 0 || irq >= SDCA_MAX_INTERRUPTS) { 328 + dev_err(dev, "bad irq position: %d\n", irq); 329 + return ERR_PTR(-EINVAL); 330 + } 331 + 332 + if (info->irqs[irq].irq) { 333 + dev_dbg(dev, "skipping irq %d, already requested\n", irq); 334 + return NULL; 335 + } 336 + 337 + return &info->irqs[irq]; 338 + } 339 + 340 + /** 341 + * sdca_irq_populate_early - process pre-audio card IRQ registrations 342 + * @dev: Device pointer for SDCA Function. 343 + * @regmap: Regmap pointer for the SDCA Function. 344 + * @function: Pointer to the SDCA Function. 345 + * @info: Pointer to the SDCA interrupt info for this device. 346 + * 347 + * This is intended to be used as part of the Function boot process. It 348 + * can be called before the soundcard is registered (ie. doesn't depend 349 + * on component) and will register the FDL interrupts. 350 + * 351 + * Return: Zero on success, and a negative error code on failure. 352 + */ 353 + int sdca_irq_populate_early(struct device *dev, struct regmap *regmap, 354 + struct sdca_function_data *function, 355 + struct sdca_interrupt_info *info) 356 + { 357 + int i, j; 358 + 359 + guard(mutex)(&info->irq_lock); 360 + 361 + for (i = 0; i < function->num_entities; i++) { 362 + struct sdca_entity *entity = &function->entities[i]; 363 + 364 + for (j = 0; j < entity->num_controls; j++) { 365 + struct sdca_control *control = &entity->controls[j]; 366 + int irq = control->interrupt_position; 367 + struct sdca_interrupt *interrupt; 368 + int ret; 369 + 370 + interrupt = get_interrupt_data(dev, irq, info); 371 + if (IS_ERR(interrupt)) 372 + return PTR_ERR(interrupt); 373 + else if (!interrupt) 374 + continue; 375 + 376 + switch (entity->type) { 377 + case SDCA_ENTITY_TYPE_XU: 378 + if (control->sel != SDCA_CTL_XU_FDL_CURRENTOWNER) 379 + break; 380 + 381 + ret = sdca_irq_data_populate(dev, regmap, NULL, 382 + function, entity, 383 + control, interrupt); 384 + if (ret) 385 + return ret; 386 + 387 + ret = sdca_fdl_alloc_state(interrupt); 388 + if (ret) 389 + return ret; 390 + 391 + ret = sdca_irq_request_locked(dev, info, irq, 392 + interrupt->name, 393 + fdl_owner_handler, 394 + interrupt); 395 + if (ret) { 396 + dev_err(dev, "failed to request irq %s: %d\n", 397 + interrupt->name, ret); 398 + return ret; 399 + } 400 + break; 401 + default: 402 + break; 403 + } 404 + } 405 + } 406 + 407 + return 0; 408 + } 409 + EXPORT_SYMBOL_NS_GPL(sdca_irq_populate_early, "SND_SOC_SDCA"); 404 410 405 411 /** 406 412 * sdca_irq_populate - Request all the individual IRQs for an SDCA Function ··· 520 348 irq_handler_t handler; 521 349 int ret; 522 350 523 - if (irq == SDCA_NO_INTERRUPT) { 351 + interrupt = get_interrupt_data(dev, irq, info); 352 + if (IS_ERR(interrupt)) 353 + return PTR_ERR(interrupt); 354 + else if (!interrupt) 524 355 continue; 525 - } else if (irq < 0 || irq >= SDCA_MAX_INTERRUPTS) { 526 - dev_err(dev, "bad irq position: %d\n", irq); 527 - return -EINVAL; 528 - } 529 356 530 - interrupt = &info->irqs[irq]; 531 - 532 - if (interrupt->externally_requested) { 533 - dev_dbg(dev, 534 - "skipping irq %d, externally requested\n", 535 - irq); 536 - continue; 537 - } 538 - 539 - ret = sdca_irq_data_populate(component, function, entity, 540 - control, interrupt); 357 + ret = sdca_irq_data_populate(dev, NULL, component, 358 + function, entity, control, 359 + interrupt); 541 360 if (ret) 542 361 return ret; 543 362 ··· 542 379 case SDCA_ENTITY_TYPE_GE: 543 380 if (control->sel == SDCA_CTL_GE_DETECTED_MODE) 544 381 handler = detected_mode_handler; 382 + break; 383 + case SDCA_ENTITY_TYPE_XU: 384 + if (control->sel == SDCA_CTL_XU_FDL_CURRENTOWNER) { 385 + ret = sdca_fdl_alloc_state(interrupt); 386 + if (ret) 387 + return ret; 388 + 389 + handler = fdl_owner_handler; 390 + } 391 + break; 392 + case SDCA_ENTITY_TYPE_HIDE: 393 + if (control->sel == SDCA_CTL_HIDE_HIDTX_CURRENTOWNER) 394 + handler = hid_handler; 545 395 break; 546 396 default: 547 397 break; ··· 576 400 577 401 /** 578 402 * sdca_irq_allocate - allocate an SDCA interrupt structure for a device 579 - * @dev: Device pointer against which things should be allocated. 403 + * @sdev: Device pointer against which things should be allocated. 580 404 * @regmap: regmap to be used for accessing the SDCA IRQ registers. 581 405 * @irq: The interrupt number. 582 406 * ··· 587 411 * Return: A pointer to the allocated sdca_interrupt_info struct, or an 588 412 * error code. 589 413 */ 590 - struct sdca_interrupt_info *sdca_irq_allocate(struct device *dev, 414 + struct sdca_interrupt_info *sdca_irq_allocate(struct device *sdev, 591 415 struct regmap *regmap, int irq) 592 416 { 593 417 struct sdca_interrupt_info *info; 594 - int ret; 418 + int ret, i; 595 419 596 - info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL); 420 + info = devm_kzalloc(sdev, sizeof(*info), GFP_KERNEL); 597 421 if (!info) 598 422 return ERR_PTR(-ENOMEM); 599 423 600 424 info->irq_chip = sdca_irq_chip; 601 425 602 - ret = devm_mutex_init(dev, &info->irq_lock); 426 + for (i = 0; i < ARRAY_SIZE(info->irqs); i++) 427 + info->irqs[i].device_regmap = regmap; 428 + 429 + ret = devm_mutex_init(sdev, &info->irq_lock); 603 430 if (ret) 604 431 return ERR_PTR(ret); 605 432 606 - ret = devm_regmap_add_irq_chip(dev, regmap, irq, IRQF_ONESHOT, 0, 433 + ret = devm_regmap_add_irq_chip(sdev, regmap, irq, IRQF_ONESHOT, 0, 607 434 &info->irq_chip, &info->irq_data); 608 435 if (ret) { 609 - dev_err(dev, "failed to register irq chip: %d\n", ret); 436 + dev_err(sdev, "failed to register irq chip: %d\n", ret); 610 437 return ERR_PTR(ret); 611 438 } 612 439 613 - dev_dbg(dev, "registered on irq %d\n", irq); 440 + dev_dbg(sdev, "registered on irq %d\n", irq); 614 441 615 442 return info; 616 443 }
+1 -8
sound/soc/sdca/sdca_regmap.c
··· 147 147 if (!control) 148 148 return false; 149 149 150 - switch (control->mode) { 151 - case SDCA_ACCESS_MODE_RO: 152 - case SDCA_ACCESS_MODE_RW1S: 153 - case SDCA_ACCESS_MODE_RW1C: 154 - return true; 155 - default: 156 - return false; 157 - } 150 + return control->is_volatile; 158 151 } 159 152 EXPORT_SYMBOL_NS(sdca_regmap_volatile, "SND_SOC_SDCA"); 160 153
+262
sound/soc/sdca/sdca_ump.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + // Copyright (C) 2025 Cirrus Logic, Inc. and 3 + // Cirrus Logic International Semiconductor Ltd. 4 + 5 + /* 6 + * The MIPI SDCA specification is available for public downloads at 7 + * https://www.mipi.org/mipi-sdca-v1-0-download 8 + */ 9 + 10 + #include <linux/dev_printk.h> 11 + #include <linux/device.h> 12 + #include <linux/regmap.h> 13 + #include <sound/sdca.h> 14 + #include <sound/sdca_function.h> 15 + #include <sound/sdca_ump.h> 16 + #include <sound/soc-component.h> 17 + #include <linux/soundwire/sdw_registers.h> 18 + 19 + /** 20 + * sdca_ump_get_owner_host - check a UMP buffer is owned by the host 21 + * @dev: Pointer to the struct device used for error messages. 22 + * @function_regmap: Pointer to the regmap for the SDCA Function. 23 + * @function: Pointer to the Function information. 24 + * @entity: Pointer to the SDCA Entity. 25 + * @control: Pointer to the SDCA Control for the UMP Owner. 26 + * 27 + * Return: Returns zero on success, and a negative error code on failure. 28 + */ 29 + int sdca_ump_get_owner_host(struct device *dev, 30 + struct regmap *function_regmap, 31 + struct sdca_function_data *function, 32 + struct sdca_entity *entity, 33 + struct sdca_control *control) 34 + { 35 + unsigned int reg, owner; 36 + int ret; 37 + 38 + reg = SDW_SDCA_CTL(function->desc->adr, entity->id, control->sel, 0); 39 + ret = regmap_read(function_regmap, reg, &owner); 40 + if (ret < 0) { 41 + dev_err(dev, "%s: failed to read UMP owner: %d\n", 42 + entity->label, ret); 43 + return ret; 44 + } 45 + 46 + if (owner != SDCA_UMP_OWNER_HOST) { 47 + dev_err(dev, "%s: host is not the UMP owner\n", entity->label); 48 + return -EINVAL; 49 + } 50 + 51 + return 0; 52 + } 53 + EXPORT_SYMBOL_NS_GPL(sdca_ump_get_owner_host, "SND_SOC_SDCA"); 54 + 55 + /** 56 + * sdca_ump_set_owner_device - set a UMP buffer's ownership back to the device 57 + * @dev: Pointer to the struct device used for error messages. 58 + * @function_regmap: Pointer to the regmap for the SDCA Function. 59 + * @function: Pointer to the Function information. 60 + * @entity: Pointer to the SDCA Entity. 61 + * @control: Pointer to the SDCA Control for the UMP Owner. 62 + * 63 + * Return: Returns zero on success, and a negative error code on failure. 64 + */ 65 + int sdca_ump_set_owner_device(struct device *dev, 66 + struct regmap *function_regmap, 67 + struct sdca_function_data *function, 68 + struct sdca_entity *entity, 69 + struct sdca_control *control) 70 + { 71 + unsigned int reg; 72 + int ret; 73 + 74 + reg = SDW_SDCA_CTL(function->desc->adr, entity->id, control->sel, 0); 75 + ret = regmap_write(function_regmap, reg, SDCA_UMP_OWNER_DEVICE); 76 + if (ret < 0) 77 + dev_err(dev, "%s: failed to write UMP owner: %d\n", 78 + entity->label, ret); 79 + 80 + return ret; 81 + } 82 + EXPORT_SYMBOL_NS_GPL(sdca_ump_set_owner_device, "SND_SOC_SDCA"); 83 + 84 + /** 85 + * sdca_ump_read_message - read a UMP message from the device 86 + * @dev: Pointer to the struct device used for error messages. 87 + * @device_regmap: Pointer to the Device register map. 88 + * @function_regmap: Pointer to the regmap for the SDCA Function. 89 + * @function: Pointer to the Function information. 90 + * @entity: Pointer to the SDCA Entity. 91 + * @offset_sel: Control Selector for the UMP Offset Control. 92 + * @length_sel: Control Selector for the UMP Length Control. 93 + * @msg: Pointer that will be populated with an dynamically buffer 94 + * containing the UMP message. Note this needs to be freed by the 95 + * caller. 96 + * 97 + * The caller should first call sdca_ump_get_owner_host() to ensure the host 98 + * currently owns the UMP buffer, and then this function can be used to 99 + * retrieve a message. It is the callers responsibility to free the 100 + * message once it is finished with it. Finally sdca_ump_set_owner_device() 101 + * should be called to return the buffer to the device. 102 + * 103 + * Return: Returns the message length on success, and a negative error 104 + * code on failure. 105 + */ 106 + int sdca_ump_read_message(struct device *dev, 107 + struct regmap *device_regmap, 108 + struct regmap *function_regmap, 109 + struct sdca_function_data *function, 110 + struct sdca_entity *entity, 111 + unsigned int offset_sel, unsigned int length_sel, 112 + void **msg) 113 + { 114 + struct sdca_control_range *range; 115 + unsigned int msg_offset, msg_len; 116 + unsigned int buf_addr, buf_len; 117 + unsigned int reg; 118 + int ret; 119 + 120 + reg = SDW_SDCA_CTL(function->desc->adr, entity->id, offset_sel, 0); 121 + ret = regmap_read(function_regmap, reg, &msg_offset); 122 + if (ret < 0) { 123 + dev_err(dev, "%s: failed to read UMP offset: %d\n", 124 + entity->label, ret); 125 + return ret; 126 + } 127 + 128 + range = sdca_selector_find_range(dev, entity, offset_sel, 129 + SDCA_MESSAGEOFFSET_NCOLS, 1); 130 + if (!range) 131 + return -ENOENT; 132 + 133 + buf_addr = sdca_range(range, SDCA_MESSAGEOFFSET_BUFFER_START_ADDRESS, 0); 134 + buf_len = sdca_range(range, SDCA_MESSAGEOFFSET_BUFFER_LENGTH, 0); 135 + 136 + reg = SDW_SDCA_CTL(function->desc->adr, entity->id, length_sel, 0); 137 + ret = regmap_read(function_regmap, reg, &msg_len); 138 + if (ret < 0) { 139 + dev_err(dev, "%s: failed to read UMP length: %d\n", 140 + entity->label, ret); 141 + return ret; 142 + } 143 + 144 + if (msg_len > buf_len - msg_offset) { 145 + dev_err(dev, "%s: message too big for UMP buffer: %d\n", 146 + entity->label, msg_len); 147 + return -EINVAL; 148 + } 149 + 150 + *msg = kmalloc(msg_len, GFP_KERNEL); 151 + if (!*msg) 152 + return -ENOMEM; 153 + 154 + ret = regmap_raw_read(device_regmap, buf_addr + msg_offset, *msg, msg_len); 155 + if (ret < 0) { 156 + dev_err(dev, "%s: failed to read UMP message: %d\n", 157 + entity->label, ret); 158 + return ret; 159 + } 160 + 161 + return msg_len; 162 + } 163 + EXPORT_SYMBOL_NS_GPL(sdca_ump_read_message, "SND_SOC_SDCA"); 164 + 165 + /** 166 + * sdca_ump_write_message - write a UMP message to the device 167 + * @dev: Pointer to the struct device used for error messages. 168 + * @device_regmap: Pointer to the Device register map. 169 + * @function_regmap: Pointer to the regmap for the SDCA Function. 170 + * @function: Pointer to the Function information. 171 + * @entity: Pointer to the SDCA Entity. 172 + * @offset_sel: Control Selector for the UMP Offset Control. 173 + * @msg_offset: Offset within the UMP buffer at which the message should 174 + * be written. 175 + * @length_sel: Control Selector for the UMP Length Control. 176 + * @msg: Pointer to the data that should be written to the UMP buffer. 177 + * @msg_len: Length of the message data in bytes. 178 + * 179 + * The caller should first call sdca_ump_get_owner_host() to ensure the host 180 + * currently owns the UMP buffer, and then this function can be used to 181 + * write a message. Finally sdca_ump_set_owner_device() should be called to 182 + * return the buffer to the device, allowing the device to access the 183 + * message. 184 + * 185 + * Return: Returns zero on success, and a negative error code on failure. 186 + */ 187 + int sdca_ump_write_message(struct device *dev, 188 + struct regmap *device_regmap, 189 + struct regmap *function_regmap, 190 + struct sdca_function_data *function, 191 + struct sdca_entity *entity, 192 + unsigned int offset_sel, unsigned int msg_offset, 193 + unsigned int length_sel, 194 + void *msg, int msg_len) 195 + { 196 + struct sdca_control_range *range; 197 + unsigned int buf_addr, buf_len, ump_mode; 198 + unsigned int reg; 199 + int ret; 200 + 201 + range = sdca_selector_find_range(dev, entity, offset_sel, 202 + SDCA_MESSAGEOFFSET_NCOLS, 1); 203 + if (!range) 204 + return -ENOENT; 205 + 206 + buf_addr = sdca_range(range, SDCA_MESSAGEOFFSET_BUFFER_START_ADDRESS, 0); 207 + buf_len = sdca_range(range, SDCA_MESSAGEOFFSET_BUFFER_LENGTH, 0); 208 + ump_mode = sdca_range(range, SDCA_MESSAGEOFFSET_UMP_MODE, 0); 209 + 210 + if (msg_len > buf_len - msg_offset) { 211 + dev_err(dev, "%s: message too big for UMP buffer: %d\n", 212 + entity->label, msg_len); 213 + return -EINVAL; 214 + } 215 + 216 + if (ump_mode != SDCA_UMP_MODE_DIRECT) { 217 + dev_err(dev, "%s: only direct mode currently supported\n", 218 + entity->label); 219 + return -EINVAL; 220 + } 221 + 222 + ret = regmap_raw_write(device_regmap, buf_addr + msg_offset, msg, msg_len); 223 + if (ret) { 224 + dev_err(dev, "%s: failed to write UMP message: %d\n", 225 + entity->label, ret); 226 + return ret; 227 + } 228 + 229 + reg = SDW_SDCA_CTL(function->desc->adr, entity->id, offset_sel, 0); 230 + ret = regmap_write(function_regmap, reg, msg_offset); 231 + if (ret < 0) { 232 + dev_err(dev, "%s: failed to write UMP offset: %d\n", 233 + entity->label, ret); 234 + return ret; 235 + } 236 + 237 + reg = SDW_SDCA_CTL(function->desc->adr, entity->id, length_sel, 0); 238 + ret = regmap_write(function_regmap, reg, msg_len); 239 + if (ret < 0) { 240 + dev_err(dev, "%s: failed to write UMP length: %d\n", 241 + entity->label, ret); 242 + return ret; 243 + } 244 + 245 + return 0; 246 + } 247 + EXPORT_SYMBOL_NS_GPL(sdca_ump_write_message, "SND_SOC_SDCA"); 248 + 249 + void sdca_ump_cancel_timeout(struct delayed_work *work) 250 + { 251 + cancel_delayed_work_sync(work); 252 + } 253 + EXPORT_SYMBOL_NS_GPL(sdca_ump_cancel_timeout, "SND_SOC_SDCA"); 254 + 255 + void sdca_ump_schedule_timeout(struct delayed_work *work, unsigned int timeout_us) 256 + { 257 + if (!timeout_us) 258 + return; 259 + 260 + queue_delayed_work(system_wq, work, usecs_to_jiffies(timeout_us)); 261 + } 262 + EXPORT_SYMBOL_NS_GPL(sdca_ump_schedule_timeout, "SND_SOC_SDCA");