|
@@ -279,6 +279,11 @@ struct wm_adsp_compr_buf {
|
|
|
|
|
|
struct wm_adsp_buffer_region *regions;
|
|
|
u32 host_buf_ptr;
|
|
|
+
|
|
|
+ u32 error;
|
|
|
+ u32 irq_count;
|
|
|
+ int read_index;
|
|
|
+ int avail;
|
|
|
};
|
|
|
|
|
|
struct wm_adsp_compr {
|
|
@@ -287,6 +292,8 @@ struct wm_adsp_compr {
|
|
|
|
|
|
struct snd_compr_stream *stream;
|
|
|
struct snd_compressed_buffer size;
|
|
|
+
|
|
|
+ unsigned int copied_total;
|
|
|
};
|
|
|
|
|
|
#define WM_ADSP_DATA_WORD_SIZE 3
|
|
@@ -2436,6 +2443,11 @@ static int wm_adsp_compr_check_params(struct snd_compr_stream *stream,
|
|
|
return -EINVAL;
|
|
|
}
|
|
|
|
|
|
+static inline unsigned int wm_adsp_compr_frag_words(struct wm_adsp_compr *compr)
|
|
|
+{
|
|
|
+ return compr->size.fragment_size / WM_ADSP_DATA_WORD_SIZE;
|
|
|
+}
|
|
|
+
|
|
|
int wm_adsp_compr_set_params(struct snd_compr_stream *stream,
|
|
|
struct snd_compr_params *params)
|
|
|
{
|
|
@@ -2622,6 +2634,8 @@ static int wm_adsp_buffer_init(struct wm_adsp *dsp)
|
|
|
return -ENOMEM;
|
|
|
|
|
|
buf->dsp = dsp;
|
|
|
+ buf->read_index = -1;
|
|
|
+ buf->irq_count = 0xFFFFFFFF;
|
|
|
|
|
|
ret = wm_adsp_buffer_locate(buf);
|
|
|
if (ret < 0) {
|
|
@@ -2705,6 +2719,16 @@ int wm_adsp_compr_trigger(struct snd_compr_stream *stream, int cmd)
|
|
|
ret);
|
|
|
break;
|
|
|
}
|
|
|
+
|
|
|
+ /* Trigger the IRQ at one fragment of data */
|
|
|
+ ret = wm_adsp_buffer_write(compr->buf,
|
|
|
+ HOST_BUFFER_FIELD(high_water_mark),
|
|
|
+ wm_adsp_compr_frag_words(compr));
|
|
|
+ if (ret < 0) {
|
|
|
+ adsp_err(dsp, "Failed to set high water mark: %d\n",
|
|
|
+ ret);
|
|
|
+ break;
|
|
|
+ }
|
|
|
break;
|
|
|
case SNDRV_PCM_TRIGGER_STOP:
|
|
|
break;
|
|
@@ -2719,4 +2743,168 @@ int wm_adsp_compr_trigger(struct snd_compr_stream *stream, int cmd)
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(wm_adsp_compr_trigger);
|
|
|
|
|
|
+static inline int wm_adsp_buffer_size(struct wm_adsp_compr_buf *buf)
|
|
|
+{
|
|
|
+ int last_region = wm_adsp_fw[buf->dsp->fw].caps->num_regions - 1;
|
|
|
+
|
|
|
+ return buf->regions[last_region].cumulative_size;
|
|
|
+}
|
|
|
+
|
|
|
+static int wm_adsp_buffer_update_avail(struct wm_adsp_compr_buf *buf)
|
|
|
+{
|
|
|
+ u32 next_read_index, next_write_index;
|
|
|
+ int write_index, read_index, avail;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ /* Only sync read index if we haven't already read a valid index */
|
|
|
+ if (buf->read_index < 0) {
|
|
|
+ ret = wm_adsp_buffer_read(buf,
|
|
|
+ HOST_BUFFER_FIELD(next_read_index),
|
|
|
+ &next_read_index);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ read_index = sign_extend32(next_read_index, 23);
|
|
|
+
|
|
|
+ if (read_index < 0) {
|
|
|
+ adsp_dbg(buf->dsp, "Avail check on unstarted stream\n");
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ buf->read_index = read_index;
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(next_write_index),
|
|
|
+ &next_write_index);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ write_index = sign_extend32(next_write_index, 23);
|
|
|
+
|
|
|
+ avail = write_index - buf->read_index;
|
|
|
+ if (avail < 0)
|
|
|
+ avail += wm_adsp_buffer_size(buf);
|
|
|
+
|
|
|
+ adsp_dbg(buf->dsp, "readindex=0x%x, writeindex=0x%x, avail=%d\n",
|
|
|
+ buf->read_index, write_index, avail);
|
|
|
+
|
|
|
+ buf->avail = avail;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+int wm_adsp_compr_handle_irq(struct wm_adsp *dsp)
|
|
|
+{
|
|
|
+ struct wm_adsp_compr_buf *buf = dsp->buffer;
|
|
|
+ int ret = 0;
|
|
|
+
|
|
|
+ mutex_lock(&dsp->pwr_lock);
|
|
|
+
|
|
|
+ if (!buf) {
|
|
|
+ adsp_err(dsp, "Spurious buffer IRQ\n");
|
|
|
+ ret = -ENODEV;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ adsp_dbg(dsp, "Handling buffer IRQ\n");
|
|
|
+
|
|
|
+ ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(error), &buf->error);
|
|
|
+ if (ret < 0) {
|
|
|
+ adsp_err(dsp, "Failed to check buffer error: %d\n", ret);
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+ if (buf->error != 0) {
|
|
|
+ adsp_err(dsp, "Buffer error occurred: %d\n", buf->error);
|
|
|
+ ret = -EIO;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(irq_count),
|
|
|
+ &buf->irq_count);
|
|
|
+ if (ret < 0) {
|
|
|
+ adsp_err(dsp, "Failed to get irq_count: %d\n", ret);
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = wm_adsp_buffer_update_avail(buf);
|
|
|
+ if (ret < 0) {
|
|
|
+ adsp_err(dsp, "Error reading avail: %d\n", ret);
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+out:
|
|
|
+ mutex_unlock(&dsp->pwr_lock);
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(wm_adsp_compr_handle_irq);
|
|
|
+
|
|
|
+static int wm_adsp_buffer_reenable_irq(struct wm_adsp_compr_buf *buf)
|
|
|
+{
|
|
|
+ if (buf->irq_count & 0x01)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ adsp_dbg(buf->dsp, "Enable IRQ(0x%x) for next fragment\n",
|
|
|
+ buf->irq_count);
|
|
|
+
|
|
|
+ buf->irq_count |= 0x01;
|
|
|
+
|
|
|
+ return wm_adsp_buffer_write(buf, HOST_BUFFER_FIELD(irq_ack),
|
|
|
+ buf->irq_count);
|
|
|
+}
|
|
|
+
|
|
|
+int wm_adsp_compr_pointer(struct snd_compr_stream *stream,
|
|
|
+ struct snd_compr_tstamp *tstamp)
|
|
|
+{
|
|
|
+ struct wm_adsp_compr *compr = stream->runtime->private_data;
|
|
|
+ struct wm_adsp_compr_buf *buf = compr->buf;
|
|
|
+ struct wm_adsp *dsp = compr->dsp;
|
|
|
+ int ret = 0;
|
|
|
+
|
|
|
+ adsp_dbg(dsp, "Pointer request\n");
|
|
|
+
|
|
|
+ mutex_lock(&dsp->pwr_lock);
|
|
|
+
|
|
|
+ if (!compr->buf) {
|
|
|
+ ret = -ENXIO;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (compr->buf->error) {
|
|
|
+ ret = -EIO;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (buf->avail < wm_adsp_compr_frag_words(compr)) {
|
|
|
+ ret = wm_adsp_buffer_update_avail(buf);
|
|
|
+ if (ret < 0) {
|
|
|
+ adsp_err(dsp, "Error reading avail: %d\n", ret);
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * If we really have less than 1 fragment available tell the
|
|
|
+ * DSP to inform us once a whole fragment is available.
|
|
|
+ */
|
|
|
+ if (buf->avail < wm_adsp_compr_frag_words(compr)) {
|
|
|
+ ret = wm_adsp_buffer_reenable_irq(buf);
|
|
|
+ if (ret < 0) {
|
|
|
+ adsp_err(dsp,
|
|
|
+ "Failed to re-enable buffer IRQ: %d\n",
|
|
|
+ ret);
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ tstamp->copied_total = compr->copied_total;
|
|
|
+ tstamp->copied_total += buf->avail * WM_ADSP_DATA_WORD_SIZE;
|
|
|
+
|
|
|
+out:
|
|
|
+ mutex_unlock(&dsp->pwr_lock);
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(wm_adsp_compr_pointer);
|
|
|
+
|
|
|
MODULE_LICENSE("GPL v2");
|