Permalink
Browse files

driver: SND_PWMSP

Signed-off-by: Robert Nelson <robertcnelson@gmail.com>
  • Loading branch information...
RobertCNelson committed Aug 4, 2018
1 parent db0af6b commit b310e62d566fed9ab3f37da79fffe2780c653cbf
View
@@ -184,6 +184,15 @@ config SND_PORTMAN2X4
To compile this driver as a module, choose M here: the module
will be called snd-portman2x4.
config SND_PWMSP
tristate "PWM Speaker driver (PCM)"
depends on HIGH_RES_TIMERS
select SND_PCM
select PWM
help
To compile this driver as a module, choose M here: the module
will be called snd-pwm.
config SND_ML403_AC97CR
tristate "Xilinx ML403 AC97 Controller Reference"
depends on XILINX_VIRTEX
View
@@ -23,4 +23,4 @@ obj-$(CONFIG_SND_MTS64) += snd-mts64.o
obj-$(CONFIG_SND_PORTMAN2X4) += snd-portman2x4.o
obj-$(CONFIG_SND_ML403_AC97CR) += snd-ml403-ac97cr.o
obj-$(CONFIG_SND) += opl3/ opl4/ mpu401/ vx/ pcsp/
obj-$(CONFIG_SND) += opl3/ opl4/ mpu401/ vx/ pcsp/ pwmsp/
@@ -0,0 +1,2 @@
snd-pwmsp-objs := pwmsp.o pwmsp_lib.o
obj-$(CONFIG_SND_PWMSP) += snd-pwmsp.o
View
@@ -0,0 +1,174 @@
/*
* 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");
@@ -0,0 +1,58 @@
/*
* 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
Oops, something went wrong.

0 comments on commit b310e62

Please sign in to comment.