mail archive of the barebox mailing list
 help / color / mirror / Atom feed
* [PATCH 0/7] Add sound and GRUB beep tune support
@ 2021-01-31 20:18 Ahmad Fatoum
  2021-01-31 20:18 ` [PATCH 1/7] sandbox: migrate to SDL 2.0 Ahmad Fatoum
                   ` (7 more replies)
  0 siblings, 8 replies; 9+ messages in thread
From: Ahmad Fatoum @ 2021-01-31 20:18 UTC (permalink / raw)
  To: barebox

I've been meaning to upstream pwm-beeper support for a piezobuzzer,
but it would be a real shame if the obnoxious buzzing would delay the
boot. So let me present asynchronous beeping support for barebox.

If you don't have the suitable hardware lying around you can synth
your own tunes on the barebox sandbox shell as well.

The Imperial March:
  beep 480 440 4 440 4 440 4 349 3 523 1 440 4 349 3 523 1 440 8 659 4 659 4 659 4 698 3 523 1 415 4 349 3 523 1 440 8

Rebel's sabotage:
  beep -c

Cheers,
Ahmad Fatoum (7):
  sandbox: migrate to SDL 2.0
  drivers: add sound card driver support
  sound: add basic synthesizers for PCM beeper use
  sound: add SDL 2.0 sound driver
  commands: add beep command
  sound: add PWM beeper support
  sound: add gpio-beeper support

 arch/sandbox/Kconfig                          |   3 +
 arch/sandbox/Makefile                         |   6 +-
 arch/sandbox/configs/sandbox_defconfig        |   1 +
 arch/sandbox/dts/sandbox.dts                  |   4 +
 .../sandbox/mach-sandbox/include/mach/linux.h |  22 ++-
 arch/sandbox/os/Makefile                      |   4 +-
 arch/sandbox/os/sdl.c                         | 184 +++++++++++-------
 commands/Kconfig                              |   7 +
 commands/Makefile                             |   1 +
 commands/beep.c                               |  99 ++++++++++
 drivers/Kconfig                               |   1 +
 drivers/Makefile                              |   1 +
 drivers/sound/Kconfig                         |  38 ++++
 drivers/sound/Makefile                        |   5 +
 drivers/sound/core.c                          | 121 ++++++++++++
 drivers/sound/gpio-beeper.c                   |  77 ++++++++
 drivers/sound/pwm-beeper.c                    | 124 ++++++++++++
 drivers/sound/sdl.c                           |  87 +++++++++
 drivers/sound/synth.c                         |  43 ++++
 drivers/video/Kconfig                         |   1 +
 drivers/video/sdl.c                           |  37 ++--
 include/linux/fixp-arith.h                    | 144 ++++++++++++++
 include/poller.h                              |   4 +
 include/pwm.h                                 |  33 ++++
 include/sound.h                               |  77 ++++++++
 25 files changed, 1033 insertions(+), 91 deletions(-)
 create mode 100644 commands/beep.c
 create mode 100644 drivers/sound/Kconfig
 create mode 100644 drivers/sound/Makefile
 create mode 100644 drivers/sound/core.c
 create mode 100644 drivers/sound/gpio-beeper.c
 create mode 100644 drivers/sound/pwm-beeper.c
 create mode 100644 drivers/sound/sdl.c
 create mode 100644 drivers/sound/synth.c
 create mode 100644 include/linux/fixp-arith.h
 create mode 100644 include/sound.h

-- 
2.30.0


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH 1/7] sandbox: migrate to SDL 2.0
  2021-01-31 20:18 [PATCH 0/7] Add sound and GRUB beep tune support Ahmad Fatoum
@ 2021-01-31 20:18 ` Ahmad Fatoum
  2021-01-31 20:18 ` [PATCH 2/7] drivers: add sound card driver support Ahmad Fatoum
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Ahmad Fatoum @ 2021-01-31 20:18 UTC (permalink / raw)
  To: barebox

SDL 2.0 has been released more than 7 years ago and should be available
everywhere. Replace barebox's SDL 1.2 with SDL 2.0 instead:

 - better maintained

 - At least on Debian, you can't install x86_64 and i386 libsdl-dev at
   the same time, which makes a quick use of CONFIG_SANDBOX_LINUX_I386
   harder as the user needs to reinstall libraries. With SDL 2.0, it
   works

 - SDL 2.0 has easier audio API, which will be used in a later commit

 - Wayland support for sandbox video driver

Port to SDL 2.0 and as we touch everything anyway, rename the
sdl_ functions to start with sdl_video_ to differentiate from upcoming
sdl_sound_.

Signed-off-by: Ahmad Fatoum <ahmad@a3f.at>
---
 arch/sandbox/Kconfig                          |   3 +
 arch/sandbox/Makefile                         |   6 +-
 .../sandbox/mach-sandbox/include/mach/linux.h |  17 +-
 arch/sandbox/os/Makefile                      |   4 +-
 arch/sandbox/os/sdl.c                         | 146 ++++++++++--------
 drivers/video/Kconfig                         |   1 +
 drivers/video/sdl.c                           |  37 +++--
 7 files changed, 120 insertions(+), 94 deletions(-)

diff --git a/arch/sandbox/Kconfig b/arch/sandbox/Kconfig
index d9fc0c947b02..deff7f56a025 100644
--- a/arch/sandbox/Kconfig
+++ b/arch/sandbox/Kconfig
@@ -43,3 +43,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 17f9a298d773..ea594944e4eb 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/mach-sandbox/include/mach/linux.h b/arch/sandbox/mach-sandbox/include/mach/linux.h
index b26bfc24a291..c636a9af9cd2 100644
--- a/arch/sandbox/mach-sandbox/include/mach/linux.h
+++ b/arch/sandbox/mach-sandbox/include/mach/linux.h
@@ -38,13 +38,16 @@ 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);
 
 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 15d688bfdd1b..575b1a213050 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 9a35279eb79e..623e24cf0b26 100644
--- a/arch/sandbox/os/sdl.c
+++ b/arch/sandbox/os/sdl.c
@@ -1,103 +1,113 @@
+// 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());
-		return -1;
+	window = SDL_CreateWindow("barebox", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
+				  info.xres, info.yres, 0);
+	if (!window) {
+		sdl_perror("create window");
+		goto quit_subsystem;
 	}
 
-	buffer = buf;
+	/* 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;
-}
 
-void sdl_close(void)
-{
-	sdl_stop_timer();
-	SDL_Quit();
+destroy_window:
+	SDL_DestroyWindow(window);
+quit_subsystem:
+	SDL_QuitSubSystem(SDL_INIT_VIDEO);
+
+	return -1;
 }
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 9ec6ea4248c1..b6d468c63c03 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 9811b2cf128e..e9debc51b1e9 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 = {
-- 
2.30.0


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH 2/7] drivers: add sound card driver support
  2021-01-31 20:18 [PATCH 0/7] Add sound and GRUB beep tune support Ahmad Fatoum
  2021-01-31 20:18 ` [PATCH 1/7] sandbox: migrate to SDL 2.0 Ahmad Fatoum
@ 2021-01-31 20:18 ` Ahmad Fatoum
  2021-01-31 20:18 ` [PATCH 3/7] sound: add basic synthesizers for PCM beeper use Ahmad Fatoum
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Ahmad Fatoum @ 2021-01-31 20:18 UTC (permalink / raw)
  To: barebox

Add driver core boilerplate for sound support in barebox. Using the
provided API in <sound.h>, consumers can play beeps for a fixed duration
of time.

Playing beeps is not blocking and new beeps can be enqueued while one
is already playing. They will be played in succession by a poller, which
will also turn off the sound card when the beep tune is over.

API is also available for blocking until all beeps are played and for
cancelling an underway beep tune.

The API could be later extended for arbitrary PCM audio, should the
need arise.

Signed-off-by: Ahmad Fatoum <ahmad@a3f.at>
---
 drivers/Kconfig        |   1 +
 drivers/Makefile       |   1 +
 drivers/sound/Kconfig  |   8 +++
 drivers/sound/Makefile |   2 +
 drivers/sound/core.c   | 121 +++++++++++++++++++++++++++++++++++++++++
 include/poller.h       |   4 ++
 include/sound.h        |  45 +++++++++++++++
 7 files changed, 182 insertions(+)
 create mode 100644 drivers/sound/Kconfig
 create mode 100644 drivers/sound/Makefile
 create mode 100644 drivers/sound/core.c
 create mode 100644 include/sound.h

diff --git a/drivers/Kconfig b/drivers/Kconfig
index dda240578067..0b87c2af2a83 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 5a03bdceab81..fab3790288f7 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 000000000000..cce5c760afdc
--- /dev/null
+++ b/drivers/sound/Kconfig
@@ -0,0 +1,8 @@
+# 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.
diff --git a/drivers/sound/Makefile b/drivers/sound/Makefile
new file mode 100644
index 000000000000..e343e0fa72b0
--- /dev/null
+++ b/drivers/sound/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-y += core.o
diff --git a/drivers/sound/core.c b/drivers/sound/core.c
new file mode 100644
index 000000000000..801b1fade552
--- /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/include/poller.h b/include/poller.h
index 886557252b69..db773265b2f6 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/sound.h b/include/sound.h
new file mode 100644
index 000000000000..f5124aebbda1
--- /dev/null
+++ b/include/sound.h
@@ -0,0 +1,45 @@
+/* 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());
+}
+
+#endif
-- 
2.30.0


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH 3/7] sound: add basic synthesizers for PCM beeper use
  2021-01-31 20:18 [PATCH 0/7] Add sound and GRUB beep tune support Ahmad Fatoum
  2021-01-31 20:18 ` [PATCH 1/7] sandbox: migrate to SDL 2.0 Ahmad Fatoum
  2021-01-31 20:18 ` [PATCH 2/7] drivers: add sound card driver support Ahmad Fatoum
@ 2021-01-31 20:18 ` Ahmad Fatoum
  2021-01-31 20:18 ` [PATCH 4/7] sound: add SDL 2.0 sound driver Ahmad Fatoum
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Ahmad Fatoum @ 2021-01-31 20:18 UTC (permalink / raw)
  To: barebox

For beeping on PCM sound cards, barebox will need to synthesize samples.
Add basic sine and square wave synthesizers to achieve this.

Client code can either call __synth_F to explicitly select synth F or
synth_F, which depending on CONFIG_SYNTH_SQUARES may expand to either
__synth_F or a gain-adjusted __synth_generate_square.

The latter is mainly useful for slow systems that can't synthesize
enough sine samples in a poller without impacting boot performance.

Signed-off-by: Ahmad Fatoum <ahmad@a3f.at>
---
 drivers/sound/Kconfig      |  13 ++++
 drivers/sound/Makefile     |   2 +-
 drivers/sound/synth.c      |  43 +++++++++++
 include/linux/fixp-arith.h | 144 +++++++++++++++++++++++++++++++++++++
 include/sound.h            |  32 +++++++++
 5 files changed, 233 insertions(+), 1 deletion(-)
 create mode 100644 drivers/sound/synth.c
 create mode 100644 include/linux/fixp-arith.h

diff --git a/drivers/sound/Kconfig b/drivers/sound/Kconfig
index cce5c760afdc..d9f63a5f3c55 100644
--- a/drivers/sound/Kconfig
+++ b/drivers/sound/Kconfig
@@ -6,3 +6,16 @@ menuconfig SOUND
 	  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 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
index e343e0fa72b0..69873faab985 100644
--- a/drivers/sound/Makefile
+++ b/drivers/sound/Makefile
@@ -1,2 +1,2 @@
 # SPDX-License-Identifier: GPL-2.0-only
-obj-y += core.o
+obj-y += core.o synth.o
diff --git a/drivers/sound/synth.c b/drivers/sound/synth.c
new file mode 100644
index 000000000000..c9de62b51684
--- /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/include/linux/fixp-arith.h b/include/linux/fixp-arith.h
new file mode 100644
index 000000000000..8396013785ef
--- /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/sound.h b/include/sound.h
index f5124aebbda1..62fef106ee73 100644
--- a/include/sound.h
+++ b/include/sound.h
@@ -42,4 +42,36 @@ 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
-- 
2.30.0


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH 4/7] sound: add SDL 2.0 sound driver
  2021-01-31 20:18 [PATCH 0/7] Add sound and GRUB beep tune support Ahmad Fatoum
                   ` (2 preceding siblings ...)
  2021-01-31 20:18 ` [PATCH 3/7] sound: add basic synthesizers for PCM beeper use Ahmad Fatoum
@ 2021-01-31 20:18 ` Ahmad Fatoum
  2021-01-31 20:18 ` [PATCH 5/7] commands: add beep command Ahmad Fatoum
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Ahmad Fatoum @ 2021-01-31 20:18 UTC (permalink / raw)
  To: barebox

SDL 2.0.4 features SDL_QueueAudio, which we could use to just queue
buffers for audio playback. Use it to implement the new sound API for
the sandbox architecture.

Signed-off-by: Ahmad Fatoum <ahmad@a3f.at>
---
 arch/sandbox/configs/sandbox_defconfig        |  1 +
 arch/sandbox/dts/sandbox.dts                  |  4 +
 .../sandbox/mach-sandbox/include/mach/linux.h |  5 ++
 arch/sandbox/os/sdl.c                         | 44 ++++++++++
 drivers/sound/Kconfig                         |  5 ++
 drivers/sound/Makefile                        |  1 +
 drivers/sound/sdl.c                           | 87 +++++++++++++++++++
 7 files changed, 147 insertions(+)
 create mode 100644 drivers/sound/sdl.c

diff --git a/arch/sandbox/configs/sandbox_defconfig b/arch/sandbox/configs/sandbox_defconfig
index ca24d81acac7..66fcfbdbd0a3 100644
--- a/arch/sandbox/configs/sandbox_defconfig
+++ b/arch/sandbox/configs/sandbox_defconfig
@@ -107,6 +107,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 afe48154c488..ef1fa7b8661f 100644
--- a/arch/sandbox/dts/sandbox.dts
+++ b/arch/sandbox/dts/sandbox.dts
@@ -94,4 +94,8 @@ barebox,sandbox-power
 		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 c636a9af9cd2..6e10fdbe6d80 100644
--- a/arch/sandbox/mach-sandbox/include/mach/linux.h
+++ b/arch/sandbox/mach-sandbox/include/mach/linux.h
@@ -49,6 +49,11 @@ 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,
 						const char *serial);
diff --git a/arch/sandbox/os/sdl.c b/arch/sandbox/os/sdl.c
index 623e24cf0b26..13178abfc044 100644
--- a/arch/sandbox/os/sdl.c
+++ b/arch/sandbox/os/sdl.c
@@ -111,3 +111,47 @@ quit_subsystem:
 
 	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;
+	}
+
+	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_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_ClearQueuedAudio(dev);
+}
diff --git a/drivers/sound/Kconfig b/drivers/sound/Kconfig
index d9f63a5f3c55..889657305b0b 100644
--- a/drivers/sound/Kconfig
+++ b/drivers/sound/Kconfig
@@ -9,6 +9,11 @@ menuconfig SOUND
 
 if SOUND
 
+config SOUND_SDL
+	bool "SDL sound driver for sandbox"
+	depends on SANDBOX && OFDEVICE
+	select SDL
+
 config SYNTH_SQUARES
 	bool "Synthesize square waves only"
 	help
diff --git a/drivers/sound/Makefile b/drivers/sound/Makefile
index 69873faab985..692105fd6b59 100644
--- a/drivers/sound/Makefile
+++ b/drivers/sound/Makefile
@@ -1,2 +1,3 @@
 # SPDX-License-Identifier: GPL-2.0-only
 obj-y += core.o synth.o
+obj-$(CONFIG_SOUND_SDL)		+= sdl.o
diff --git a/drivers/sound/sdl.c b/drivers/sound/sdl.c
new file mode 100644
index 000000000000..118d7742955f
--- /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);
-- 
2.30.0


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH 5/7] commands: add beep command
  2021-01-31 20:18 [PATCH 0/7] Add sound and GRUB beep tune support Ahmad Fatoum
                   ` (3 preceding siblings ...)
  2021-01-31 20:18 ` [PATCH 4/7] sound: add SDL 2.0 sound driver Ahmad Fatoum
@ 2021-01-31 20:18 ` Ahmad Fatoum
  2021-01-31 20:18 ` [PATCH 6/7] sound: add PWM beeper support Ahmad Fatoum
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Ahmad Fatoum @ 2021-01-31 20:18 UTC (permalink / raw)
  To: barebox

Add a beep command that's compatible with GRUB's

  play "tempo pitch1 duration1..."

Unlike the GRUB command, playing a tune is not a blocking operating.
For this reason barebox, additionally supports:
  * -w: wait until tune is over
  * -c: cancel playing tune

Additionally, `beep tempo` can be used to ring the bell at a frequency
chosen by the sound card.

Examples:

  # 1-up
  beep 1750 523 1 392 1 523 1 659 1 784 1 1047 1 784 1 415 1 523 \
	1 622 1 831 1 622 1 831 1 1046 1 1244 1 1661 1

  # 1-second beep
  beep 60

Signed-off-by: Ahmad Fatoum <ahmad@a3f.at>
---
 commands/Kconfig  |  7 ++++
 commands/Makefile |  1 +
 commands/beep.c   | 99 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 107 insertions(+)
 create mode 100644 commands/beep.c

diff --git a/commands/Kconfig b/commands/Kconfig
index 03ddfc887074..b672f0c16a85 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 dc285cd00e1b..034c0e6383d3 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 000000000000..4e90215cbf26
--- /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 diraction2] ...]")
+	BAREBOX_CMD_HELP(cmd_beep_help)
+	BAREBOX_CMD_GROUP(CMD_GRP_CONSOLE)
+BAREBOX_CMD_END
-- 
2.30.0


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH 6/7] sound: add PWM beeper support
  2021-01-31 20:18 [PATCH 0/7] Add sound and GRUB beep tune support Ahmad Fatoum
                   ` (4 preceding siblings ...)
  2021-01-31 20:18 ` [PATCH 5/7] commands: add beep command Ahmad Fatoum
@ 2021-01-31 20:18 ` Ahmad Fatoum
  2021-01-31 20:18 ` [PATCH 7/7] sound: add gpio-beeper support Ahmad Fatoum
  2021-02-08 10:42 ` [PATCH 0/7] Add sound and GRUB beep tune support Sascha Hauer
  7 siblings, 0 replies; 9+ messages in thread
From: Ahmad Fatoum @ 2021-01-31 20:18 UTC (permalink / raw)
  To: barebox

This driver can be used to drive a piezo-buzzer attached to a PWM.

Signed-off-by: Ahmad Fatoum <ahmad@a3f.at>
---
 drivers/sound/Kconfig      |   6 ++
 drivers/sound/Makefile     |   1 +
 drivers/sound/pwm-beeper.c | 124 +++++++++++++++++++++++++++++++++++++
 include/pwm.h              |  33 ++++++++++
 4 files changed, 164 insertions(+)
 create mode 100644 drivers/sound/pwm-beeper.c

diff --git a/drivers/sound/Kconfig b/drivers/sound/Kconfig
index 889657305b0b..9b7bbd7e7a33 100644
--- a/drivers/sound/Kconfig
+++ b/drivers/sound/Kconfig
@@ -14,6 +14,12 @@ config SOUND_SDL
 	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 SYNTH_SQUARES
 	bool "Synthesize square waves only"
 	help
diff --git a/drivers/sound/Makefile b/drivers/sound/Makefile
index 692105fd6b59..468e5bee838d 100644
--- a/drivers/sound/Makefile
+++ b/drivers/sound/Makefile
@@ -1,3 +1,4 @@
 # 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
diff --git a/drivers/sound/pwm-beeper.c b/drivers/sound/pwm-beeper.c
new file mode 100644
index 000000000000..ef053f97cf47
--- /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/include/pwm.h b/include/pwm.h
index b67ab13d2e2d..2bd59fb8d3b6 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;
 
 /**
-- 
2.30.0


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH 7/7] sound: add gpio-beeper support
  2021-01-31 20:18 [PATCH 0/7] Add sound and GRUB beep tune support Ahmad Fatoum
                   ` (5 preceding siblings ...)
  2021-01-31 20:18 ` [PATCH 6/7] sound: add PWM beeper support Ahmad Fatoum
@ 2021-01-31 20:18 ` Ahmad Fatoum
  2021-02-08 10:42 ` [PATCH 0/7] Add sound and GRUB beep tune support Sascha Hauer
  7 siblings, 0 replies; 9+ messages in thread
From: Ahmad Fatoum @ 2021-01-31 20:18 UTC (permalink / raw)
  To: barebox

Add support for simple gpio-beepers.

Note that unlike with PWM buzzers, GPIO buzzers can't be controlled
in frequency and thus it doesn't make much sense to use the multiple
argument version of beep with them.

Signed-off-by: Ahmad Fatoum <ahmad@a3f.at>
---
 drivers/sound/Kconfig       |  6 +++
 drivers/sound/Makefile      |  1 +
 drivers/sound/gpio-beeper.c | 77 +++++++++++++++++++++++++++++++++++++
 3 files changed, 84 insertions(+)
 create mode 100644 drivers/sound/gpio-beeper.c

diff --git a/drivers/sound/Kconfig b/drivers/sound/Kconfig
index 9b7bbd7e7a33..bf6f715200e0 100644
--- a/drivers/sound/Kconfig
+++ b/drivers/sound/Kconfig
@@ -20,6 +20,12 @@ config PWM_BEEPER
 	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
diff --git a/drivers/sound/Makefile b/drivers/sound/Makefile
index 468e5bee838d..57d9cbd332f7 100644
--- a/drivers/sound/Makefile
+++ b/drivers/sound/Makefile
@@ -2,3 +2,4 @@
 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/gpio-beeper.c b/drivers/sound/gpio-beeper.c
new file mode 100644
index 000000000000..86fd4a4ee67c
--- /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);
-- 
2.30.0


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 0/7] Add sound and GRUB beep tune support
  2021-01-31 20:18 [PATCH 0/7] Add sound and GRUB beep tune support Ahmad Fatoum
                   ` (6 preceding siblings ...)
  2021-01-31 20:18 ` [PATCH 7/7] sound: add gpio-beeper support Ahmad Fatoum
@ 2021-02-08 10:42 ` Sascha Hauer
  7 siblings, 0 replies; 9+ messages in thread
From: Sascha Hauer @ 2021-02-08 10:42 UTC (permalink / raw)
  To: Ahmad Fatoum; +Cc: barebox

On Sun, Jan 31, 2021 at 09:18:39PM +0100, Ahmad Fatoum wrote:
> I've been meaning to upstream pwm-beeper support for a piezobuzzer,
> but it would be a real shame if the obnoxious buzzing would delay the
> boot. So let me present asynchronous beeping support for barebox.
> 
> If you don't have the suitable hardware lying around you can synth
> your own tunes on the barebox sandbox shell as well.
> 
> The Imperial March:
>   beep 480 440 4 440 4 440 4 349 3 523 1 440 4 349 3 523 1 440 8 659 4 659 4 659 4 698 3 523 1 415 4 349 3 523 1 440 8
> 
> Rebel's sabotage:
>   beep -c
> 
> Cheers,
> Ahmad Fatoum (7):
>   sandbox: migrate to SDL 2.0
>   drivers: add sound card driver support
>   sound: add basic synthesizers for PCM beeper use
>   sound: add SDL 2.0 sound driver
>   commands: add beep command
>   sound: add PWM beeper support
>   sound: add gpio-beeper support

Finally! I've been waiting for that for so long ;)

Applied, thanks

Sascha


-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2021-02-08 10:42 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-01-31 20:18 [PATCH 0/7] Add sound and GRUB beep tune support Ahmad Fatoum
2021-01-31 20:18 ` [PATCH 1/7] sandbox: migrate to SDL 2.0 Ahmad Fatoum
2021-01-31 20:18 ` [PATCH 2/7] drivers: add sound card driver support Ahmad Fatoum
2021-01-31 20:18 ` [PATCH 3/7] sound: add basic synthesizers for PCM beeper use Ahmad Fatoum
2021-01-31 20:18 ` [PATCH 4/7] sound: add SDL 2.0 sound driver Ahmad Fatoum
2021-01-31 20:18 ` [PATCH 5/7] commands: add beep command Ahmad Fatoum
2021-01-31 20:18 ` [PATCH 6/7] sound: add PWM beeper support Ahmad Fatoum
2021-01-31 20:18 ` [PATCH 7/7] sound: add gpio-beeper support Ahmad Fatoum
2021-02-08 10:42 ` [PATCH 0/7] Add sound and GRUB beep tune support Sascha Hauer

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox