Skip to content

Commit

Permalink
Merge pull request #286 from martinezjavier/rpi-3.6.y-dev
Browse files Browse the repository at this point in the history
add mmap support and some cleanups to bcm2835 ALSA driver
  • Loading branch information
popcornmix committed Feb 22, 2014
1 parent 36fecbe commit 7ae3c3d
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 70 deletions.
69 changes: 43 additions & 26 deletions sound/arm/bcm2835-pcm.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@

/* hardware definition */
static struct snd_pcm_hardware snd_bcm2835_playback_hw = {
.info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER),
.info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID),
.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
.rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
.rate_min = 8000,
Expand Down Expand Up @@ -251,6 +252,12 @@ static int snd_bcm2835_pcm_prepare(struct snd_pcm_substream *substream)

audio_info(" .. IN\n");

memset(&alsa_stream->pcm_indirect, 0, sizeof(alsa_stream->pcm_indirect));

alsa_stream->pcm_indirect.hw_buffer_size =
alsa_stream->pcm_indirect.sw_buffer_size =
snd_pcm_lib_buffer_bytes(substream);

alsa_stream->buffer_size = snd_pcm_lib_buffer_bytes(substream);
alsa_stream->period_size = snd_pcm_lib_period_bytes(substream);
alsa_stream->pos = 0;
Expand All @@ -263,6 +270,32 @@ static int snd_bcm2835_pcm_prepare(struct snd_pcm_substream *substream)
return 0;
}

static void snd_bcm2835_pcm_transfer(struct snd_pcm_substream *substream,
struct snd_pcm_indirect *rec, size_t bytes)
{
struct snd_pcm_runtime *runtime = substream->runtime;
bcm2835_alsa_stream_t *alsa_stream = runtime->private_data;
void *src = (void *)(substream->runtime->dma_area + rec->sw_data);
int err;

err = bcm2835_audio_write(alsa_stream, bytes, src);
if (err)
audio_error(" Failed to transfer to alsa device (%d)\n", err);

}

static int snd_bcm2835_pcm_ack(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
bcm2835_alsa_stream_t *alsa_stream = runtime->private_data;
struct snd_pcm_indirect *pcm_indirect = &alsa_stream->pcm_indirect;

pcm_indirect->hw_queue_size = runtime->hw.buffer_bytes_max;
snd_pcm_indirect_playback_transfer(substream, pcm_indirect,
snd_bcm2835_pcm_transfer);
return 0;
}

/* trigger callback */
static int snd_bcm2835_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
{
Expand All @@ -279,6 +312,11 @@ static int snd_bcm2835_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
if (!alsa_stream->running) {
err = bcm2835_audio_start(alsa_stream);
if (err == 0) {
alsa_stream->pcm_indirect.hw_io =
alsa_stream->pcm_indirect.hw_data =
bytes_to_frames(runtime,
alsa_stream->pos);
substream->ops->ack(substream);
alsa_stream->running = 1;
alsa_stream->draining = 1;
} else {
Expand Down Expand Up @@ -327,30 +365,9 @@ snd_bcm2835_pcm_pointer(struct snd_pcm_substream *substream)
alsa_stream->pos);

audio_info(" .. OUT\n");
return bytes_to_frames(runtime, alsa_stream->pos);
}

static int snd_bcm2835_pcm_copy(struct snd_pcm_substream *substream,
int channel, snd_pcm_uframes_t pos, void *src,
snd_pcm_uframes_t count)
{
int ret;
struct snd_pcm_runtime *runtime = substream->runtime;
bcm2835_alsa_stream_t *alsa_stream = runtime->private_data;

audio_info(" .. IN\n");
audio_debug("copy.......... (%d) hwptr=%d appl=%d pos=%d\n",
frames_to_bytes(runtime, count), frames_to_bytes(runtime,
runtime->
status->
hw_ptr),
frames_to_bytes(runtime, runtime->control->appl_ptr),
alsa_stream->pos);
ret =
bcm2835_audio_write(alsa_stream, frames_to_bytes(runtime, count),
src);
audio_info(" .. OUT\n");
return ret;
return snd_pcm_indirect_playback_pointer(substream,
&alsa_stream->pcm_indirect,
alsa_stream->pos);
}

static int snd_bcm2835_pcm_lib_ioctl(struct snd_pcm_substream *substream,
Expand All @@ -372,7 +389,7 @@ static struct snd_pcm_ops snd_bcm2835_playback_ops = {
.prepare = snd_bcm2835_pcm_prepare,
.trigger = snd_bcm2835_pcm_trigger,
.pointer = snd_bcm2835_pcm_pointer,
.copy = snd_bcm2835_pcm_copy,
.ack = snd_bcm2835_pcm_ack,
};

/* create a pcm device */
Expand Down
89 changes: 62 additions & 27 deletions sound/arm/bcm2835-vchiq.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include <linux/delay.h>
#include <linux/atomic.h>
#include <linux/module.h>
#include <linux/completion.h>

#include "bcm2835.h"

Expand All @@ -37,6 +38,10 @@

/* ---- Private Constants and Types ------------------------------------------ */

#define BCM2835_AUDIO_STOP 0
#define BCM2835_AUDIO_START 1
#define BCM2835_AUDIO_WRITE 2

/* Logging macros (for remapping to other logging mechanisms, i.e., printf) */
#ifdef AUDIO_DEBUG_ENABLE
#define LOG_ERR( fmt, arg... ) pr_err( "%s:%d " fmt, __func__, __LINE__, ##arg)
Expand All @@ -53,7 +58,7 @@
typedef struct opaque_AUDIO_INSTANCE_T {
uint32_t num_connections;
VCHI_SERVICE_HANDLE_T vchi_handle[VCHI_MAX_NUM_CONNECTIONS];
struct semaphore msg_avail_event;
struct completion msg_avail_comp;
struct mutex vchi_mutex;
bcm2835_alsa_stream_t *alsa_stream;
int32_t result;
Expand All @@ -70,27 +75,35 @@ bool force_bulk = false;

static int bcm2835_audio_stop_worker(bcm2835_alsa_stream_t * alsa_stream);
static int bcm2835_audio_start_worker(bcm2835_alsa_stream_t * alsa_stream);
static int bcm2835_audio_write_worker(bcm2835_alsa_stream_t *alsa_stream,
uint32_t count, void *src);

typedef struct {
struct work_struct my_work;
bcm2835_alsa_stream_t *alsa_stream;
int x;
int cmd;
void *src;
uint32_t count;
} my_work_t;

static void my_wq_function(struct work_struct *work)
{
my_work_t *w = (my_work_t *) work;
int ret = -9;
LOG_DBG(" .. IN %p:%d\n", w->alsa_stream, w->x);
switch (w->x) {
case 1:
LOG_DBG(" .. IN %p:%d\n", w->alsa_stream, w->cmd);
switch (w->cmd) {
case BCM2835_AUDIO_START:
ret = bcm2835_audio_start_worker(w->alsa_stream);
break;
case 2:
case BCM2835_AUDIO_STOP:
ret = bcm2835_audio_stop_worker(w->alsa_stream);
break;
case BCM2835_AUDIO_WRITE:
ret = bcm2835_audio_write_worker(w->alsa_stream, w->count,
w->src);
break;
default:
LOG_ERR(" Unexpected work: %p:%d\n", w->alsa_stream, w->x);
LOG_ERR(" Unexpected work: %p:%d\n", w->alsa_stream, w->cmd);
break;
}
kfree((void *)work);
Expand All @@ -107,7 +120,7 @@ int bcm2835_audio_start(bcm2835_alsa_stream_t * alsa_stream)
if (work) {
INIT_WORK((struct work_struct *)work, my_wq_function);
work->alsa_stream = alsa_stream;
work->x = 1;
work->cmd = BCM2835_AUDIO_START;
if (queue_work
(alsa_stream->my_wq, (struct work_struct *)work))
ret = 0;
Expand All @@ -128,7 +141,31 @@ int bcm2835_audio_stop(bcm2835_alsa_stream_t * alsa_stream)
if (work) {
INIT_WORK((struct work_struct *)work, my_wq_function);
work->alsa_stream = alsa_stream;
work->x = 2;
work->cmd = BCM2835_AUDIO_STOP;
if (queue_work
(alsa_stream->my_wq, (struct work_struct *)work))
ret = 0;
} else
LOG_ERR(" .. Error: NULL work kmalloc\n");
}
LOG_DBG(" .. OUT %d\n", ret);
return ret;
}

int bcm2835_audio_write(bcm2835_alsa_stream_t *alsa_stream,
uint32_t count, void *src)
{
int ret = -1;
LOG_DBG(" .. IN\n");
if (alsa_stream->my_wq) {
my_work_t *work = kmalloc(sizeof(my_work_t), GFP_ATOMIC);
/*--- Queue some work (item 1) ---*/
if (work) {
INIT_WORK((struct work_struct *)work, my_wq_function);
work->alsa_stream = alsa_stream;
work->cmd = BCM2835_AUDIO_WRITE;
work->src = src;
work->count = count;
if (queue_work
(alsa_stream->my_wq, (struct work_struct *)work))
ret = 0;
Expand Down Expand Up @@ -178,7 +215,7 @@ static void audio_vchi_callback(void *param,
(" .. instance=%p, m.type=VC_AUDIO_MSG_TYPE_RESULT, success=%d\n",
instance, m.u.result.success);
instance->result = m.u.result.success;
up(&instance->msg_avail_event);
complete(&instance->msg_avail_comp);
} else if (m.type == VC_AUDIO_MSG_TYPE_COMPLETE) {
irq_handler_t callback = (irq_handler_t) m.u.complete.callback;
LOG_DBG
Expand Down Expand Up @@ -435,8 +472,8 @@ static int bcm2835_audio_set_ctls_chan(bcm2835_alsa_stream_t * alsa_stream,
m.u.control.dest = chip->dest;
m.u.control.volume = chip->volume;

/* Create the message available event */
sema_init(&instance->msg_avail_event, 0);
/* Create the message available completion */
init_completion(&instance->msg_avail_comp);

/* Send the message to the videocore */
success = vchi_msg_queue(instance->vchi_handle[0],
Expand All @@ -452,11 +489,10 @@ static int bcm2835_audio_set_ctls_chan(bcm2835_alsa_stream_t * alsa_stream,
}

/* We are expecting a reply from the videocore */
if (down_interruptible(&instance->msg_avail_event)) {
ret = wait_for_completion_interruptible(&instance->msg_avail_comp);
if (ret) {
LOG_ERR("%s: failed on waiting for event (status=%d)\n",
__func__, success);

ret = -1;
goto unlock;
}

Expand Down Expand Up @@ -539,8 +575,8 @@ int bcm2835_audio_set_params(bcm2835_alsa_stream_t * alsa_stream,
m.u.config.samplerate = samplerate;
m.u.config.bps = bps;

/* Create the message available event */
sema_init(&instance->msg_avail_event, 0);
/* Create the message available completion */
init_completion(&instance->msg_avail_comp);

/* Send the message to the videocore */
success = vchi_msg_queue(instance->vchi_handle[0],
Expand All @@ -556,11 +592,10 @@ int bcm2835_audio_set_params(bcm2835_alsa_stream_t * alsa_stream,
}

/* We are expecting a reply from the videocore */
if (down_interruptible(&instance->msg_avail_event)) {
ret = wait_for_completion_interruptible(&instance->msg_avail_comp);
if (ret) {
LOG_ERR("%s: failed on waiting for event (status=%d)\n",
__func__, success);

ret = -1;
goto unlock;
}

Expand Down Expand Up @@ -688,8 +723,8 @@ int bcm2835_audio_close(bcm2835_alsa_stream_t * alsa_stream)

m.type = VC_AUDIO_MSG_TYPE_CLOSE;

/* Create the message available event */
sema_init(&instance->msg_avail_event, 0);
/* Create the message available completion */
init_completion(&instance->msg_avail_comp);

/* Send the message to the videocore */
success = vchi_msg_queue(instance->vchi_handle[0],
Expand All @@ -702,11 +737,11 @@ int bcm2835_audio_close(bcm2835_alsa_stream_t * alsa_stream)
ret = -1;
goto unlock;
}
if (down_interruptible(&instance->msg_avail_event)) {

ret = wait_for_completion_interruptible(&instance->msg_avail_comp);
if (ret) {
LOG_ERR("%s: failed on waiting for event (status=%d)",
__func__, success);

ret = -1;
goto unlock;
}
if (instance->result != 0) {
Expand All @@ -732,8 +767,8 @@ int bcm2835_audio_close(bcm2835_alsa_stream_t * alsa_stream)
return ret;
}

int bcm2835_audio_write(bcm2835_alsa_stream_t * alsa_stream, uint32_t count,
void *src)
int bcm2835_audio_write_worker(bcm2835_alsa_stream_t *alsa_stream,
uint32_t count, void *src)
{
VC_AUDIO_MSG_T m;
AUDIO_INSTANCE_T *instance = alsa_stream->instance;
Expand Down
Loading

0 comments on commit 7ae3c3d

Please sign in to comment.