Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add mmap support and some cleanups to bcm2835 ALSA driver #286

Merged
merged 5 commits into from
Apr 26, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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