|
| 1 | +// SPDX-License-Identifier: BSD-3-Clause |
| 2 | +// |
| 3 | +// Copyright(c) 2024 Intel Corporation. All rights reserved. |
| 4 | +// |
| 5 | + |
| 6 | +/* SOF topology kcontrols */ |
| 7 | + |
| 8 | +#include <stdio.h> |
| 9 | +#include <sys/poll.h> |
| 10 | +#include <string.h> |
| 11 | +#include <sys/types.h> |
| 12 | +#include <unistd.h> |
| 13 | +#include <stdlib.h> |
| 14 | +#include <sys/wait.h> |
| 15 | +#include <sys/stat.h> |
| 16 | +#include <mqueue.h> |
| 17 | +#include <fcntl.h> |
| 18 | +#include <sys/mman.h> |
| 19 | +#include <semaphore.h> |
| 20 | +#include <assert.h> |
| 21 | +#include <errno.h> |
| 22 | +#include <math.h> |
| 23 | + |
| 24 | +#include <alsa/asoundlib.h> |
| 25 | +#include <alsa/pcm_external.h> |
| 26 | + |
| 27 | +#include <rtos/sof.h> |
| 28 | +#include <sof/audio/pipeline.h> |
| 29 | +#include <sof/audio/component.h> |
| 30 | +#include <ipc/stream.h> |
| 31 | +#include <tplg_parser/topology.h> |
| 32 | + |
| 33 | +#include "plugin.h" |
| 34 | +#include "common.h" |
| 35 | + |
| 36 | +#define SOF_IPC4_VOL_ZERO_DB 0x7fffffff |
| 37 | +#define VOLUME_FWL 16 |
| 38 | +/* |
| 39 | + * Constants used in the computation of linear volume gain |
| 40 | + * from dB gain 20th root of 10 in Q1.16 fixed-point notation |
| 41 | + */ |
| 42 | +#define VOL_TWENTIETH_ROOT_OF_TEN 73533 |
| 43 | +/* 40th root of 10 in Q1.16 fixed-point notation*/ |
| 44 | +#define VOL_FORTIETH_ROOT_OF_TEN 69419 |
| 45 | + |
| 46 | +/* 0.5 dB step value in topology TLV */ |
| 47 | +#define VOL_HALF_DB_STEP 50 |
| 48 | + |
| 49 | +/* |
| 50 | + * Function to truncate an unsigned 64-bit number |
| 51 | + * by x bits and return 32-bit unsigned number. This |
| 52 | + * function also takes care of rounding while truncating |
| 53 | + */ |
| 54 | +static uint32_t vol_shift_64(uint64_t i, uint32_t x) |
| 55 | +{ |
| 56 | + if (x == 0) |
| 57 | + return (uint32_t)i; |
| 58 | + |
| 59 | + /* do not truncate more than 32 bits */ |
| 60 | + if (x > 32) |
| 61 | + x = 32; |
| 62 | + |
| 63 | + return (uint32_t)(((i >> (x - 1)) + 1) >> 1); |
| 64 | +} |
| 65 | + |
| 66 | +/* |
| 67 | + * Function to compute a ** exp where, |
| 68 | + * a is a fractional number represented by a fixed-point integer with a fractional word length |
| 69 | + * of "fwl" |
| 70 | + * exp is an integer |
| 71 | + * fwl is the fractional word length |
| 72 | + * Return value is a fractional number represented by a fixed-point integer with a fractional |
| 73 | + * word length of "fwl" |
| 74 | + */ |
| 75 | +static uint32_t vol_pow32(uint32_t a, int exp, uint32_t fwl) |
| 76 | +{ |
| 77 | + int i, iter; |
| 78 | + uint32_t power = 1 << fwl; |
| 79 | + unsigned long long numerator; |
| 80 | + |
| 81 | + /* if exponent is 0, return 1 */ |
| 82 | + if (exp == 0) |
| 83 | + return power; |
| 84 | + |
| 85 | + /* determine the number of iterations based on the exponent */ |
| 86 | + if (exp < 0) |
| 87 | + iter = exp * -1; |
| 88 | + else |
| 89 | + iter = exp; |
| 90 | + |
| 91 | + /* multiply a "iter" times to compute power */ |
| 92 | + for (i = 0; i < iter; i++) { |
| 93 | + /* |
| 94 | + * Product of 2 Qx.fwl fixed-point numbers yields a Q2*x.2*fwl |
| 95 | + * Truncate product back to fwl fractional bits with rounding |
| 96 | + */ |
| 97 | + power = vol_shift_64((uint64_t)power * a, fwl); |
| 98 | + } |
| 99 | + |
| 100 | + if (exp > 0) { |
| 101 | + /* if exp is positive, return the result */ |
| 102 | + return power; |
| 103 | + } |
| 104 | + |
| 105 | + /* if exp is negative, return the multiplicative inverse */ |
| 106 | + numerator = (uint64_t)1 << (fwl << 1); |
| 107 | + numerator /= power; |
| 108 | + |
| 109 | + return (uint32_t)numerator; |
| 110 | +} |
| 111 | + |
| 112 | +/* |
| 113 | + * Function to calculate volume gain from TLV data. |
| 114 | + * This function can only handle gain steps that are multiples of 0.5 dB |
| 115 | + */ |
| 116 | +static uint32_t vol_compute_gain(uint32_t value, struct snd_soc_tplg_tlv_dbscale *scale) |
| 117 | +{ |
| 118 | + int dB_gain; |
| 119 | + uint32_t linear_gain; |
| 120 | + int f_step; |
| 121 | + |
| 122 | + /* mute volume */ |
| 123 | + if (value == 0 && scale->mute) |
| 124 | + return 0; |
| 125 | + |
| 126 | + /* compute dB gain from tlv. tlv_step in topology is multiplied by 100 */ |
| 127 | + dB_gain = (int)scale->min / 100 + (value * scale->step) / 100; |
| 128 | + |
| 129 | + /* compute linear gain represented by fixed-point int with VOLUME_FWL fractional bits */ |
| 130 | + linear_gain = vol_pow32(VOL_TWENTIETH_ROOT_OF_TEN, dB_gain, VOLUME_FWL); |
| 131 | + |
| 132 | + /* extract the fractional part of volume step */ |
| 133 | + f_step = scale->step - (scale->step / 100); |
| 134 | + |
| 135 | + /* if volume step is an odd multiple of 0.5 dB */ |
| 136 | + if (f_step == VOL_HALF_DB_STEP && (value & 1)) |
| 137 | + linear_gain = vol_shift_64((uint64_t)linear_gain * VOL_FORTIETH_ROOT_OF_TEN, |
| 138 | + VOLUME_FWL); |
| 139 | + |
| 140 | + return linear_gain; |
| 141 | +} |
| 142 | + |
| 143 | +/* helper function to add new kcontrols to the list of kcontrols in the global context */ |
| 144 | +int plug_kcontrol_cb_new(struct snd_soc_tplg_ctl_hdr *tplg_ctl, void *_comp, void *arg) |
| 145 | +{ |
| 146 | + snd_sof_plug_t *plug = arg; |
| 147 | + struct plug_shm_glb_state *glb = plug->glb_ctx.addr; |
| 148 | + struct plug_shm_ctl *ctl; |
| 149 | + |
| 150 | + if (glb->num_ctls >= MAX_CTLS) { |
| 151 | + SNDERR("Failed to add a new control. Too many controls already\n"); |
| 152 | + return -EINVAL; |
| 153 | + } |
| 154 | + |
| 155 | + switch (tplg_ctl->type) { |
| 156 | + case SND_SOC_TPLG_CTL_VOLSW: |
| 157 | + case SND_SOC_TPLG_CTL_VOLSW_SX: |
| 158 | + case SND_SOC_TPLG_CTL_VOLSW_XR_SX: |
| 159 | + { |
| 160 | + struct snd_soc_tplg_mixer_control *tplg_mixer = |
| 161 | + (struct snd_soc_tplg_mixer_control *)tplg_ctl; |
| 162 | + struct tplg_comp_info *comp_info = _comp; |
| 163 | + struct snd_soc_tplg_ctl_tlv *tlv; |
| 164 | + struct snd_soc_tplg_tlv_dbscale *scale; |
| 165 | + int i; |
| 166 | + |
| 167 | + glb->size += sizeof(struct plug_shm_ctl); |
| 168 | + ctl = &glb->ctl[glb->num_ctls++]; |
| 169 | + ctl->module_id = comp_info->module_id; |
| 170 | + ctl->instance_id = comp_info->instance_id; |
| 171 | + ctl->mixer_ctl = *tplg_mixer; |
| 172 | + tlv = &tplg_ctl->tlv; |
| 173 | + scale = &tlv->scale; |
| 174 | + |
| 175 | + /* populate the volume table */ |
| 176 | + for (i = 0; i < tplg_mixer->max + 1 ; i++) { |
| 177 | + uint32_t val = vol_compute_gain(i, scale); |
| 178 | + |
| 179 | + /* Can be over Q1.31, need to saturate */ |
| 180 | + uint64_t q31val = ((uint64_t)val) << 15; |
| 181 | + |
| 182 | + ctl->volume_table[i] = q31val > SOF_IPC4_VOL_ZERO_DB ? |
| 183 | + SOF_IPC4_VOL_ZERO_DB : q31val; |
| 184 | + } |
| 185 | + break; |
| 186 | + } |
| 187 | + case SND_SOC_TPLG_CTL_ENUM: |
| 188 | + case SND_SOC_TPLG_CTL_ENUM_VALUE: |
| 189 | + case SND_SOC_TPLG_CTL_BYTES: |
| 190 | + break; |
| 191 | + case SND_SOC_TPLG_CTL_RANGE: |
| 192 | + case SND_SOC_TPLG_CTL_STROBE: |
| 193 | + default: |
| 194 | + SNDERR("Invalid ctl type %d\n", tplg_ctl->type); |
| 195 | + return -EINVAL; |
| 196 | + } |
| 197 | + |
| 198 | + return 0; |
| 199 | +} |
0 commit comments