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