summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSascha Hauer <s.hauer@pengutronix.de>2021-02-22 10:39:39 +0100
committerSascha Hauer <s.hauer@pengutronix.de>2021-02-22 10:39:39 +0100
commit2b1f426e511bf5b41c84d05ec09949ebd5dc216e (patch)
treefcf06dba00432686c8c0bd5137357d2aabf356f8
parent774f731a63500deec5d529bb17a75a983f3a4629 (diff)
parentfdb315c4a0764c43916236363a0060972108f5d5 (diff)
downloadbarebox-2b1f426e511bf5b41c84d05ec09949ebd5dc216e.tar.gz
barebox-2b1f426e511bf5b41c84d05ec09949ebd5dc216e.tar.xz
Merge branch 'for-next/sound'
-rw-r--r--arch/sandbox/Kconfig3
-rw-r--r--arch/sandbox/Makefile6
-rw-r--r--arch/sandbox/configs/sandbox_defconfig1
-rw-r--r--arch/sandbox/dts/sandbox.dts4
-rw-r--r--arch/sandbox/mach-sandbox/include/mach/linux.h22
-rw-r--r--arch/sandbox/os/Makefile4
-rw-r--r--arch/sandbox/os/sdl.c184
-rw-r--r--commands/Kconfig7
-rw-r--r--commands/Makefile1
-rw-r--r--commands/beep.c99
-rw-r--r--drivers/Kconfig1
-rw-r--r--drivers/Makefile1
-rw-r--r--drivers/sound/Kconfig38
-rw-r--r--drivers/sound/Makefile5
-rw-r--r--drivers/sound/core.c121
-rw-r--r--drivers/sound/gpio-beeper.c77
-rw-r--r--drivers/sound/pwm-beeper.c124
-rw-r--r--drivers/sound/sdl.c87
-rw-r--r--drivers/sound/synth.c43
-rw-r--r--drivers/video/Kconfig1
-rw-r--r--drivers/video/sdl.c37
-rw-r--r--include/linux/fixp-arith.h144
-rw-r--r--include/poller.h4
-rw-r--r--include/pwm.h33
-rw-r--r--include/sound.h77
25 files changed, 1033 insertions, 91 deletions
diff --git a/arch/sandbox/Kconfig b/arch/sandbox/Kconfig
index 5d1702480f..1a4e3bacf6 100644
--- a/arch/sandbox/Kconfig
+++ b/arch/sandbox/Kconfig
@@ -44,3 +44,6 @@ config 64BIT
config SANDBOX_LINUX_I386
bool "32-bit x86 barebox" if CC_HAS_LINUX_I386_SUPPORT
+
+config SDL
+ bool
diff --git a/arch/sandbox/Makefile b/arch/sandbox/Makefile
index 17f9a298d7..ea594944e4 100644
--- a/arch/sandbox/Makefile
+++ b/arch/sandbox/Makefile
@@ -37,8 +37,8 @@ archprepare: maketools
PHONY += maketools
-ifeq ($(CONFIG_DRIVER_VIDEO_SDL),y)
-SDL_LIBS := $(shell pkg-config sdl --libs)
+ifeq ($(CONFIG_SDL),y)
+SDL_LIBS := $(shell pkg-config sdl2 --libs)
endif
ifeq ($(CONFIG_GPIO_LIBFTDI1),y)
@@ -64,7 +64,7 @@ endif
BAREBOX_LDFLAGS += \
-Wl,-T,$(BAREBOX_LDS) \
-Wl,--whole-archive $(BAREBOX_OBJS) -Wl,--no-whole-archive \
- -lrt -lpthread $(SDL_LIBS) $(FTDI1_LIBS) \
+ -lrt $(SDL_LIBS) $(FTDI1_LIBS) \
$(SANITIZER_LIBS)
cmd_barebox__ = $(CC) -o $@ $(BAREBOX_LDFLAGS)
diff --git a/arch/sandbox/configs/sandbox_defconfig b/arch/sandbox/configs/sandbox_defconfig
index dcd0557326..d9d96d9481 100644
--- a/arch/sandbox/configs/sandbox_defconfig
+++ b/arch/sandbox/configs/sandbox_defconfig
@@ -108,6 +108,7 @@ CONFIG_MTD=y
CONFIG_MTD_M25P80=y
CONFIG_VIDEO=y
CONFIG_FRAMEBUFFER_CONSOLE=y
+CONFIG_SOUND=y
CONFIG_MFD_SYSCON=y
CONFIG_STATE_DRV=y
CONFIG_UBOOTVAR=y
diff --git a/arch/sandbox/dts/sandbox.dts b/arch/sandbox/dts/sandbox.dts
index afe48154c4..ef1fa7b866 100644
--- a/arch/sandbox/dts/sandbox.dts
+++ b/arch/sandbox/dts/sandbox.dts
@@ -94,4 +94,8 @@
compatible = "barebox,sandbox-watchdog";
barebox,reset-source = <&stickypage 0>;
};
+
+ sound {
+ compatible = "barebox,sandbox-sound";
+ };
};
diff --git a/arch/sandbox/mach-sandbox/include/mach/linux.h b/arch/sandbox/mach-sandbox/include/mach/linux.h
index f047c83e17..831e170d90 100644
--- a/arch/sandbox/mach-sandbox/include/mach/linux.h
+++ b/arch/sandbox/mach-sandbox/include/mach/linux.h
@@ -38,13 +38,21 @@ struct linux_console_data {
extern int sdl_xres;
extern int sdl_yres;
-void sdl_close(void);
-int sdl_open(int xres, int yres, int bpp, void* buf);
-void sdl_stop_timer(void);
-void sdl_start_timer(void);
-void sdl_get_bitfield_rgba(struct fb_bitfield *r, struct fb_bitfield *g,
- struct fb_bitfield *b, struct fb_bitfield *a);
-void sdl_setpixel(int x, int y, uint8_t r, uint8_t g, uint8_t b, uint8_t a);
+struct sdl_fb_info {
+ void *screen_base;
+ int xres;
+ int yres;
+ int bpp;
+ int rmask, gmask, bmask, amask;
+};
+int sdl_video_open(const struct sdl_fb_info *);
+void sdl_video_pause(void);
+void sdl_video_close(void);
+
+int sdl_sound_init(unsigned sample_rate);
+int sdl_sound_play(const void *data, unsigned nsamples);
+void sdl_sound_stop(void);
+void sdl_sound_close(void);
struct ft2232_bitbang;
struct ft2232_bitbang *barebox_libftdi1_open(int vendor_id, int device_id,
diff --git a/arch/sandbox/os/Makefile b/arch/sandbox/os/Makefile
index 9a264ca314..fb2c3cfd86 100644
--- a/arch/sandbox/os/Makefile
+++ b/arch/sandbox/os/Makefile
@@ -17,8 +17,8 @@ endif
obj-y = common.o tap.o
obj-$(CONFIG_MALLOC_LIBC) += libc_malloc.o
-CFLAGS_sdl.o = $(shell pkg-config sdl --cflags)
-obj-$(CONFIG_DRIVER_VIDEO_SDL) += sdl.o
+CFLAGS_sdl.o = $(shell pkg-config sdl2 --cflags)
+obj-$(CONFIG_SDL) += sdl.o
CFLAGS_ftdi.o = $(shell pkg-config libftdi1 --cflags)
obj-$(CONFIG_GPIO_LIBFTDI1) += ftdi.o
diff --git a/arch/sandbox/os/sdl.c b/arch/sandbox/os/sdl.c
index 9a35279eb7..13178abfc0 100644
--- a/arch/sandbox/os/sdl.c
+++ b/arch/sandbox/os/sdl.c
@@ -1,103 +1,157 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2012 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
- *
- * GPL v2
+ * Copyright (c) 2021 Ahmad Fatoum
*/
#include <stdio.h>
+#include <stdbool.h>
#include <SDL.h>
-#include <time.h>
-#include <signal.h>
#include <mach/linux.h>
-#include <unistd.h>
-#include <pthread.h>
-struct fb_bitfield {
- uint32_t offset; /* beginning of bitfield */
- uint32_t length; /* length of bitfield */
- uint32_t msb_right; /* != 0 : Most significant bit is */
- /* right */
-};
+static void sdl_perror(const char *what)
+{
+ printf("SDL: Could not %s: %s.\n", what, SDL_GetError());
+}
-static SDL_Surface *real_screen;
-static void *buffer = NULL;
-pthread_t th;
+static struct sdl_fb_info info;
+static SDL_atomic_t shutdown;
+SDL_Window *window;
-static void sdl_copy_buffer(SDL_Surface *screen)
+static int scanout(void *ptr)
{
- if (SDL_MUSTLOCK(screen)) {
- if (SDL_LockSurface(screen) < 0)
- return;
+ SDL_Renderer *renderer;
+ SDL_Surface *surface;
+ SDL_Texture *texture;
+ void *buf = info.screen_base;
+ int ret = -1;
+
+ renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
+ if (!renderer) {
+ sdl_perror("create renderer");
+ return -1;
}
- memcpy(screen->pixels, buffer, screen->pitch * screen->h);
+ surface = SDL_CreateRGBSurface(0, info.xres, info.yres, info.bpp,
+ info.rmask, info.gmask, info.bmask, info.amask);
+ if (!surface) {
+ sdl_perror("create surface");
+ goto destroy_renderer;
+ }
- if(SDL_MUSTLOCK(screen))
- SDL_UnlockSurface(screen);
-}
+ texture = SDL_CreateTextureFromSurface(renderer, surface);
+ if (!texture) {
+ sdl_perror("create texture");
+ goto free_surface;
+ }
-static void *threadStart(void *ptr)
-{
- while (1) {
- usleep(1000 * 100);
+ while (!SDL_AtomicGet(&shutdown)) {
+ SDL_Delay(100);
- sdl_copy_buffer(real_screen);
- SDL_Flip(real_screen);
+ SDL_UpdateTexture(texture, NULL, buf, surface->pitch);
+ SDL_RenderClear(renderer);
+ SDL_RenderCopy(renderer, texture, NULL, NULL);
+ SDL_RenderPresent(renderer);
}
- return 0;
-}
+ ret = 0;
-void sdl_start_timer(void)
-{
- pthread_attr_t attr;
- pthread_attr_init(&attr);
- pthread_create(&th, &attr, threadStart, NULL);
-}
+ SDL_DestroyTexture(texture);
+free_surface:
+ SDL_FreeSurface(surface);
+destroy_renderer:
+ SDL_DestroyRenderer(renderer);
-void sdl_stop_timer(void)
-{
- pthread_cancel(th);
+ return ret;
}
-void sdl_get_bitfield_rgba(struct fb_bitfield *r, struct fb_bitfield *g,
- struct fb_bitfield *b, struct fb_bitfield *a)
+static SDL_Thread *thread;
+
+void sdl_video_close(void)
{
- SDL_Surface *screen = real_screen;
-
- r->length = 8 - screen->format->Rloss;
- r->offset = screen->format->Rshift;
- g->length = 8 - screen->format->Gloss;
- g->offset = screen->format->Gshift;
- b->length = 8 - screen->format->Bloss;
- b->offset = screen->format->Bshift;
- a->length = 8 - screen->format->Aloss;
- a->offset = screen->format->Ashift;
+ SDL_AtomicSet(&shutdown, true); /* implies full memory barrier */
+ SDL_WaitThread(thread, NULL);
+ SDL_AtomicSet(&shutdown, false);
+ SDL_DestroyWindow(window);
+ SDL_QuitSubSystem(SDL_INIT_VIDEO);
}
-int sdl_open(int xres, int yres, int bpp, void* buf)
+int sdl_video_open(const struct sdl_fb_info *_info)
{
- int flags = SDL_HWSURFACE | SDL_ASYNCBLIT | SDL_HWACCEL;
+ info = *_info;
- if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE) < 0) {
- printf("Could not initialize SDL: %s.\n", SDL_GetError());
+ if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0) {
+ sdl_perror("initialize SDL Video");
return -1;
}
- real_screen = SDL_SetVideoMode(xres, yres, bpp, flags);
- if (!real_screen) {
- sdl_close();
- fprintf(stderr, "Couldn't create renderer: %s\n", SDL_GetError());
+ window = SDL_CreateWindow("barebox", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
+ info.xres, info.yres, 0);
+ if (!window) {
+ sdl_perror("create window");
+ goto quit_subsystem;
+ }
+
+ /* All scanout needs to happen in the same thread, because not all
+ * graphic backends are thread-safe. The window is created in the main
+ * thread though to work around libEGL crashing with SDL_VIDEODRIVER=wayland
+ */
+
+ thread = SDL_CreateThread(scanout, "video-scanout", NULL);
+ if (!thread) {
+ sdl_perror("start scanout thread");
+ goto destroy_window;
+ }
+
+ return 0;
+
+destroy_window:
+ SDL_DestroyWindow(window);
+quit_subsystem:
+ SDL_QuitSubSystem(SDL_INIT_VIDEO);
+
+ return -1;
+}
+
+static SDL_AudioDeviceID dev;
+
+int sdl_sound_init(unsigned sample_rate)
+{
+ SDL_AudioSpec audiospec = {
+ .freq = sample_rate,
+ .format = AUDIO_S16,
+ .channels = 1,
+ .samples = 2048,
+ };
+
+ if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {
+ sdl_perror("initialize SDL Audio");
return -1;
}
- buffer = buf;
+ dev = SDL_OpenAudioDevice(NULL, 0, &audiospec, NULL, 0);
+ if (!dev) {
+ sdl_perror("initialize open audio device");
+ SDL_QuitSubSystem(SDL_INIT_AUDIO);
+ return -1;
+ }
+ SDL_PauseAudioDevice(dev, 0);
return 0;
}
-void sdl_close(void)
+void sdl_sound_close(void)
+{
+ SDL_QuitSubSystem(SDL_INIT_AUDIO);
+}
+
+int sdl_sound_play(const void *data, unsigned nsamples)
+{
+ /* core sound support handles all the queueing for us */
+ SDL_ClearQueuedAudio(dev);
+ return SDL_QueueAudio(dev, data, nsamples * sizeof(uint16_t));
+}
+
+void sdl_sound_stop(void)
{
- sdl_stop_timer();
- SDL_Quit();
+ SDL_ClearQueuedAudio(dev);
}
diff --git a/commands/Kconfig b/commands/Kconfig
index 1667e50cc6..520ad4b1de 100644
--- a/commands/Kconfig
+++ b/commands/Kconfig
@@ -1474,6 +1474,13 @@ config CMD_FBTEST
Framebuffer test command that allows to produce a number of
test patterns on a screen.
+config CMD_BEEP
+ def_bool y
+ depends on SOUND
+ prompt "Beep"
+ help
+ Play beeps. Accepts same format as GRUB play
+
config CMD_READLINE
tristate
prompt "readline"
diff --git a/commands/Makefile b/commands/Makefile
index dc285cd00e..034c0e6383 100644
--- a/commands/Makefile
+++ b/commands/Makefile
@@ -58,6 +58,7 @@ obj-$(CONFIG_CMD_LSMOD) += lsmod.o
obj-$(CONFIG_CMD_INSMOD) += insmod.o
obj-$(CONFIG_CMD_SPLASH) += splash.o
obj-$(CONFIG_CMD_FBTEST) += fbtest.o
+obj-$(CONFIG_CMD_BEEP) += beep.o
obj-$(CONFIG_USB_GADGET_DFU) += dfu.o
obj-$(CONFIG_USB_GADGET_SERIAL) += usbserial.o
obj-$(CONFIG_CMD_GPIO) += gpio.o
diff --git a/commands/beep.c b/commands/beep.c
new file mode 100644
index 0000000000..29569fb51a
--- /dev/null
+++ b/commands/beep.c
@@ -0,0 +1,99 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// SPDX-FileCopyrightText: © 2021 Ahmad Fatoum
+
+#include <common.h>
+#include <command.h>
+#include <sound.h>
+#include <getopt.h>
+
+static int do_beep(int argc, char *argv[])
+{
+ int ret, i, opt;
+ u32 tempo, total_us = 0;
+ bool wait = false;
+
+ while((opt = getopt(argc, argv, "wc")) > 0) {
+ switch(opt) {
+ case 'w':
+ wait = true;
+ break;
+ case 'c':
+ return beep_cancel();
+ default:
+ return COMMAND_ERROR_USAGE;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc == 0 || argc % 2 != 1)
+ return COMMAND_ERROR_USAGE;
+
+ ret = kstrtou32(argv[0], 0, &tempo);
+ if (ret || tempo == 0)
+ return COMMAND_ERROR_USAGE;
+
+ tempo = 60 * USEC_PER_SEC / tempo;
+
+ if (argc == 1) {
+ ret = beep(BELL_DEFAULT_FREQUENCY, tempo);
+ if (ret)
+ return ret;
+
+ total_us += tempo;
+ goto out;
+ }
+
+ for (i = 1; i < argc; i += 2) {
+ u32 pitch = 0, duration;
+ u16 val;
+
+ ret = kstrtou16(argv[i], 0, &val);
+ if (ret)
+ return COMMAND_ERROR_USAGE;
+
+ if (val)
+ pitch = clamp_t(unsigned, val, 20, 20000);
+
+ ret = kstrtou16(argv[i+1], 0, &val);
+ if (ret)
+ return COMMAND_ERROR_USAGE;
+
+ duration = val * tempo;
+
+ ret = beep(pitch, duration);
+ if (ret)
+ return ret;
+
+ total_us += duration;
+ }
+
+out:
+ if (wait)
+ beep_wait(total_us);
+
+ return 0;
+}
+
+/* https://www.gnu.org/software/grub/manual/grub/html_node/play.html */
+BAREBOX_CMD_HELP_START(beep)
+BAREBOX_CMD_HELP_TEXT("Tempo is an unsigned 32bit number. It's followed by pairs of unsigned")
+BAREBOX_CMD_HELP_TEXT("16bit numbers for pitch and duration.")
+BAREBOX_CMD_HELP_TEXT("The tempo is the base for all note durations. 60 gives a 1-second base,")
+BAREBOX_CMD_HELP_TEXT("120 gives a half-second base, etc. Pitches are Hz.")
+BAREBOX_CMD_HELP_TEXT("Set pitch to 0 to produce a rest.")
+BAREBOX_CMD_HELP_TEXT("When only tempo is given, a beep of duration 1 at bell frequency results.")
+BAREBOX_CMD_HELP_TEXT("")
+BAREBOX_CMD_HELP_TEXT("Options:")
+BAREBOX_CMD_HELP_OPT ("-c", "cancel pending beeps")
+BAREBOX_CMD_HELP_OPT ("-w", "wait until beep is over")
+BAREBOX_CMD_HELP_END
+
+BAREBOX_CMD_START(beep)
+ .cmd = do_beep,
+ BAREBOX_CMD_DESC("play a GRUB beep tune")
+ BAREBOX_CMD_OPTS("tempo [pitch1 duration1 [pitch2 duration2] ...]")
+ BAREBOX_CMD_HELP(cmd_beep_help)
+ BAREBOX_CMD_GROUP(CMD_GRP_CONSOLE)
+BAREBOX_CMD_END
diff --git a/drivers/Kconfig b/drivers/Kconfig
index dda2405780..0b87c2af2a 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -13,6 +13,7 @@ source "drivers/mtd/Kconfig"
source "drivers/ata/Kconfig"
source "drivers/usb/Kconfig"
source "drivers/video/Kconfig"
+source "drivers/sound/Kconfig"
source "drivers/mci/Kconfig"
source "drivers/clk/Kconfig"
source "drivers/clocksource/Kconfig"
diff --git a/drivers/Makefile b/drivers/Makefile
index 5a03bdceab..fab3790288 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -43,3 +43,4 @@ obj-y += soc/imx/
obj-y += nvme/
obj-y += ddr/
obj-y += power/
+obj-$(CONFIG_SOUND) += sound/
diff --git a/drivers/sound/Kconfig b/drivers/sound/Kconfig
new file mode 100644
index 0000000000..bf6f715200
--- /dev/null
+++ b/drivers/sound/Kconfig
@@ -0,0 +1,38 @@
+# SPDX-License-Identifier: GPL-2.0-only
+menuconfig SOUND
+ bool "Sound drivers"
+ select POLLER
+ help
+ Say Y here for sound support. At the moment that's just beep tones.
+ Tones are played asynchronously in a poller. Check the beep command
+ for how to exercise the API.
+
+if SOUND
+
+config SOUND_SDL
+ bool "SDL sound driver for sandbox"
+ depends on SANDBOX && OFDEVICE
+ select SDL
+
+config PWM_BEEPER
+ bool "PWM beeper support"
+ depends on PWM && OFDEVICE
+ help
+ Say Y here to get support for PWM based beeper devices.
+
+config GPIO_BEEPER
+ bool "GPIO beeper support"
+ depends on GPIOLIB && OFDEVICE
+ help
+ Say Y here to get support for GPIO based beeper devices.
+
+config SYNTH_SQUARES
+ bool "Synthesize square waves only"
+ help
+ For beeping on PCM sound cards, barebox needs to synthesize samples,
+ which can take too much poller time for crisp playback and/or quick
+ booting. If your playback stutters, say Y here. This will have all
+ synthesizers output a gain-adjusted square wave instead, which is
+ less time-consuming to compute.
+
+endif
diff --git a/drivers/sound/Makefile b/drivers/sound/Makefile
new file mode 100644
index 0000000000..57d9cbd332
--- /dev/null
+++ b/drivers/sound/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-y += core.o synth.o
+obj-$(CONFIG_SOUND_SDL) += sdl.o
+obj-$(CONFIG_PWM_BEEPER) += pwm-beeper.o
+obj-$(CONFIG_GPIO_BEEPER) += gpio-beeper.o
diff --git a/drivers/sound/core.c b/drivers/sound/core.c
new file mode 100644
index 0000000000..801b1fade5
--- /dev/null
+++ b/drivers/sound/core.c
@@ -0,0 +1,121 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// SPDX-FileCopyrightText: © 2021 Ahmad Fatoum
+
+#include <common.h>
+#include <linux/list.h>
+#include <sound.h>
+#include <poller.h>
+#include <linux/iopoll.h>
+
+static LIST_HEAD(card_list);
+
+struct beep {
+ int freq;
+ unsigned int us;
+ struct list_head list;
+};
+
+static int sound_card_do_beep(struct sound_card *card,
+ int freq, unsigned int us)
+{
+ if (freq == -1)
+ freq = card->bell_frequency;
+
+ return card->beep(card, freq, us);
+}
+
+static void sound_card_poller_cb(void *_card)
+{
+ struct sound_card *card = _card;
+ struct beep *beep;
+
+ beep = list_first_entry_or_null(&card->tune, struct beep, list);
+ if (!beep) {
+ sound_card_do_beep(card, 0, 0);
+ return;
+ }
+
+ list_del(&beep->list);
+
+ poller_call_async(&card->poller, beep->us * 1000ULL,
+ sound_card_poller_cb, card);
+ sound_card_do_beep(card, beep->freq, beep->us);
+
+ free(beep);
+}
+
+int sound_card_register(struct sound_card *card)
+{
+ if (!card->name)
+ return -EINVAL;
+
+ if (card->bell_frequency <= 0)
+ card->bell_frequency = 1000;
+
+ poller_async_register(&card->poller, card->name);
+ INIT_LIST_HEAD(&card->tune);
+
+ list_add_tail(&card->list, &card_list);
+ return 0;
+}
+
+struct sound_card *sound_card_get_default(void)
+{
+ return list_first_entry_or_null(&card_list, struct sound_card, list);
+}
+
+int sound_card_beep(struct sound_card *card, int freq, unsigned int us)
+{
+ struct beep *beep;
+ int ret;
+
+ if (!card)
+ return -ENODEV;
+
+ if (!poller_async_active(&card->poller)) {
+ ret = sound_card_do_beep(card, freq, us);
+ if (!ret)
+ poller_call_async(&card->poller, us * 1000ULL,
+ sound_card_poller_cb, card);
+
+ return ret;
+ }
+
+ beep = malloc(sizeof(*beep));
+ if (!beep)
+ return -ENOMEM;
+
+ beep->freq = freq;
+ beep->us = us;
+
+ list_add_tail(&beep->list, &card->tune);
+
+ return 0;
+}
+
+int sound_card_beep_wait(struct sound_card *card, unsigned timeout)
+{
+ bool active;
+ return read_poll_timeout(poller_async_active, active,
+ !active, timeout, &card->poller);
+}
+
+int sound_card_beep_cancel(struct sound_card *card)
+{
+ struct beep *beep, *tmp;
+ int ret;
+
+ if (!card)
+ return -ENODEV;
+
+ poller_async_cancel(&card->poller);
+
+ ret = card->beep(card, 0, 0);
+
+ list_for_each_entry_safe(beep, tmp, &card->tune, list) {
+ list_del(&beep->list);
+ free(beep);
+ }
+
+ return ret;
+}
diff --git a/drivers/sound/gpio-beeper.c b/drivers/sound/gpio-beeper.c
new file mode 100644
index 0000000000..86fd4a4ee6
--- /dev/null
+++ b/drivers/sound/gpio-beeper.c
@@ -0,0 +1,77 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2021, Ahmad Fatoum
+ */
+
+#include <common.h>
+#include <regulator.h>
+#include <sound.h>
+#include <of.h>
+#include <gpio.h>
+#include <of_gpio.h>
+
+struct gpio_beeper {
+ int gpio;
+ struct sound_card card;
+};
+
+static int gpio_beeper_beep(struct sound_card *card, unsigned freq, unsigned duration)
+{
+ struct gpio_beeper *beeper = container_of(card, struct gpio_beeper, card);
+
+ gpio_set_active(beeper->gpio, freq);
+ return 0;
+}
+
+static int gpio_beeper_probe(struct device_d *dev)
+{
+ struct device_node *np = dev->device_node;
+ struct gpio_beeper *beeper;
+ struct sound_card *card;
+ enum of_gpio_flags of_flags;
+ unsigned long gpio_flags = GPIOF_OUT_INIT_ACTIVE;
+ int ret, gpio;
+
+ gpio = of_get_named_gpio_flags(np, "gpios", 0, &of_flags);
+ if (!gpio_is_valid(gpio))
+ return gpio;
+
+ if (of_flags & OF_GPIO_ACTIVE_LOW)
+ gpio_flags |= GPIOF_ACTIVE_LOW;
+
+ ret = gpio_request_one(gpio, gpio_flags, "gpio-beeper");
+ if (ret) {
+ dev_err(dev, "failed to request gpio %d: %d\n", gpio, ret);
+ return ret;
+ }
+
+ beeper = xzalloc(sizeof(*beeper));
+ beeper->gpio = gpio;
+ dev->priv = beeper;
+
+ card = &beeper->card;
+ card->name = np->full_name;
+ card->beep = gpio_beeper_beep;
+
+ return sound_card_register(card);
+}
+
+static void gpio_beeper_suspend(struct device_d *dev)
+{
+ struct gpio_beeper *beeper = dev->priv;
+
+ gpio_beeper_beep(&beeper->card, 0, 0);
+}
+
+static const struct of_device_id gpio_beeper_match[] = {
+ { .compatible = "gpio-beeper", },
+ { },
+};
+
+static struct driver_d gpio_beeper_driver = {
+ .name = "gpio-beeper",
+ .probe = gpio_beeper_probe,
+ .remove = gpio_beeper_suspend,
+ .of_compatible = gpio_beeper_match,
+};
+device_platform_driver(gpio_beeper_driver);
diff --git a/drivers/sound/pwm-beeper.c b/drivers/sound/pwm-beeper.c
new file mode 100644
index 0000000000..ef053f97cf
--- /dev/null
+++ b/drivers/sound/pwm-beeper.c
@@ -0,0 +1,124 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de>
+ * Copyright (C) 2021, Ahmad Fatoum
+ */
+
+#include <common.h>
+#include <regulator.h>
+#include <sound.h>
+#include <of.h>
+#include <pwm.h>
+
+struct pwm_beeper {
+ struct pwm_device *pwm;
+ struct regulator *amplifier;
+ struct sound_card card;
+};
+
+#define HZ_TO_NANOSECONDS(x) (1000000000UL/(x))
+
+static int pwm_beeper_beep(struct sound_card *card, unsigned freq, unsigned duration)
+{
+ struct pwm_beeper *beeper = container_of(card, struct pwm_beeper, card);
+ struct pwm_state state;
+ int error = 0;
+
+ if (!freq) {
+ regulator_disable(beeper->amplifier);
+ goto pwm_disable;
+ }
+
+ pwm_get_state(beeper->pwm, &state);
+
+ state.p_enable = true;
+ state.period_ns = HZ_TO_NANOSECONDS(freq);
+ pwm_set_relative_duty_cycle(&state, 50, 100);
+
+ error = pwm_apply_state(beeper->pwm, &state);
+ if (error)
+ return error;
+
+ error = regulator_enable(beeper->amplifier);
+ if (error)
+ goto pwm_disable;
+
+ return 0;
+pwm_disable:
+ pwm_disable(beeper->pwm);
+ return error;
+}
+
+static int pwm_beeper_probe(struct device_d *dev)
+{
+ struct pwm_beeper *beeper;
+ struct sound_card *card;
+ struct pwm_state state;
+ u32 bell_frequency;
+ int error;
+
+ beeper = xzalloc(sizeof(*beeper));
+ dev->priv = beeper;
+
+ beeper->pwm = of_pwm_request(dev->device_node, NULL);
+ if (IS_ERR(beeper->pwm)) {
+ error = PTR_ERR(beeper->pwm);
+ if (error != -EPROBE_DEFER)
+ dev_err(dev, "Failed to request PWM device: %d\n",
+ error);
+ return error;
+ }
+
+ /* Sync up PWM state and ensure it is off. */
+ pwm_init_state(beeper->pwm, &state);
+ state.p_enable = false;
+ error = pwm_apply_state(beeper->pwm, &state);
+ if (error) {
+ dev_err(dev, "failed to apply initial PWM state: %d\n",
+ error);
+ return error;
+ }
+
+ beeper->amplifier = regulator_get(dev, "amp");
+ if (IS_ERR(beeper->amplifier)) {
+ error = PTR_ERR(beeper->amplifier);
+ if (error != -EPROBE_DEFER)
+ dev_err(dev, "Failed to get 'amp' regulator: %d\n",
+ error);
+ return error;
+ }
+
+ error = of_property_read_u32(dev->device_node, "beeper-hz", &bell_frequency);
+ if (error) {
+ bell_frequency = 1000;
+ dev_dbg(dev, "failed to parse 'beeper-hz' property, using default: %uHz\n",
+ bell_frequency);
+ }
+
+ card = &beeper->card;
+ card->name = dev->device_node->full_name;
+ card->bell_frequency = bell_frequency;
+ card->beep = pwm_beeper_beep;
+
+ return sound_card_register(card);
+}
+
+static void pwm_beeper_suspend(struct device_d *dev)
+{
+ struct pwm_beeper *beeper = dev->priv;
+
+ pwm_beeper_beep(&beeper->card, 0, 0);
+}
+
+static const struct of_device_id pwm_beeper_match[] = {
+ { .compatible = "pwm-beeper", },
+ { },
+};
+
+static struct driver_d pwm_beeper_driver = {
+ .name = "pwm-beeper",
+ .probe = pwm_beeper_probe,
+ .remove = pwm_beeper_suspend,
+ .of_compatible = pwm_beeper_match,
+};
+device_platform_driver(pwm_beeper_driver);
diff --git a/drivers/sound/sdl.c b/drivers/sound/sdl.c
new file mode 100644
index 0000000000..118d774295
--- /dev/null
+++ b/drivers/sound/sdl.c
@@ -0,0 +1,87 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <common.h>
+#include <errno.h>
+#include <driver.h>
+#include <mach/linux.h>
+#include <linux/time.h>
+#include <linux/math64.h>
+#include <of.h>
+#include <sound.h>
+
+#define AMPLITUDE 28000
+#define SAMPLERATE 44000ULL
+
+struct sandbox_sound {
+ struct sound_card card;
+};
+
+static int sandbox_sound_beep(struct sound_card *card, unsigned freq, unsigned duration)
+{
+ size_t nsamples = div_s64(SAMPLERATE * duration, USEC_PER_SEC);
+ int16_t *data;
+ int ret;
+
+ if (!freq) {
+ sdl_sound_stop();
+ return 0;
+ }
+
+ data = malloc(nsamples * sizeof(*data));
+ if (!data)
+ return -ENOMEM;
+
+ synth_sin(freq, AMPLITUDE, data, SAMPLERATE, nsamples);
+ ret = sdl_sound_play(data, nsamples);
+ if (ret)
+ ret = -EIO;
+ free(data);
+
+ return ret;
+}
+
+static int sandbox_sound_probe(struct device_d *dev)
+{
+ struct sandbox_sound *priv;
+ struct sound_card *card;
+ int ret;
+
+ priv = xzalloc(sizeof(*priv));
+
+ card = &priv->card;
+ card->name = "SDL-Audio";
+ card->beep = sandbox_sound_beep;
+
+ ret = sdl_sound_init(SAMPLERATE);
+ if (ret) {
+ ret = -ENODEV;
+ goto free_priv;
+ }
+
+ ret = sound_card_register(card);
+ if (ret)
+ goto sdl_sound_close;
+
+ dev_info(dev, "probed\n");
+ return 0;
+
+sdl_sound_close:
+ sdl_sound_close();
+free_priv:
+ free(priv);
+
+ return ret;
+}
+
+
+static __maybe_unused struct of_device_id sandbox_sound_dt_ids[] = {
+ { .compatible = "barebox,sandbox-sound" },
+ { /* sentinel */ }
+};
+
+static struct driver_d sandbox_sound_drv = {
+ .name = "sandbox-sound",
+ .of_compatible = sandbox_sound_dt_ids,
+ .probe = sandbox_sound_probe,
+};
+device_platform_driver(sandbox_sound_drv);
diff --git a/drivers/sound/synth.c b/drivers/sound/synth.c
new file mode 100644
index 0000000000..c9de62b516
--- /dev/null
+++ b/drivers/sound/synth.c
@@ -0,0 +1,43 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2012 Samsung Electronics, R. Chandrasekar <rcsekar@samsung.com>
+ * Copyright (C) 2021 Ahmad Fatoum
+ */
+
+#include <common.h>
+#include <linux/fixp-arith.h>
+#include <linux/math64.h>
+#include <sound.h>
+
+void __synth_sin(unsigned freq, s16 amplitude, s16 *stream,
+ unsigned sample_rate, unsigned nsamples)
+{
+ int64_t v = 0;
+ int i = 0;
+
+ for (i = 0; i < nsamples; i++) {
+ /* Assume RHS sign extension, true for GCC */
+ stream[i] = (fixp_sin32(div_s64(v * 360, sample_rate)) * (int64_t)amplitude) >> 31;
+ v += freq;
+ }
+}
+
+void __synth_square(unsigned freq, s16 amplitude, s16 *stream,
+ unsigned sample_rate, unsigned nsamples)
+{
+ unsigned period = freq ? sample_rate / freq : 0;
+ int half = period / 2;
+
+ while (nsamples) {
+ int i;
+
+ for (i = 0; nsamples && i < half; i++) {
+ nsamples--;
+ *stream++ = amplitude;
+ }
+ for (i = 0; nsamples && i < period - half; i++) {
+ nsamples--;
+ *stream++ = -amplitude;
+ }
+ }
+}
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 9ec6ea4248..b6d468c63c 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -82,6 +82,7 @@ endif
config DRIVER_VIDEO_SDL
bool "SDL framebuffer driver"
depends on SANDBOX
+ select SDL
config DRIVER_VIDEO_PXA
bool "PXA27x framebuffer driver"
diff --git a/drivers/video/sdl.c b/drivers/video/sdl.c
index 9811b2cf12..e9debc51b1 100644
--- a/drivers/video/sdl.c
+++ b/drivers/video/sdl.c
@@ -13,23 +13,25 @@
#include <errno.h>
#include <gui/graphic_utils.h>
+#define to_mask(color) GENMASK(color.length - 1, color.offset)
+
static void sdlfb_enable(struct fb_info *info)
{
- int ret;
-
- ret = sdl_open(info->xres, info->yres, info->bits_per_pixel,
- info->screen_base);
- if (ret)
- return;
- sdl_get_bitfield_rgba(&info->red, &info->green, &info->blue, &info->transp);
-
- sdl_start_timer();
+ struct sdl_fb_info sdl_info = {
+ .screen_base = info->screen_base,
+ .xres = info->xres, .yres = info->yres, .bpp = info->bits_per_pixel,
+ .rmask = to_mask(info->red),
+ .gmask = to_mask(info->green),
+ .bmask = to_mask(info->blue),
+ .amask = to_mask(info->transp),
+ };
+
+ sdl_video_open(&sdl_info);
}
static void sdlfb_disable(struct fb_info *info)
{
- sdl_stop_timer();
- sdl_close();
+ sdl_video_close();
}
static struct fb_ops sdlfb_ops = {
@@ -48,10 +50,19 @@ static int sdlfb_probe(struct device_d *dev)
fb = xzalloc(sizeof(*fb));
fb->modes.modes = fb->mode = dev->platform_data;
fb->modes.num_modes = 1;
- fb->bits_per_pixel = 4 << 3;
fb->xres = fb->mode->xres;
fb->yres = fb->mode->yres;
+ fb->bits_per_pixel = 32;
+ fb->transp.length = 8;
+ fb->red.length = 8;
+ fb->green.length = 8;
+ fb->blue.length = 8;
+ fb->transp.offset = 24;
+ fb->red.offset = 16;
+ fb->green.offset = 8;
+ fb->blue.offset = 0;
+
fb->priv = fb;
fb->fbops = &sdlfb_ops;
@@ -68,7 +79,6 @@ static int sdlfb_probe(struct device_d *dev)
kfree(fb->screen_base);
kfree(fb);
- sdl_close();
return ret;
}
@@ -78,7 +88,6 @@ static void sdlfb_remove(struct device_d *dev)
kfree(fb->screen_base);
kfree(fb);
- sdl_close();
}
static struct driver_d sdlfb_driver = {
diff --git a/include/linux/fixp-arith.h b/include/linux/fixp-arith.h
new file mode 100644
index 0000000000..8396013785
--- /dev/null
+++ b/include/linux/fixp-arith.h
@@ -0,0 +1,144 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#ifndef _FIXP_ARITH_H
+#define _FIXP_ARITH_H
+
+#include <linux/math64.h>
+
+/*
+ * Simplistic fixed-point arithmetics.
+ * Hmm, I'm probably duplicating some code :(
+ *
+ * Copyright (c) 2002 Johann Deneux
+ */
+
+/*
+ *
+ * Should you need to contact me, the author, you can do so by
+ * e-mail - mail your message to <johann.deneux@gmail.com>
+ */
+
+#include <linux/types.h>
+
+static const s32 sin_table[] = {
+ 0x00000000, 0x023be165, 0x04779632, 0x06b2f1d2, 0x08edc7b6, 0x0b27eb5c,
+ 0x0d61304d, 0x0f996a26, 0x11d06c96, 0x14060b67, 0x163a1a7d, 0x186c6ddd,
+ 0x1a9cd9ac, 0x1ccb3236, 0x1ef74bf2, 0x2120fb82, 0x234815ba, 0x256c6f9e,
+ 0x278dde6e, 0x29ac379f, 0x2bc750e8, 0x2ddf003f, 0x2ff31bdd, 0x32037a44,
+ 0x340ff241, 0x36185aee, 0x381c8bb5, 0x3a1c5c56, 0x3c17a4e7, 0x3e0e3ddb,
+ 0x3fffffff, 0x41ecc483, 0x43d464fa, 0x45b6bb5d, 0x4793a20f, 0x496af3e1,
+ 0x4b3c8c11, 0x4d084650, 0x4ecdfec6, 0x508d9210, 0x5246dd48, 0x53f9be04,
+ 0x55a6125a, 0x574bb8e5, 0x58ea90c2, 0x5a827999, 0x5c135399, 0x5d9cff82,
+ 0x5f1f5ea0, 0x609a52d1, 0x620dbe8a, 0x637984d3, 0x64dd894f, 0x6639b039,
+ 0x678dde6d, 0x68d9f963, 0x6a1de735, 0x6b598ea1, 0x6c8cd70a, 0x6db7a879,
+ 0x6ed9eba0, 0x6ff389de, 0x71046d3c, 0x720c8074, 0x730baeec, 0x7401e4bf,
+ 0x74ef0ebb, 0x75d31a5f, 0x76adf5e5, 0x777f903b, 0x7847d908, 0x7906c0af,
+ 0x79bc384c, 0x7a6831b8, 0x7b0a9f8c, 0x7ba3751c, 0x7c32a67c, 0x7cb82884,
+ 0x7d33f0c8, 0x7da5f5a3, 0x7e0e2e31, 0x7e6c924f, 0x7ec11aa3, 0x7f0bc095,
+ 0x7f4c7e52, 0x7f834ecf, 0x7fb02dc4, 0x7fd317b3, 0x7fec09e1, 0x7ffb025e,
+ 0x7fffffff
+};
+
+/**
+ * __fixp_sin32() returns the sin of an angle in degrees
+ *
+ * @degrees: angle, in degrees, from 0 to 360.
+ *
+ * The returned value ranges from -0x7fffffff to +0x7fffffff.
+ */
+static inline s32 __fixp_sin32(int degrees)
+{
+ s32 ret;
+ bool negative = false;
+
+ if (degrees > 180) {
+ negative = true;
+ degrees -= 180;
+ }
+ if (degrees > 90)
+ degrees = 180 - degrees;
+
+ ret = sin_table[degrees];
+
+ return negative ? -ret : ret;
+}
+
+/**
+ * fixp_sin32() returns the sin of an angle in degrees
+ *
+ * @degrees: angle, in degrees. The angle can be positive or negative
+ *
+ * The returned value ranges from -0x7fffffff to +0x7fffffff.
+ */
+static inline s32 fixp_sin32(int degrees)
+{
+ degrees = (degrees % 360 + 360) % 360;
+
+ return __fixp_sin32(degrees);
+}
+
+/* cos(x) = sin(x + 90 degrees) */
+#define fixp_cos32(v) fixp_sin32((v) + 90)
+
+/*
+ * 16 bits variants
+ *
+ * The returned value ranges from -0x7fff to 0x7fff
+ */
+
+#define fixp_sin16(v) (fixp_sin32(v) >> 16)
+#define fixp_cos16(v) (fixp_cos32(v) >> 16)
+
+/**
+ * fixp_sin32_rad() - calculates the sin of an angle in radians
+ *
+ * @radians: angle, in radians
+ * @twopi: value to be used for 2*pi
+ *
+ * Provides a variant for the cases where just 360
+ * values is not enough. This function uses linear
+ * interpolation to a wider range of values given by
+ * twopi var.
+ *
+ * Experimental tests gave a maximum difference of
+ * 0.000038 between the value calculated by sin() and
+ * the one produced by this function, when twopi is
+ * equal to 360000. That seems to be enough precision
+ * for practical purposes.
+ *
+ * Please notice that two high numbers for twopi could cause
+ * overflows, so the routine will not allow values of twopi
+ * bigger than 1^18.
+ */
+static inline s32 fixp_sin32_rad(u32 radians, u32 twopi)
+{
+ int degrees;
+ s32 v1, v2, dx, dy;
+ s64 tmp;
+
+ /*
+ * Avoid too large values for twopi, as we don't want overflows.
+ */
+ BUG_ON(twopi > 1 << 18);
+
+ degrees = (radians * 360) / twopi;
+ tmp = radians - (degrees * twopi) / 360;
+
+ degrees = (degrees % 360 + 360) % 360;
+ v1 = __fixp_sin32(degrees);
+
+ v2 = fixp_sin32(degrees + 1);
+
+ dx = twopi / 360;
+ dy = v2 - v1;
+
+ tmp *= dy;
+
+ return v1 + div_s64(tmp, dx);
+}
+
+/* cos(x) = sin(x + pi/2 radians) */
+
+#define fixp_cos32_rad(rad, twopi) \
+ fixp_sin32_rad(rad + twopi / 4, twopi)
+
+#endif
diff --git a/include/poller.h b/include/poller.h
index 886557252b..db773265b2 100644
--- a/include/poller.h
+++ b/include/poller.h
@@ -34,6 +34,10 @@ int poller_async_unregister(struct poller_async *pa);
int poller_call_async(struct poller_async *pa, uint64_t delay_ns,
void (*fn)(void *), void *ctx);
int poller_async_cancel(struct poller_async *pa);
+static inline bool poller_async_active(struct poller_async *pa)
+{
+ return pa->active;
+}
#ifdef CONFIG_POLLER
void poller_call(void);
diff --git a/include/pwm.h b/include/pwm.h
index b67ab13d2e..2bd59fb8d3 100644
--- a/include/pwm.h
+++ b/include/pwm.h
@@ -3,6 +3,7 @@
#define __PWM_H
#include <dt-bindings/pwm/pwm.h>
+#include <errno.h>
struct pwm_device;
struct device_d;
@@ -63,6 +64,38 @@ void pwm_disable(struct pwm_device *pwm);
unsigned int pwm_get_period(struct pwm_device *pwm);
+/**
+ * pwm_set_relative_duty_cycle() - Set a relative duty cycle value
+ * @state: PWM state to fill
+ * @duty_cycle: relative duty cycle value
+ * @scale: scale in which @duty_cycle is expressed
+ *
+ * This functions converts a relative into an absolute duty cycle (expressed
+ * in nanoseconds), and puts the result in state->duty_cycle.
+ *
+ * For example if you want to configure a 50% duty cycle, call:
+ *
+ * pwm_init_state(pwm, &state);
+ * pwm_set_relative_duty_cycle(&state, 50, 100);
+ * pwm_apply_state(pwm, &state);
+ *
+ * This functions returns -EINVAL if @duty_cycle and/or @scale are
+ * inconsistent (@scale == 0 or @duty_cycle > @scale).
+ */
+static inline int
+pwm_set_relative_duty_cycle(struct pwm_state *state, unsigned int duty_cycle,
+ unsigned int scale)
+{
+ if (!scale || duty_cycle > scale)
+ return -EINVAL;
+
+ state->duty_ns = DIV_ROUND_CLOSEST_ULL((u64)duty_cycle *
+ state->period_ns,
+ scale);
+
+ return 0;
+}
+
struct pwm_chip;
/**
diff --git a/include/sound.h b/include/sound.h
new file mode 100644
index 0000000000..62fef106ee
--- /dev/null
+++ b/include/sound.h
@@ -0,0 +1,77 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* SPDX-FileCopyrightText: © 2021 Ahmad Fatoum */
+
+#ifndef __SOUND_H_
+#define __SOUND_H_
+
+#include <linux/list.h>
+#include <poller.h>
+
+#define BELL_DEFAULT_FREQUENCY -1
+
+struct sound_card {
+ const char *name;
+ int bell_frequency;
+ int (*beep)(struct sound_card *, unsigned freq, unsigned us);
+
+ /* private */
+ struct list_head list;
+ struct list_head tune;
+ struct poller_async poller;
+};
+
+int sound_card_register(struct sound_card *card);
+int sound_card_beep_wait(struct sound_card *card, unsigned timeout_us);
+int sound_card_beep(struct sound_card *card, int freq, unsigned int us);
+int sound_card_beep_cancel(struct sound_card *card);
+
+struct sound_card *sound_card_get_default(void);
+
+static inline int beep(int freq, unsigned us)
+{
+ return sound_card_beep(sound_card_get_default(), freq, us);
+}
+
+static inline int beep_wait(unsigned timeout_us)
+{
+ return sound_card_beep_wait(sound_card_get_default(), timeout_us);
+}
+
+static inline int beep_cancel(void)
+{
+ return sound_card_beep_cancel(sound_card_get_default());
+}
+
+
+/*
+ * Synthesizers all have the same prototype, but their implementation
+ * is replaced with a gain-adjusted square wave if CONFIG_SYNTH_SQUARES=y.
+ * This is to support PCM beeping on systems, where sine generation may
+ * spend to much time in the poller.
+ */
+typedef void synth_t(unsigned freq, s16 amplitude, s16 *stream,
+ unsigned sample_rate, unsigned nsamples);
+
+#ifdef CONFIG_SYNTH_SQUARES
+#define SYNTH(fn, volume_percent) \
+ static inline void fn(unsigned f, s16 amplitude, s16 *s, \
+ unsigned r, unsigned n) { \
+ synth_t __synth_square; \
+ amplitude = amplitude * volume_percent / 100; \
+ __synth_square(f, amplitude, s, r, n); \
+ } \
+ synth_t __##fn
+#else
+#define SYNTH(fn, volume_percent) \
+ static inline void fn(unsigned f, s16 a, s16 *s, \
+ unsigned r, unsigned n) { \
+ synth_t __##fn; \
+ __##fn(f, a, s, r, n); \
+ } \
+ synth_t __##fn
+#endif
+
+SYNTH(synth_square, 100); /* square wave always has full amplitude */
+SYNTH(synth_sin, 64); /* ∫₀¹ sin(πx) dx ≈ 64% */
+
+#endif