uboot/drivers/sound/sound-uclass.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright 2018 Google LLC
   4 * Written by Simon Glass <sjg@chromium.org>
   5 */
   6
   7#define LOG_CATEGORY UCLASS_SOUND
   8
   9#include <common.h>
  10#include <dm.h>
  11#include <i2s.h>
  12#include <log.h>
  13#include <malloc.h>
  14#include <sound.h>
  15#include <linux/delay.h>
  16
  17#define SOUND_BITS_IN_BYTE 8
  18
  19int sound_setup(struct udevice *dev)
  20{
  21        struct sound_ops *ops = sound_get_ops(dev);
  22
  23        if (!ops->setup)
  24                return 0;
  25
  26        return ops->setup(dev);
  27}
  28
  29int sound_play(struct udevice *dev, void *data, uint data_size)
  30{
  31        struct sound_ops *ops = sound_get_ops(dev);
  32
  33        if (!ops->play)
  34                return -ENOSYS;
  35
  36        return ops->play(dev, data, data_size);
  37}
  38
  39int sound_stop_play(struct udevice *dev)
  40{
  41        struct sound_ops *ops = sound_get_ops(dev);
  42
  43        if (!ops->play)
  44                return -ENOSYS;
  45
  46        return ops->stop_play(dev);
  47}
  48
  49int sound_start_beep(struct udevice *dev, int frequency_hz)
  50{
  51        struct sound_ops *ops = sound_get_ops(dev);
  52
  53        if (!ops->start_beep)
  54                return -ENOSYS;
  55
  56        return ops->start_beep(dev, frequency_hz);
  57}
  58
  59int sound_stop_beep(struct udevice *dev)
  60{
  61        struct sound_ops *ops = sound_get_ops(dev);
  62
  63        if (!ops->stop_beep)
  64                return -ENOSYS;
  65
  66        return ops->stop_beep(dev);
  67}
  68
  69int sound_beep(struct udevice *dev, int msecs, int frequency_hz)
  70{
  71        struct sound_uc_priv *uc_priv = dev_get_uclass_priv(dev);
  72        struct i2s_uc_priv *i2s_uc_priv;
  73        unsigned short *data;
  74        uint data_size;
  75        int ret;
  76
  77        ret = sound_setup(dev);
  78        if (ret && ret != -EALREADY)
  79                return ret;
  80
  81        /* Try using the beep interface if available */
  82        ret = sound_start_beep(dev, frequency_hz);
  83        if (ret != -ENOSYS) {
  84                if (ret)
  85                        return ret;
  86                mdelay(msecs);
  87                ret = sound_stop_beep(dev);
  88
  89                return ret;
  90        }
  91
  92        /* Buffer length computation */
  93        i2s_uc_priv = dev_get_uclass_priv(uc_priv->i2s);
  94        data_size = i2s_uc_priv->samplingrate * i2s_uc_priv->channels;
  95        data_size *= (i2s_uc_priv->bitspersample / SOUND_BITS_IN_BYTE);
  96        data = malloc(data_size);
  97        if (!data) {
  98                debug("%s: malloc failed\n", __func__);
  99                return -ENOMEM;
 100        }
 101
 102        sound_create_square_wave(i2s_uc_priv->samplingrate, data, data_size,
 103                                 frequency_hz, i2s_uc_priv->channels);
 104
 105        ret = 0;
 106        while (msecs >= 1000) {
 107                ret = sound_play(dev, data, data_size);
 108                if (ret)
 109                        break;
 110                msecs -= 1000;
 111        }
 112        if (!ret && msecs) {
 113                unsigned long size =
 114                        (data_size * msecs) / (sizeof(int) * 1000);
 115
 116                ret = sound_play(dev, data, size);
 117        }
 118        sound_stop_play(dev);
 119
 120        free(data);
 121
 122        return ret;
 123}
 124
 125int sound_find_codec_i2s(struct udevice *dev)
 126{
 127        struct sound_uc_priv *uc_priv = dev_get_uclass_priv(dev);
 128        struct ofnode_phandle_args args;
 129        ofnode node;
 130        int ret;
 131
 132        /* First the codec */
 133        node = ofnode_find_subnode(dev_ofnode(dev), "codec");
 134        if (!ofnode_valid(node)) {
 135                debug("Failed to find /cpu subnode\n");
 136                return -EINVAL;
 137        }
 138        ret = ofnode_parse_phandle_with_args(node, "sound-dai",
 139                                             "#sound-dai-cells", 0, 0, &args);
 140        if (ret) {
 141                debug("Cannot find phandle: %d\n", ret);
 142                return ret;
 143        }
 144        ret = uclass_get_device_by_ofnode(UCLASS_AUDIO_CODEC, args.node,
 145                                          &uc_priv->codec);
 146        if (ret) {
 147                debug("Cannot find codec: %d\n", ret);
 148                return ret;
 149        }
 150
 151        /* Now the i2s */
 152        node = ofnode_find_subnode(dev_ofnode(dev), "cpu");
 153        if (!ofnode_valid(node)) {
 154                debug("Failed to find /cpu subnode\n");
 155                return -EINVAL;
 156        }
 157        ret = ofnode_parse_phandle_with_args(node, "sound-dai",
 158                                             "#sound-dai-cells", 0, 0, &args);
 159        if (ret) {
 160                debug("Cannot find phandle: %d\n", ret);
 161                return ret;
 162        }
 163        ret = uclass_get_device_by_ofnode(UCLASS_I2S, args.node, &uc_priv->i2s);
 164        if (ret) {
 165                debug("Cannot find i2s: %d\n", ret);
 166                return ret;
 167        }
 168        debug("Probed sound '%s' with codec '%s' and i2s '%s'\n", dev->name,
 169              uc_priv->codec->name, uc_priv->i2s->name);
 170
 171        return 0;
 172}
 173
 174UCLASS_DRIVER(sound) = {
 175        .id             = UCLASS_SOUND,
 176        .name           = "sound",
 177        .per_device_auto        = sizeof(struct sound_uc_priv),
 178};
 179