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