Skip to content

Instantly share code, notes, and snippets.

@jadonk
Last active July 19, 2019 18:57
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jadonk/d435886df74a061052d82c1d7e22313b to your computer and use it in GitHub Desktop.
Save jadonk/d435886df74a061052d82c1d7e22313b to your computer and use it in GitHub Desktop.
pwmsp driver

http://git.infradead.org/users/ezequielg/linux/commitdiff/a71564c13877aac573232d9d203ea7cdaacb072b https://patchwork.kernel.org/patch/6217971/

sudo apt-get install linux-headers-`uname -r`
make -C /lib/modules/`uname -r`/build M=$PWD modules V=1 ARCH=arm
sudo make -C /lib/modules/`uname -r`/build M=$PWD modules_install V=1 ARCH=arm
sudo /opt/scripts/tools/developers/update_initrd.sh

reboot

config-pin p1.33 pwm
sudo modprobe snd_pwmsp
aplay /usr/share/sounds/alsa/Front_Center.wav

Test PWM

config-pin p1.33 pwm
echo 1 > /sys/class/pwm/pwmchip0/export
sleep 1
echo 1000000 > /sys/class/pwm/pwm-0\:1/period
echo 500000 > /sys/class/pwm/pwm-0\:1/duty_cycle
echo 1 > /sys/class/pwm/pwm-0\:1/enable
sleep 3
echo 0 > /sys/class/pwm/pwm-0\:1/enable
echo 1 > /sys/class/pwm/pwmchip0/unexport

Examine PWM driver status

sudo cat /sys/kernel/debug/pwm
config SND_PWMSP
tristate "PWM Speaker driver (PCM)"
depends on HIGH_RES_TIMERS
select SND_PCM
select PWM
help
snd-pwmsp-objs := pwmsp.o pwmsp_lib.o
obj-m += snd-pwmsp.o
/*
* Based on PC-Speaker driver for Linux
*
* Copyright (C) 1997-2001 David Woodhouse
* Copyright (C) 2001-2008 Stas Sergeev
*
* sndpwm {
* compatible = "snd-pwm";
* pwms = <&ehrpwm2 1 0 0>;
* status = "okay";
* };
*
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <sound/core.h>
#include <sound/initval.h>
#include <sound/pcm.h>
#include <linux/delay.h>
#include <asm/bitops.h>
#include <linux/pwm.h>
#include "pwmsp.h"
static int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */
static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */
static bool enable = SNDRV_DEFAULT_ENABLE1; /* Enable this card */
module_param(index, int, 0444);
MODULE_PARM_DESC(index, "Index value for pwmsp soundcard.");
module_param(id, charp, 0444);
MODULE_PARM_DESC(id, "ID string for pwmsp soundcard.");
module_param(enable, bool, 0444);
MODULE_PARM_DESC(enable, "Enable PC-Speaker sound.");
struct snd_pwmsp pwmsp_chip;
static int snd_pwmsp_create(struct snd_card *card)
{
static struct snd_device_ops ops = { };
int err;
pr_info("PCSP: Timer resolution is (%linS)\n", hrtimer_resolution);
spin_lock_init(&pwmsp_chip.substream_lock);
atomic_set(&pwmsp_chip.timer_active, 0);
pwmsp_chip.playback_ptr = 0;
pwmsp_chip.period_ptr = 0;
pwmsp_chip.enable = 1;
pwmsp_chip.card = card;
/* Register device */
err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, &pwmsp_chip, &ops);
if (err < 0)
return err;
return 0;
}
static int snd_card_pwmsp_probe(int devnum, struct device *dev)
{
struct snd_card *card;
int err;
if (devnum != 0)
return -EINVAL;
hrtimer_init(&pwmsp_chip.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
pwmsp_chip.timer.function = pwmsp_do_timer;
err = snd_card_new(dev, index, id, THIS_MODULE, 0, &card);
if (err < 0) {
printk(KERN_ERR "PWM PC-Speaker snd_card_new() call failed.\n");
return err;
}
err = snd_pwmsp_create(card);
if (err < 0) {
printk(KERN_ERR "PWM PC-Speaker snd_pwmsp_create() call failed.\n");
snd_card_free(card);
return err;
}
err = snd_pwmsp_new_pcm(&pwmsp_chip);
if (err < 0) {
printk(KERN_ERR "PWM PC-Speaker snd_pwmsp_new_pcm() call failed.\n");
snd_card_free(card);
return err;
}
strcpy(card->driver, "PC-Speaker");
strcpy(card->shortname, "pwmsp");
sprintf(card->longname, "PWM PC-Speaker");
err = snd_card_register(card);
if (err < 0) {
printk(KERN_ERR "PWM PC-Speaker snd_card_register() call failed.\n");
snd_card_free(card);
return err;
}
return 0;
}
static int alsa_card_pwmsp_init(struct device *dev)
{
int err;
err = snd_card_pwmsp_probe(0, dev);
if (err) {
printk(KERN_ERR "PC-Speaker initialization failed.\n");
return err;
}
#ifdef CONFIG_DEBUG_PAGEALLOC
/* Well, CONFIG_DEBUG_PAGEALLOC makes the sound horrible. Lets alert */
printk(KERN_WARNING "PCSP: CONFIG_DEBUG_PAGEALLOC is enabled, "
"which may make the sound noisy.\n");
#endif
return 0;
}
static void alsa_card_pwmsp_exit(struct snd_pwmsp *chip)
{
snd_card_free(chip->card);
}
static int pwmsp_probe(struct platform_device *dev)
{
int err;
pwmsp_chip.pwm = devm_pwm_get(&dev->dev, NULL);
if (IS_ERR(pwmsp_chip.pwm)) {
printk(KERN_ERR "PWM PC-Speaker devm_pwm_get() call failed.\n");
dev_err(&dev->dev, "unable to request PWM\n");
err = PTR_ERR(pwmsp_chip.pwm);
return err;
}
err = alsa_card_pwmsp_init(&dev->dev);
if (err < 0) {
printk(KERN_ERR "PWM PC-Speaker alsa_card_pwmsp_init() call failed.\n");
return err;
}
platform_set_drvdata(dev, &pwmsp_chip);
return 0;
}
static int pwmsp_remove(struct platform_device *dev)
{
struct snd_pwmsp *chip = platform_get_drvdata(dev);
alsa_card_pwmsp_exit(chip);
return 0;
}
static struct of_device_id pwmsp_of_match[] = {
{ .compatible = "snd-pwmsp" },
{ },
};
static struct platform_driver pwmsp_platform_driver = {
.driver = {
.name = "pwmspkr",
.owner = THIS_MODULE,
.of_match_table = pwmsp_of_match,
},
.probe = pwmsp_probe,
.remove = pwmsp_remove,
};
module_platform_driver(pwmsp_platform_driver);
MODULE_LICENSE("GPL");
/*
* PC-Speaker driver for Linux
*
* Copyright (C) 1993-1997 Michael Beck
* Copyright (C) 1997-2001 David Woodhouse
* Copyright (C) 2001-2008 Stas Sergeev
*/
#ifndef __PCSP_H__
#define __PCSP_H__
#include <linux/hrtimer.h>
#include <linux/timex.h>
#define MAX_DUTY_NS 998
#define MIN_DUTY_NS 10
#define DELTA_DUTY (MAX_DUTY_NS - MIN_DUTY_NS)
#define NSECS_PER_SEC 1000000000UL
#define HZ_TO_NANOSECONDS(x) (NSECS_PER_SEC / (x))
#define PWM_FREQ 1000000UL /* Period is 1us */
#define SAMPLING_FREQ 44100
#define PWM_PERIOD_NS HZ_TO_NANOSECONDS(PWM_FREQ)
#define SAMPLING_PERIOD_NS HZ_TO_NANOSECONDS(SAMPLING_FREQ)
#define PWMSP_MAX_PERIOD_SIZE (64*1024)
#define PWMSP_MAX_PERIODS 512
#define PWMSP_BUFFER_SIZE (128*1024)
struct snd_pwmsp {
struct snd_card *card;
struct snd_pcm *pcm;
struct hrtimer timer;
spinlock_t substream_lock;
struct snd_pcm_substream *playback_substream;
unsigned int fmt_size;
unsigned int is_signed;
size_t playback_ptr;
size_t period_ptr;
atomic_t timer_active;
int enable;
struct pwm_device *pwm;
};
extern struct snd_pwmsp pwmsp_chip;
extern enum hrtimer_restart pwmsp_do_timer(struct hrtimer *handle);
extern void pwmsp_sync_stop(struct snd_pwmsp *chip);
extern int snd_pwmsp_new_pcm(struct snd_pwmsp *chip);
extern int snd_pwmsp_new_mixer(struct snd_pwmsp *chip, int nopcm);
void pwmsp_stop_sound(void);
#endif
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
/*
* Based on PC-Speaker driver for Linux
*
* Copyright (C) 1993-1997 Michael Beck
* Copyright (C) 1997-2001 David Woodhouse
* Copyright (C) 2001-2008 Stas Sergeev
*/
#include <linux/module.h>
#include <linux/gfp.h>
#include <linux/moduleparam.h>
#include <linux/interrupt.h>
#include <linux/pwm.h>
#include <sound/pcm.h>
#include "pwmsp.h"
//#define PWMSP_DEBUG
#define DEBUG_TIME
static __maybe_unused ktime_t calltime, delta, rettime;
static __maybe_unused unsigned long duration;
static __maybe_unused unsigned long pwm_config_calls;
/*
* Call snd_pcm_period_elapsed in a tasklet
* This avoids spinlock messes and long-running irq contexts
*/
static void pwmsp_call_pcm_elapsed(unsigned long priv)
{
if (atomic_read(&pwmsp_chip.timer_active)) {
struct snd_pcm_substream *substream;
substream = pwmsp_chip.playback_substream;
if (substream)
snd_pcm_period_elapsed(substream);
#ifdef DEBUG_TIME
pr_debug("%lu calls, %lu ns, %lu ns per call\n",
pwm_config_calls, duration,
duration / pwm_config_calls);
pwm_config_calls = 0;
duration = 0;
#endif
}
}
static DECLARE_TASKLET(pwmsp_pcm_tasklet, pwmsp_call_pcm_elapsed, 0);
/* write the port and returns the next expire time in ns;
* called at the trigger-start and in hrtimer callback
*/
static u64 pwmsp_timer_update(struct snd_pwmsp *chip)
{
struct snd_pcm_substream *substream;
struct snd_pcm_runtime *runtime;
int duty_ns;
s16 val;
substream = chip->playback_substream;
if (!substream)
return 0;
runtime = substream->runtime;
/* TODO: Revisit here */
val = runtime->dma_area[chip->playback_ptr] |
runtime->dma_area[chip->playback_ptr + 1] << 8;
duty_ns = (val * DELTA_DUTY) / 65535 + DELTA_DUTY / 2 + MIN_DUTY_NS;
#ifdef DEBUG_TIME
calltime = ktime_get();
#endif
if (duty_ns && chip->enable)
pwm_config(chip->pwm, duty_ns, PWM_PERIOD_NS);
#ifdef DEBUG_TIME
rettime = ktime_get();
delta = ktime_sub(rettime, calltime);
duration += (unsigned long long) ktime_to_ns(delta) >> 10;
pwm_config_calls++;
#endif
return SAMPLING_PERIOD_NS;
}
static void pwmsp_pointer_update(struct snd_pwmsp *chip)
{
struct snd_pcm_substream *substream;
size_t period_bytes, buffer_bytes;
int periods_elapsed;
unsigned long flags;
/* update the playback position */
substream = chip->playback_substream;
if (!substream)
return;
period_bytes = snd_pcm_lib_period_bytes(substream);
buffer_bytes = snd_pcm_lib_buffer_bytes(substream);
spin_lock_irqsave(&chip->substream_lock, flags);
chip->playback_ptr += chip->fmt_size;
periods_elapsed = chip->playback_ptr - chip->period_ptr;
if (periods_elapsed < 0) {
#ifdef PWMSP_DEBUG
printk(KERN_INFO "PWMSP: buffer_bytes mod period_bytes != 0 ? "
"(%zi %zi %zi)\n",
chip->playback_ptr, period_bytes, buffer_bytes);
#endif
periods_elapsed += buffer_bytes;
}
periods_elapsed /= period_bytes;
/* wrap the pointer _before_ calling snd_pcm_period_elapsed(),
* or ALSA will BUG on us. */
chip->playback_ptr %= buffer_bytes;
if (periods_elapsed) {
chip->period_ptr += periods_elapsed * period_bytes;
chip->period_ptr %= buffer_bytes;
}
spin_unlock_irqrestore(&chip->substream_lock, flags);
if (periods_elapsed)
tasklet_schedule(&pwmsp_pcm_tasklet);
}
enum hrtimer_restart pwmsp_do_timer(struct hrtimer *handle)
{
struct snd_pwmsp *chip = container_of(handle, struct snd_pwmsp, timer);
u64 ns;
if (!atomic_read(&chip->timer_active) || !chip->playback_substream)
return HRTIMER_NORESTART;
ns = pwmsp_timer_update(chip);
if (!ns) {
printk(KERN_WARNING "PWMSP: unexpected stop\n");
return HRTIMER_NORESTART;
}
hrtimer_forward(handle, hrtimer_get_expires(handle), ns_to_ktime(ns));
pwmsp_pointer_update(chip);
return HRTIMER_RESTART;
}
static int pwmsp_start_playing(struct snd_pwmsp *chip)
{
#ifdef PWMSP_DEBUG
printk(KERN_INFO "PWMSP: start_playing called\n");
#endif
if (atomic_read(&chip->timer_active)) {
printk(KERN_ERR "PWMSP: Timer already active\n");
return -EIO;
}
/* Start the PWM */
pwm_enable(chip->pwm);
pwm_config(chip->pwm, 0, PWM_PERIOD_NS);
atomic_set(&chip->timer_active, 1);
hrtimer_start(&pwmsp_chip.timer, ktime_set(0, 0), HRTIMER_MODE_REL);
return 0;
}
static void pwmsp_stop_playing(struct snd_pwmsp *chip)
{
#ifdef PWMSP_DEBUG
printk(KERN_INFO "PWMSP: stop_playing called\n");
#endif
if (!atomic_read(&chip->timer_active))
return;
atomic_set(&chip->timer_active, 0);
pwm_disable(chip->pwm);
}
/*
* Force to stop and sync the stream
*/
void pwmsp_sync_stop(struct snd_pwmsp *chip)
{
local_irq_disable();
pwmsp_stop_playing(chip);
local_irq_enable();
hrtimer_cancel(&chip->timer);
tasklet_kill(&pwmsp_pcm_tasklet);
}
static int snd_pwmsp_playback_close(struct snd_pcm_substream *substream)
{
struct snd_pwmsp *chip = snd_pcm_substream_chip(substream);
#ifdef PWMSP_DEBUG
printk(KERN_INFO "PWMSP: close called\n");
#endif
pwmsp_sync_stop(chip);
chip->playback_substream = NULL;
return 0;
}
static int snd_pwmsp_playback_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
struct snd_pwmsp *chip = snd_pcm_substream_chip(substream);
int err;
pwmsp_sync_stop(chip);
err = snd_pcm_lib_malloc_pages(substream,
params_buffer_bytes(hw_params));
if (err < 0)
return err;
return 0;
}
static int snd_pwmsp_playback_hw_free(struct snd_pcm_substream *substream)
{
struct snd_pwmsp *chip = snd_pcm_substream_chip(substream);
#ifdef PWMSP_DEBUG
printk(KERN_INFO "PWMSP: hw_free called\n");
#endif
pwmsp_sync_stop(chip);
return snd_pcm_lib_free_pages(substream);
}
static int snd_pwmsp_playback_prepare(struct snd_pcm_substream *substream)
{
struct snd_pwmsp *chip = snd_pcm_substream_chip(substream);
pwmsp_sync_stop(chip);
chip->playback_ptr = 0;
chip->period_ptr = 0;
chip->fmt_size =
snd_pcm_format_physical_width(substream->runtime->format) >> 3;
chip->is_signed = snd_pcm_format_signed(substream->runtime->format);
#ifdef PWMSP_DEBUG
printk(KERN_INFO "PWMSP: prepare called, "
"size=%zi psize=%zi f=%zi f1=%i fsize=%i\n",
snd_pcm_lib_buffer_bytes(substream),
snd_pcm_lib_period_bytes(substream),
snd_pcm_lib_buffer_bytes(substream) /
snd_pcm_lib_period_bytes(substream),
substream->runtime->periods,
chip->fmt_size);
#endif
return 0;
}
static int snd_pwmsp_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct snd_pwmsp *chip = snd_pcm_substream_chip(substream);
#ifdef PWMSP_DEBUG
printk(KERN_INFO "PWMSP: trigger called\n");
#endif
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
return pwmsp_start_playing(chip);
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
pwmsp_stop_playing(chip);
break;
default:
return -EINVAL;
}
return 0;
}
static snd_pcm_uframes_t snd_pwmsp_playback_pointer(struct snd_pcm_substream
*substream)
{
struct snd_pwmsp *chip = snd_pcm_substream_chip(substream);
unsigned int pos;
spin_lock(&chip->substream_lock);
pos = chip->playback_ptr;
spin_unlock(&chip->substream_lock);
return bytes_to_frames(substream->runtime, pos);
}
static struct snd_pcm_hardware snd_pwmsp_playback = {
.info = (SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_HALF_DUPLEX |
SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID),
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.rates = SNDRV_PCM_RATE_KNOT,
.rate_min = SAMPLING_FREQ,
.rate_max = SAMPLING_FREQ,
.channels_min = 1,
.channels_max = 1,
.buffer_bytes_max = PWMSP_BUFFER_SIZE,
.period_bytes_min = 64,
.period_bytes_max = PWMSP_MAX_PERIOD_SIZE,
.periods_min = 2,
.periods_max = PWMSP_MAX_PERIODS,
.fifo_size = 0,
};
static int snd_pwmsp_playback_open(struct snd_pcm_substream *substream)
{
struct snd_pwmsp *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
#ifdef PWMSP_DEBUG
printk(KERN_INFO "PWMSP: open called\n");
#endif
if (atomic_read(&chip->timer_active)) {
printk(KERN_ERR "PWMSP: still active!!\n");
return -EBUSY;
}
runtime->hw = snd_pwmsp_playback;
chip->playback_substream = substream;
return 0;
}
static struct snd_pcm_ops snd_pwmsp_playback_ops = {
.open = snd_pwmsp_playback_open,
.close = snd_pwmsp_playback_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_pwmsp_playback_hw_params,
.hw_free = snd_pwmsp_playback_hw_free,
.prepare = snd_pwmsp_playback_prepare,
.trigger = snd_pwmsp_trigger,
.pointer = snd_pwmsp_playback_pointer,
};
int snd_pwmsp_new_pcm(struct snd_pwmsp *chip)
{
int err;
err = snd_pcm_new(chip->card, "pwmspeaker", 0, 1, 0, &chip->pcm);
if (err < 0)
return err;
snd_pcm_set_ops(chip->pcm, SNDRV_PCM_STREAM_PLAYBACK,
&snd_pwmsp_playback_ops);
chip->pcm->private_data = chip;
chip->pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX;
strcpy(chip->pcm->name, "pwmsp");
snd_pcm_lib_preallocate_pages_for_all(chip->pcm,
SNDRV_DMA_TYPE_CONTINUOUS,
snd_dma_continuous_data
(GFP_KERNEL), PWMSP_BUFFER_SIZE,
PWMSP_BUFFER_SIZE);
return 0;
}
@wolfallein
Copy link

wolfallein commented Jul 16, 2019

Hi,

I'm trying to use this code on the BBB wireless. I managed to get it working with a customized dtbo, but I tried everything to change the PWM port to 1, but I only have audio on P9.28. I don't know why. I would like to use another pin since I want to use this pin to another application.

My dts is attached:

`

/dts-v1/;
/plugin/;

/ {
        compatible = "ti,beaglebone", "ti,beaglebone-black";

        /* identification */
        part-number = "BB-SND-PWM1";
        version = "00A0";

        /* state the resources this cape uses */
        exclusive-use =
                "P9.14",
                "P9.16";

        /*
         * Free up the pins used by the cape from the pinmux helpers.
         */
        fragment@0 {
                target = <&ocp>;
                __overlay__ {
                        P9_14_pinmux { status = "disabled"; };  /* (U14) gpmc_a2.ehrpwm1A */
                        P9_16_pinmux { status = "disabled"; };  /* (T14) gpmc_a3.ehrpwm1B */
                };
        };

        fragment@1 {
                target = <&am33xx_pinmux>;
                __overlay__ {
                        bb_pwm1_pin: pinmux-pwm1-pin {
                                pinctrl-single,pins = <
                                        0x48 0x06 /* (U14) gpmc_a2.ehrpwm1A */
                                        0x4c 0x06 /* (B17) gpmc_a3.ehrpwm1B */
                                >;
                        };
                };
        };

        fragment@2 {
                target = <&ocp>;
                __overlay__ {
                        bb_pwm1_test_helper: bb_snd-pwm1 {
                                compatible = "bone-pinmux-helper";
                                pinctrl-names = "default";
                                pinctrl-0 = <&bb_pwm1_pin>;
                                status = "okay";
                        };
                };
        };

        fragment@3 {
                target = <&epwmss1>;
                __overlay__ {
                        status = "okay";
                };
        };


        fragment@4 {
                target = <&ehrpwm1>;
                __overlay__ {
                        status = "okay";
                };
        };

        /*
         * Load ALSA driver for piezo
         */
        fragment@5 {
                target-path="/";
                __overlay__ {
                        sndpwm {
                                compatible = "snd-pwmsp";
                                pwms = <&ehrpwm1 1 0 0>;
                                status = "okay";
                        };
                };
        };`

I would like to know if there is an electronic diagram to connect a piezo, or a small speaker. I tried to use a piezo with a low pass filter but still very noisy...

I also need to disable the cape universal from uEnv.txt or my system crash when I try to play a sound.

Thank you in advance.

Best regards,
Joao Pereira

@wolfallein
Copy link

I updated the kernel, and now looks like my *.dtbo is not doing anything. The snd-pwmsp module is auto enabling at boot time even if I remove my dtbo from uEnv.txt. This works with cape_universal disabled.

Is there a way to change the pwm pins used by the module?

@jadonk
Copy link
Author

jadonk commented Jul 17, 2019

@wolfallein I tested this with https://beagleboard.org/gamepup and my overlay is at https://github.com/beagleboard/bb.org-overlays/blob/master/src/arm/BBORG_GAMEPUP-00A2.dts. However, I experienced a regression at some point and haven't run it back down again. I have someone new asking me about it, so it looks like it could get some testing.

The line

         pwms = <&ehrpwm1 1 0 0>;

sets the PWM. The first argument is the PWM (as located in the main device tree) and the second argument is the channel. I guess this entry above would be EHRPWM1B (as channel indexes start at 0). See https://www.kernel.org/doc/Documentation/devicetree/bindings/pwm/pwm.txt.

@wolfallein
Copy link

@jadonk Thank you for replying me. :)

I tried with the BBORG_GAMEUP-00A2.dts but I had a similar problem, even in my pocketbeagle, as the overlay is made for.

I'm new to beagleboards, and what I think what happened is that I was listening to the i2s bus on pin 9.28 because the line disable_uboot_overlay_audio=1 was commented in the uEnv.txt, and in the kernel messages I had:

[ 2.913839] ALSA device list: [ 2.916851] #0: TI BeagleBone Black
I think that this uboot overlay disable the hdmi when doesn't detect any display, and direct the i2s sound to P9.28, P9.29, and P9.31 (https://www.element14.com/community/community/designcenter/single-board-computers/next-gen_beaglebone//blog/2013/05/28/bbb--audio-notes) I couldn't find detailed informations about these u-boot lines...

In this case, the dtbo is not working, and what I thought was the module was just the i2s signal. Disabling the audio overlay on uEnv.txt removes the ALSA (i2s?) card. Probably this should be done to get pwmsp working.

In any case, with your overlay, or mine my system crashes when I try to play any audio. I tried different kernel versions and also with one that I compiled.

For some reason, pwmsp is not talking to the pwm hardware.

Can you tell me in what kernel you had your overlay working? Or even what Debian version? I tried on the actual stable image of IoT Debian 9.5 and with kernels 4.14.71-ti-r80, 4.14.108-ti-r108, and a 4.9 that I compiled.

Thnk you

@jadonk
Copy link
Author

jadonk commented Jul 18, 2019

I was also seeing hangs when I tried to play audio and was confused because it worked for me when I manually built and tested. Just today, I figured out to try to test the PWM first, then load the driver and it started working again. It seems there is a bug in the driver that keeps it from configuring the driver correctly at first.

Try running the "Test PWM" instructions first.

jadonk/bb.org-overlays@b6aecdd

@wolfallein
Copy link

@jadonk
I found the problem. My system was freezing when I was trying to play any sound, but I could see the kernel messages, and I also used #define PWMSP_DEBUG on pwmsp.c to see where it was happening.

I figured out that the driver was having a problem on the line #162 atomic_set(&chip->timer_active, 1); of pwmsp_lib.c I could fix the problem adding the line pm_runtime_irq_safe(&pdev->dev); to the drivers/pwm/pwm-tiehrpwm.c (like here) from the sources of the kernel 4.14.108.

When I recompiled the kernel I got sound :)

The inconvenience is that the sound work only in the second attempt. I have to start playing something, stop, and start again, and them it works.

I'm using the dts that I pasted in the previous message, and then I don't need to use config-pin pxx pwm, and I can also disable cape universal to save some seconds in the boot.

So, maybe this can be added to future builds of the kernel to avoid this problem, but I don't know what else can be impacted by the line...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment