linux/sound/soc/sof/control.c
<<
>>
Prefs
   1// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
   2//
   3// This file is provided under a dual BSD/GPLv2 license.  When using or
   4// redistributing this file, you may do so under either license.
   5//
   6// Copyright(c) 2018 Intel Corporation. All rights reserved.
   7//
   8// Author: Liam Girdwood <liam.r.girdwood@linux.intel.com>
   9//
  10
  11/* Mixer Controls */
  12
  13#include <linux/pm_runtime.h>
  14#include <linux/leds.h>
  15#include "sof-priv.h"
  16
  17static void update_mute_led(struct snd_sof_control *scontrol,
  18                            struct snd_kcontrol *kcontrol,
  19                            struct snd_ctl_elem_value *ucontrol)
  20{
  21        unsigned int temp = 0;
  22        unsigned int mask;
  23        int i;
  24
  25        mask = 1U << snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
  26
  27        for (i = 0; i < scontrol->num_channels; i++) {
  28                if (ucontrol->value.integer.value[i]) {
  29                        temp |= mask;
  30                        break;
  31                }
  32        }
  33
  34        if (temp == scontrol->led_ctl.led_value)
  35                return;
  36
  37        scontrol->led_ctl.led_value = temp;
  38
  39#if IS_REACHABLE(CONFIG_LEDS_TRIGGER_AUDIO)
  40        if (!scontrol->led_ctl.direction)
  41                ledtrig_audio_set(LED_AUDIO_MUTE, temp ? LED_OFF : LED_ON);
  42        else
  43                ledtrig_audio_set(LED_AUDIO_MICMUTE, temp ? LED_OFF : LED_ON);
  44#endif
  45}
  46
  47static inline u32 mixer_to_ipc(unsigned int value, u32 *volume_map, int size)
  48{
  49        if (value >= size)
  50                return volume_map[size - 1];
  51
  52        return volume_map[value];
  53}
  54
  55static inline u32 ipc_to_mixer(u32 value, u32 *volume_map, int size)
  56{
  57        int i;
  58
  59        for (i = 0; i < size; i++) {
  60                if (volume_map[i] >= value)
  61                        return i;
  62        }
  63
  64        return i - 1;
  65}
  66
  67int snd_sof_volume_get(struct snd_kcontrol *kcontrol,
  68                       struct snd_ctl_elem_value *ucontrol)
  69{
  70        struct soc_mixer_control *sm =
  71                (struct soc_mixer_control *)kcontrol->private_value;
  72        struct snd_sof_control *scontrol = sm->dobj.private;
  73        struct sof_ipc_ctrl_data *cdata = scontrol->control_data;
  74        unsigned int i, channels = scontrol->num_channels;
  75
  76        /* read back each channel */
  77        for (i = 0; i < channels; i++)
  78                ucontrol->value.integer.value[i] =
  79                        ipc_to_mixer(cdata->chanv[i].value,
  80                                     scontrol->volume_table, sm->max + 1);
  81
  82        return 0;
  83}
  84
  85int snd_sof_volume_put(struct snd_kcontrol *kcontrol,
  86                       struct snd_ctl_elem_value *ucontrol)
  87{
  88        struct soc_mixer_control *sm =
  89                (struct soc_mixer_control *)kcontrol->private_value;
  90        struct snd_sof_control *scontrol = sm->dobj.private;
  91        struct snd_sof_dev *sdev = scontrol->sdev;
  92        struct sof_ipc_ctrl_data *cdata = scontrol->control_data;
  93        unsigned int i, channels = scontrol->num_channels;
  94        bool change = false;
  95        u32 value;
  96
  97        /* update each channel */
  98        for (i = 0; i < channels; i++) {
  99                value = mixer_to_ipc(ucontrol->value.integer.value[i],
 100                                     scontrol->volume_table, sm->max + 1);
 101                change = change || (value != cdata->chanv[i].value);
 102                cdata->chanv[i].channel = i;
 103                cdata->chanv[i].value = value;
 104        }
 105
 106        /* notify DSP of mixer updates */
 107        if (pm_runtime_active(sdev->dev))
 108                snd_sof_ipc_set_get_comp_data(sdev->ipc, scontrol,
 109                                              SOF_IPC_COMP_SET_VALUE,
 110                                              SOF_CTRL_TYPE_VALUE_CHAN_GET,
 111                                              SOF_CTRL_CMD_VOLUME,
 112                                              true);
 113        return change;
 114}
 115
 116int snd_sof_switch_get(struct snd_kcontrol *kcontrol,
 117                       struct snd_ctl_elem_value *ucontrol)
 118{
 119        struct soc_mixer_control *sm =
 120                (struct soc_mixer_control *)kcontrol->private_value;
 121        struct snd_sof_control *scontrol = sm->dobj.private;
 122        struct sof_ipc_ctrl_data *cdata = scontrol->control_data;
 123        unsigned int i, channels = scontrol->num_channels;
 124
 125        /* read back each channel */
 126        for (i = 0; i < channels; i++)
 127                ucontrol->value.integer.value[i] = cdata->chanv[i].value;
 128
 129        return 0;
 130}
 131
 132int snd_sof_switch_put(struct snd_kcontrol *kcontrol,
 133                       struct snd_ctl_elem_value *ucontrol)
 134{
 135        struct soc_mixer_control *sm =
 136                (struct soc_mixer_control *)kcontrol->private_value;
 137        struct snd_sof_control *scontrol = sm->dobj.private;
 138        struct snd_sof_dev *sdev = scontrol->sdev;
 139        struct sof_ipc_ctrl_data *cdata = scontrol->control_data;
 140        unsigned int i, channels = scontrol->num_channels;
 141        bool change = false;
 142        u32 value;
 143
 144        /* update each channel */
 145        for (i = 0; i < channels; i++) {
 146                value = ucontrol->value.integer.value[i];
 147                change = change || (value != cdata->chanv[i].value);
 148                cdata->chanv[i].channel = i;
 149                cdata->chanv[i].value = value;
 150        }
 151
 152        if (scontrol->led_ctl.use_led)
 153                update_mute_led(scontrol, kcontrol, ucontrol);
 154
 155        /* notify DSP of mixer updates */
 156        if (pm_runtime_active(sdev->dev))
 157                snd_sof_ipc_set_get_comp_data(sdev->ipc, scontrol,
 158                                              SOF_IPC_COMP_SET_VALUE,
 159                                              SOF_CTRL_TYPE_VALUE_CHAN_GET,
 160                                              SOF_CTRL_CMD_SWITCH,
 161                                              true);
 162
 163        return change;
 164}
 165
 166int snd_sof_enum_get(struct snd_kcontrol *kcontrol,
 167                     struct snd_ctl_elem_value *ucontrol)
 168{
 169        struct soc_enum *se =
 170                (struct soc_enum *)kcontrol->private_value;
 171        struct snd_sof_control *scontrol = se->dobj.private;
 172        struct sof_ipc_ctrl_data *cdata = scontrol->control_data;
 173        unsigned int i, channels = scontrol->num_channels;
 174
 175        /* read back each channel */
 176        for (i = 0; i < channels; i++)
 177                ucontrol->value.enumerated.item[i] = cdata->chanv[i].value;
 178
 179        return 0;
 180}
 181
 182int snd_sof_enum_put(struct snd_kcontrol *kcontrol,
 183                     struct snd_ctl_elem_value *ucontrol)
 184{
 185        struct soc_enum *se =
 186                (struct soc_enum *)kcontrol->private_value;
 187        struct snd_sof_control *scontrol = se->dobj.private;
 188        struct snd_sof_dev *sdev = scontrol->sdev;
 189        struct sof_ipc_ctrl_data *cdata = scontrol->control_data;
 190        unsigned int i, channels = scontrol->num_channels;
 191        bool change = false;
 192        u32 value;
 193
 194        /* update each channel */
 195        for (i = 0; i < channels; i++) {
 196                value = ucontrol->value.enumerated.item[i];
 197                change = change || (value != cdata->chanv[i].value);
 198                cdata->chanv[i].channel = i;
 199                cdata->chanv[i].value = value;
 200        }
 201
 202        /* notify DSP of enum updates */
 203        if (pm_runtime_active(sdev->dev))
 204                snd_sof_ipc_set_get_comp_data(sdev->ipc, scontrol,
 205                                              SOF_IPC_COMP_SET_VALUE,
 206                                              SOF_CTRL_TYPE_VALUE_CHAN_GET,
 207                                              SOF_CTRL_CMD_ENUM,
 208                                              true);
 209
 210        return change;
 211}
 212
 213int snd_sof_bytes_get(struct snd_kcontrol *kcontrol,
 214                      struct snd_ctl_elem_value *ucontrol)
 215{
 216        struct soc_bytes_ext *be =
 217                (struct soc_bytes_ext *)kcontrol->private_value;
 218        struct snd_sof_control *scontrol = be->dobj.private;
 219        struct snd_sof_dev *sdev = scontrol->sdev;
 220        struct sof_ipc_ctrl_data *cdata = scontrol->control_data;
 221        struct sof_abi_hdr *data = cdata->data;
 222        size_t size;
 223        int ret = 0;
 224
 225        if (be->max > sizeof(ucontrol->value.bytes.data)) {
 226                dev_err_ratelimited(sdev->dev,
 227                                    "error: data max %d exceeds ucontrol data array size\n",
 228                                    be->max);
 229                return -EINVAL;
 230        }
 231
 232        size = data->size + sizeof(*data);
 233        if (size > be->max) {
 234                dev_err_ratelimited(sdev->dev,
 235                                    "error: DSP sent %zu bytes max is %d\n",
 236                                    size, be->max);
 237                ret = -EINVAL;
 238                goto out;
 239        }
 240
 241        /* copy back to kcontrol */
 242        memcpy(ucontrol->value.bytes.data, data, size);
 243
 244out:
 245        return ret;
 246}
 247
 248int snd_sof_bytes_put(struct snd_kcontrol *kcontrol,
 249                      struct snd_ctl_elem_value *ucontrol)
 250{
 251        struct soc_bytes_ext *be =
 252                (struct soc_bytes_ext *)kcontrol->private_value;
 253        struct snd_sof_control *scontrol = be->dobj.private;
 254        struct snd_sof_dev *sdev = scontrol->sdev;
 255        struct sof_ipc_ctrl_data *cdata = scontrol->control_data;
 256        struct sof_abi_hdr *data = cdata->data;
 257        size_t size = data->size + sizeof(*data);
 258
 259        if (be->max > sizeof(ucontrol->value.bytes.data)) {
 260                dev_err_ratelimited(sdev->dev,
 261                                    "error: data max %d exceeds ucontrol data array size\n",
 262                                    be->max);
 263                return -EINVAL;
 264        }
 265
 266        if (size > be->max) {
 267                dev_err_ratelimited(sdev->dev,
 268                                    "error: size too big %zu bytes max is %d\n",
 269                                    size, be->max);
 270                return -EINVAL;
 271        }
 272
 273        /* copy from kcontrol */
 274        memcpy(data, ucontrol->value.bytes.data, size);
 275
 276        /* notify DSP of byte control updates */
 277        if (pm_runtime_active(sdev->dev))
 278                snd_sof_ipc_set_get_comp_data(sdev->ipc, scontrol,
 279                                              SOF_IPC_COMP_SET_DATA,
 280                                              SOF_CTRL_TYPE_DATA_SET,
 281                                              scontrol->cmd,
 282                                              true);
 283
 284        return 0;
 285}
 286
 287int snd_sof_bytes_ext_put(struct snd_kcontrol *kcontrol,
 288                          const unsigned int __user *binary_data,
 289                          unsigned int size)
 290{
 291        struct soc_bytes_ext *be =
 292                (struct soc_bytes_ext *)kcontrol->private_value;
 293        struct snd_sof_control *scontrol = be->dobj.private;
 294        struct snd_sof_dev *sdev = scontrol->sdev;
 295        struct sof_ipc_ctrl_data *cdata = scontrol->control_data;
 296        struct snd_ctl_tlv header;
 297        const struct snd_ctl_tlv __user *tlvd =
 298                (const struct snd_ctl_tlv __user *)binary_data;
 299
 300        /*
 301         * The beginning of bytes data contains a header from where
 302         * the length (as bytes) is needed to know the correct copy
 303         * length of data from tlvd->tlv.
 304         */
 305        if (copy_from_user(&header, tlvd, sizeof(const struct snd_ctl_tlv)))
 306                return -EFAULT;
 307
 308        /* be->max is coming from topology */
 309        if (header.length > be->max) {
 310                dev_err_ratelimited(sdev->dev, "error: Bytes data size %d exceeds max %d.\n",
 311                                    header.length, be->max);
 312                return -EINVAL;
 313        }
 314
 315        /* Check that header id matches the command */
 316        if (header.numid != scontrol->cmd) {
 317                dev_err_ratelimited(sdev->dev,
 318                                    "error: incorrect numid %d\n",
 319                                    header.numid);
 320                return -EINVAL;
 321        }
 322
 323        if (copy_from_user(cdata->data, tlvd->tlv, header.length))
 324                return -EFAULT;
 325
 326        if (cdata->data->magic != SOF_ABI_MAGIC) {
 327                dev_err_ratelimited(sdev->dev,
 328                                    "error: Wrong ABI magic 0x%08x.\n",
 329                                    cdata->data->magic);
 330                return -EINVAL;
 331        }
 332
 333        if (SOF_ABI_VERSION_INCOMPATIBLE(SOF_ABI_VERSION, cdata->data->abi)) {
 334                dev_err_ratelimited(sdev->dev, "error: Incompatible ABI version 0x%08x.\n",
 335                                    cdata->data->abi);
 336                return -EINVAL;
 337        }
 338
 339        if (cdata->data->size + sizeof(const struct sof_abi_hdr) > be->max) {
 340                dev_err_ratelimited(sdev->dev, "error: Mismatch in ABI data size (truncated?).\n");
 341                return -EINVAL;
 342        }
 343
 344        /* notify DSP of byte control updates */
 345        if (pm_runtime_active(sdev->dev))
 346                snd_sof_ipc_set_get_comp_data(sdev->ipc, scontrol,
 347                                              SOF_IPC_COMP_SET_DATA,
 348                                              SOF_CTRL_TYPE_DATA_SET,
 349                                              scontrol->cmd,
 350                                              true);
 351
 352        return 0;
 353}
 354
 355int snd_sof_bytes_ext_get(struct snd_kcontrol *kcontrol,
 356                          unsigned int __user *binary_data,
 357                          unsigned int size)
 358{
 359        struct soc_bytes_ext *be =
 360                (struct soc_bytes_ext *)kcontrol->private_value;
 361        struct snd_sof_control *scontrol = be->dobj.private;
 362        struct snd_sof_dev *sdev = scontrol->sdev;
 363        struct sof_ipc_ctrl_data *cdata = scontrol->control_data;
 364        struct snd_ctl_tlv header;
 365        struct snd_ctl_tlv __user *tlvd =
 366                (struct snd_ctl_tlv __user *)binary_data;
 367        int data_size;
 368        int ret = 0;
 369
 370        /*
 371         * Decrement the limit by ext bytes header size to
 372         * ensure the user space buffer is not exceeded.
 373         */
 374        size -= sizeof(const struct snd_ctl_tlv);
 375
 376        /* set the ABI header values */
 377        cdata->data->magic = SOF_ABI_MAGIC;
 378        cdata->data->abi = SOF_ABI_VERSION;
 379
 380        /* Prevent read of other kernel data or possibly corrupt response */
 381        data_size = cdata->data->size + sizeof(const struct sof_abi_hdr);
 382
 383        /* check data size doesn't exceed max coming from topology */
 384        if (data_size > be->max) {
 385                dev_err_ratelimited(sdev->dev, "error: user data size %d exceeds max size %d.\n",
 386                                    data_size, be->max);
 387                ret = -EINVAL;
 388                goto out;
 389        }
 390
 391        header.numid = scontrol->cmd;
 392        header.length = data_size;
 393        if (copy_to_user(tlvd, &header, sizeof(const struct snd_ctl_tlv))) {
 394                ret = -EFAULT;
 395                goto out;
 396        }
 397
 398        if (copy_to_user(tlvd->tlv, cdata->data, data_size))
 399                ret = -EFAULT;
 400
 401out:
 402        return ret;
 403}
 404