|
7 | 7 | * https://www.mipi.org/mipi-sdca-v1-0-download
|
8 | 8 | */
|
9 | 9 |
|
| 10 | +#include <linux/bits.h> |
10 | 11 | #include <linux/bitmap.h>
|
| 12 | +#include <linux/build_bug.h> |
11 | 13 | #include <linux/delay.h>
|
12 | 14 | #include <linux/dev_printk.h>
|
13 | 15 | #include <linux/device.h>
|
14 | 16 | #include <linux/minmax.h>
|
15 | 17 | #include <linux/module.h>
|
16 | 18 | #include <linux/overflow.h>
|
| 19 | +#include <linux/regmap.h> |
17 | 20 | #include <linux/soundwire/sdw_registers.h>
|
18 | 21 | #include <linux/string_helpers.h>
|
19 | 22 | #include <sound/control.h>
|
| 23 | +#include <sound/pcm.h> |
20 | 24 | #include <sound/sdca.h>
|
21 | 25 | #include <sound/sdca_asoc.h>
|
22 | 26 | #include <sound/sdca_function.h>
|
@@ -1269,3 +1273,98 @@ int sdca_asoc_populate_component(struct device *dev,
|
1269 | 1273 | return 0;
|
1270 | 1274 | }
|
1271 | 1275 | EXPORT_SYMBOL_NS(sdca_asoc_populate_component, "SND_SOC_SDCA");
|
| 1276 | + |
| 1277 | +/** |
| 1278 | + * sdca_asoc_set_constraints - constrain channels available on a DAI |
| 1279 | + * @dev: Pointer to the device, used for error messages. |
| 1280 | + * @regmap: Pointer to the Function register map. |
| 1281 | + * @function: Pointer to the Function information. |
| 1282 | + * @substream: Pointer to the PCM substream. |
| 1283 | + * @dai: Pointer to the ASoC DAI. |
| 1284 | + * |
| 1285 | + * Typically called from startup(). |
| 1286 | + * |
| 1287 | + * Return: Returns zero on success, and a negative error code on failure. |
| 1288 | + */ |
| 1289 | +int sdca_asoc_set_constraints(struct device *dev, struct regmap *regmap, |
| 1290 | + struct sdca_function_data *function, |
| 1291 | + struct snd_pcm_substream *substream, |
| 1292 | + struct snd_soc_dai *dai) |
| 1293 | +{ |
| 1294 | + static const unsigned int channel_list[] = { |
| 1295 | + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, |
| 1296 | + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, |
| 1297 | + }; |
| 1298 | + struct sdca_entity *entity = &function->entities[dai->id]; |
| 1299 | + struct snd_pcm_hw_constraint_list *constraint; |
| 1300 | + struct sdca_control_range *range; |
| 1301 | + struct sdca_control *control; |
| 1302 | + unsigned int channel_mask = 0; |
| 1303 | + int i, ret; |
| 1304 | + |
| 1305 | + static_assert(ARRAY_SIZE(channel_list) == SDCA_MAX_CHANNEL_COUNT); |
| 1306 | + static_assert(sizeof(channel_mask) * BITS_PER_BYTE >= SDCA_MAX_CHANNEL_COUNT); |
| 1307 | + |
| 1308 | + if (entity->type != SDCA_ENTITY_TYPE_IT) |
| 1309 | + return 0; |
| 1310 | + |
| 1311 | + control = sdca_selector_find_control(dev, entity, SDCA_CTL_IT_CLUSTERINDEX); |
| 1312 | + if (!control) |
| 1313 | + return -EINVAL; |
| 1314 | + |
| 1315 | + range = sdca_control_find_range(dev, entity, control, SDCA_CLUSTER_NCOLS, 0); |
| 1316 | + if (!range) |
| 1317 | + return -EINVAL; |
| 1318 | + |
| 1319 | + for (i = 0; i < range->rows; i++) { |
| 1320 | + int clusterid = sdca_range(range, SDCA_CLUSTER_CLUSTERID, i); |
| 1321 | + struct sdca_cluster *cluster; |
| 1322 | + |
| 1323 | + cluster = sdca_id_find_cluster(dev, function, clusterid); |
| 1324 | + if (!cluster) |
| 1325 | + return -ENODEV; |
| 1326 | + |
| 1327 | + channel_mask |= (1 << (cluster->num_channels - 1)); |
| 1328 | + } |
| 1329 | + |
| 1330 | + dev_dbg(dev, "%s: set channel constraint mask: %#x\n", |
| 1331 | + entity->label, channel_mask); |
| 1332 | + |
| 1333 | + constraint = kzalloc(sizeof(*constraint), GFP_KERNEL); |
| 1334 | + if (!constraint) |
| 1335 | + return -ENOMEM; |
| 1336 | + |
| 1337 | + constraint->count = ARRAY_SIZE(channel_list); |
| 1338 | + constraint->list = channel_list; |
| 1339 | + constraint->mask = channel_mask; |
| 1340 | + |
| 1341 | + ret = snd_pcm_hw_constraint_list(substream->runtime, 0, |
| 1342 | + SNDRV_PCM_HW_PARAM_CHANNELS, |
| 1343 | + constraint); |
| 1344 | + if (ret) { |
| 1345 | + dev_err(dev, "%s: failed to add constraint: %d\n", entity->label, ret); |
| 1346 | + kfree(constraint); |
| 1347 | + return ret; |
| 1348 | + } |
| 1349 | + |
| 1350 | + dai->priv = constraint; |
| 1351 | + |
| 1352 | + return 0; |
| 1353 | +} |
| 1354 | +EXPORT_SYMBOL_NS(sdca_asoc_set_constraints, "SND_SOC_SDCA"); |
| 1355 | + |
| 1356 | +/** |
| 1357 | + * sdca_asoc_free_constraints - free constraint allocations |
| 1358 | + * @substream: Pointer to the PCM substream. |
| 1359 | + * @dai: Pointer to the ASoC DAI. |
| 1360 | + * |
| 1361 | + * Typically called from shutdown(). |
| 1362 | + */ |
| 1363 | +void sdca_asoc_free_constraints(struct snd_pcm_substream *substream, |
| 1364 | + struct snd_soc_dai *dai) |
| 1365 | +{ |
| 1366 | + struct snd_pcm_hw_constraint_list *constraint = dai->priv; |
| 1367 | + |
| 1368 | + kfree(constraint); |
| 1369 | +} |
| 1370 | +EXPORT_SYMBOL_NS(sdca_asoc_free_constraints, "SND_SOC_SDCA"); |
0 commit comments