mirror of
https://github.com/brain-hackers/u-boot-brain
synced 2024-08-31 06:18:51 +09:00
![Simon Glass](/assets/img/avatar_default.png)
This construct is quite long-winded. In earlier days it made some sense since auto-allocation was a strange concept. But with driver model now used pretty universally, we can shorten this to 'auto'. This reduces verbosity and makes it easier to read. Coincidentally it also ensures that every declaration is on one line, thus making dtoc's job easier. Signed-off-by: Simon Glass <sjg@chromium.org>
177 lines
3.7 KiB
C
177 lines
3.7 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* Copyright 2018 Google LLC
|
|
* Written by Simon Glass <sjg@chromium.org>
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <dm.h>
|
|
#include <i2s.h>
|
|
#include <log.h>
|
|
#include <malloc.h>
|
|
#include <sound.h>
|
|
#include <linux/delay.h>
|
|
|
|
#define SOUND_BITS_IN_BYTE 8
|
|
|
|
int sound_setup(struct udevice *dev)
|
|
{
|
|
struct sound_ops *ops = sound_get_ops(dev);
|
|
|
|
if (!ops->setup)
|
|
return 0;
|
|
|
|
return ops->setup(dev);
|
|
}
|
|
|
|
int sound_play(struct udevice *dev, void *data, uint data_size)
|
|
{
|
|
struct sound_ops *ops = sound_get_ops(dev);
|
|
|
|
if (!ops->play)
|
|
return -ENOSYS;
|
|
|
|
return ops->play(dev, data, data_size);
|
|
}
|
|
|
|
int sound_stop_play(struct udevice *dev)
|
|
{
|
|
struct sound_ops *ops = sound_get_ops(dev);
|
|
|
|
if (!ops->play)
|
|
return -ENOSYS;
|
|
|
|
return ops->stop_play(dev);
|
|
}
|
|
|
|
int sound_start_beep(struct udevice *dev, int frequency_hz)
|
|
{
|
|
struct sound_ops *ops = sound_get_ops(dev);
|
|
|
|
if (!ops->start_beep)
|
|
return -ENOSYS;
|
|
|
|
return ops->start_beep(dev, frequency_hz);
|
|
}
|
|
|
|
int sound_stop_beep(struct udevice *dev)
|
|
{
|
|
struct sound_ops *ops = sound_get_ops(dev);
|
|
|
|
if (!ops->stop_beep)
|
|
return -ENOSYS;
|
|
|
|
return ops->stop_beep(dev);
|
|
}
|
|
|
|
int sound_beep(struct udevice *dev, int msecs, int frequency_hz)
|
|
{
|
|
struct sound_uc_priv *uc_priv = dev_get_uclass_priv(dev);
|
|
struct i2s_uc_priv *i2s_uc_priv;
|
|
unsigned short *data;
|
|
uint data_size;
|
|
int ret;
|
|
|
|
ret = sound_setup(dev);
|
|
if (ret && ret != -EALREADY)
|
|
return ret;
|
|
|
|
/* Try using the beep interface if available */
|
|
ret = sound_start_beep(dev, frequency_hz);
|
|
if (ret != -ENOSYS) {
|
|
if (ret)
|
|
return ret;
|
|
mdelay(msecs);
|
|
ret = sound_stop_beep(dev);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Buffer length computation */
|
|
i2s_uc_priv = dev_get_uclass_priv(uc_priv->i2s);
|
|
data_size = i2s_uc_priv->samplingrate * i2s_uc_priv->channels;
|
|
data_size *= (i2s_uc_priv->bitspersample / SOUND_BITS_IN_BYTE);
|
|
data = malloc(data_size);
|
|
if (!data) {
|
|
debug("%s: malloc failed\n", __func__);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
sound_create_square_wave(i2s_uc_priv->samplingrate, data, data_size,
|
|
frequency_hz, i2s_uc_priv->channels);
|
|
|
|
ret = 0;
|
|
while (msecs >= 1000) {
|
|
ret = sound_play(dev, data, data_size);
|
|
if (ret)
|
|
break;
|
|
msecs -= 1000;
|
|
}
|
|
if (!ret && msecs) {
|
|
unsigned long size =
|
|
(data_size * msecs) / (sizeof(int) * 1000);
|
|
|
|
ret = sound_play(dev, data, size);
|
|
}
|
|
sound_stop_play(dev);
|
|
|
|
free(data);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int sound_find_codec_i2s(struct udevice *dev)
|
|
{
|
|
struct sound_uc_priv *uc_priv = dev_get_uclass_priv(dev);
|
|
struct ofnode_phandle_args args;
|
|
ofnode node;
|
|
int ret;
|
|
|
|
/* First the codec */
|
|
node = ofnode_find_subnode(dev_ofnode(dev), "codec");
|
|
if (!ofnode_valid(node)) {
|
|
debug("Failed to find /cpu subnode\n");
|
|
return -EINVAL;
|
|
}
|
|
ret = ofnode_parse_phandle_with_args(node, "sound-dai",
|
|
"#sound-dai-cells", 0, 0, &args);
|
|
if (ret) {
|
|
debug("Cannot find phandle: %d\n", ret);
|
|
return ret;
|
|
}
|
|
ret = uclass_get_device_by_ofnode(UCLASS_AUDIO_CODEC, args.node,
|
|
&uc_priv->codec);
|
|
if (ret) {
|
|
debug("Cannot find codec: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
/* Now the i2s */
|
|
node = ofnode_find_subnode(dev_ofnode(dev), "cpu");
|
|
if (!ofnode_valid(node)) {
|
|
debug("Failed to find /cpu subnode\n");
|
|
return -EINVAL;
|
|
}
|
|
ret = ofnode_parse_phandle_with_args(node, "sound-dai",
|
|
"#sound-dai-cells", 0, 0, &args);
|
|
if (ret) {
|
|
debug("Cannot find phandle: %d\n", ret);
|
|
return ret;
|
|
}
|
|
ret = uclass_get_device_by_ofnode(UCLASS_I2S, args.node, &uc_priv->i2s);
|
|
if (ret) {
|
|
debug("Cannot find i2s: %d\n", ret);
|
|
return ret;
|
|
}
|
|
debug("Probed sound '%s' with codec '%s' and i2s '%s'\n", dev->name,
|
|
uc_priv->codec->name, uc_priv->i2s->name);
|
|
|
|
return 0;
|
|
}
|
|
|
|
UCLASS_DRIVER(sound) = {
|
|
.id = UCLASS_SOUND,
|
|
.name = "sound",
|
|
.per_device_auto = sizeof(struct sound_uc_priv),
|
|
};
|