{ "IN2L PGA", NULL, "IN2L" },
{ "IN2R PGA", NULL, "IN2R" },
+ { "Audio Trace DSP", NULL, "DSP2" },
+ { "Audio Trace DSP", NULL, "SYSCLK" },
+
ARIZONA_MIXER_ROUTES("OUT1L", "HPOUT1L"),
ARIZONA_MIXER_ROUTES("OUT1R", "HPOUT1R"),
.formats = CS47L24_FORMATS,
},
},
+ {
+ .name = "cs47l24-cpu-trace",
+ .capture = {
+ .stream_name = "Audio Trace CPU",
+ .channels_min = 1,
+ .channels_max = 6,
+ .rates = CS47L24_RATES,
+ .formats = CS47L24_FORMATS,
+ },
+ .compress_new = snd_soc_new_compress,
+ },
+ {
+ .name = "cs47l24-dsp-trace",
+ .capture = {
+ .stream_name = "Audio Trace DSP",
+ .channels_min = 1,
+ .channels_max = 6,
+ .rates = CS47L24_RATES,
+ .formats = CS47L24_FORMATS,
+ },
+ },
};
static int cs47l24_open(struct snd_compr_stream *stream)
if (strcmp(rtd->codec_dai->name, "cs47l24-dsp-voicectrl") == 0) {
n_adsp = 2;
+ } else if (strcmp(rtd->codec_dai->name, "cs47l24-dsp-trace") == 0) {
+ n_adsp = 1;
} else {
dev_err(arizona->dev,
"No suitable compressed stream for DAI '%s'\n",
{
struct cs47l24_priv *priv = data;
struct arizona *arizona = priv->core.arizona;
- int ret;
+ int serviced = 0;
+ int i, ret;
+
+ for (i = 1; i <= 2; ++i) {
+ ret = wm_adsp_compr_handle_irq(&priv->core.adsp[i]);
+ if (ret != -ENODEV)
+ serviced++;
+ }
- ret = wm_adsp_compr_handle_irq(&priv->core.adsp[2]);
- if (ret == -ENODEV) {
+ if (!serviced) {
dev_err(arizona->dev, "Spurious compressed data IRQ\n");
return IRQ_NONE;
}
static struct snd_soc_platform_driver cs47l24_compr_platform = {
.compr_ops = &cs47l24_compr_ops,
};
+
static int cs47l24_probe(struct platform_device *pdev)
{
struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
dev_err(&pdev->dev, "Failed to register platform: %d\n", ret);
return ret;
}
+
ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_cs47l24,
cs47l24_dai, ARRAY_SIZE(cs47l24_dai));
-
if (ret < 0) {
dev_err(&pdev->dev, "Failed to register codec: %d\n", ret);
snd_soc_unregister_platform(&pdev->dev);
static int cs47l24_remove(struct platform_device *pdev)
{
+ struct cs47l24_priv *cs47l24 = platform_get_drvdata(pdev);
+
snd_soc_unregister_platform(&pdev->dev);
snd_soc_unregister_codec(&pdev->dev);
pm_runtime_disable(&pdev->dev);
+ wm_adsp2_remove(&cs47l24->core.adsp[1]);
+ wm_adsp2_remove(&cs47l24->core.adsp[2]);
+
return 0;
}
#define ADSP2_RAM_RDY_SHIFT 0
#define ADSP2_RAM_RDY_WIDTH 1
+#define ADSP_MAX_STD_CTRL_SIZE 512
+
struct wm_adsp_buf {
struct list_head list;
void *buf;
size_t len;
unsigned int set:1;
struct snd_kcontrol *kcontrol;
+ struct soc_bytes_ext bytes_ext;
unsigned int flags;
};
be16_to_cpu(scratch[3]));
}
+static inline struct wm_coeff_ctl *bytes_ext_to_ctl(struct soc_bytes_ext *ext)
+{
+ return container_of(ext, struct wm_coeff_ctl, bytes_ext);
+}
+
static int wm_coeff_info(struct snd_kcontrol *kctl,
struct snd_ctl_elem_info *uinfo)
{
- struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kctl->private_value;
+ struct soc_bytes_ext *bytes_ext =
+ (struct soc_bytes_ext *)kctl->private_value;
+ struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
uinfo->count = ctl->len;
static int wm_coeff_put(struct snd_kcontrol *kctl,
struct snd_ctl_elem_value *ucontrol)
{
- struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kctl->private_value;
+ struct soc_bytes_ext *bytes_ext =
+ (struct soc_bytes_ext *)kctl->private_value;
+ struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
char *p = ucontrol->value.bytes.data;
int ret = 0;
return ret;
}
+static int wm_coeff_tlv_put(struct snd_kcontrol *kctl,
+ const unsigned int __user *bytes, unsigned int size)
+{
+ struct soc_bytes_ext *bytes_ext =
+ (struct soc_bytes_ext *)kctl->private_value;
+ struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
+ int ret = 0;
+
+ mutex_lock(&ctl->dsp->pwr_lock);
+
+ if (copy_from_user(ctl->cache, bytes, size)) {
+ ret = -EFAULT;
+ } else {
+ ctl->set = 1;
+ if (ctl->enabled)
+ ret = wm_coeff_write_control(ctl, ctl->cache, size);
+ }
+
+ mutex_unlock(&ctl->dsp->pwr_lock);
+
+ return ret;
+}
+
static int wm_coeff_read_control(struct wm_coeff_ctl *ctl,
void *buf, size_t len)
{
static int wm_coeff_get(struct snd_kcontrol *kctl,
struct snd_ctl_elem_value *ucontrol)
{
- struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kctl->private_value;
+ struct soc_bytes_ext *bytes_ext =
+ (struct soc_bytes_ext *)kctl->private_value;
+ struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
char *p = ucontrol->value.bytes.data;
int ret = 0;
return ret;
}
+static int wm_coeff_tlv_get(struct snd_kcontrol *kctl,
+ unsigned int __user *bytes, unsigned int size)
+{
+ struct soc_bytes_ext *bytes_ext =
+ (struct soc_bytes_ext *)kctl->private_value;
+ struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
+ int ret = 0;
+
+ mutex_lock(&ctl->dsp->pwr_lock);
+
+ if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) {
+ if (ctl->enabled)
+ ret = wm_coeff_read_control(ctl, ctl->cache, size);
+ else
+ ret = -EPERM;
+ } else {
+ if (!ctl->flags && ctl->enabled)
+ ret = wm_coeff_read_control(ctl, ctl->cache, size);
+ }
+
+ if (!ret && copy_to_user(bytes, ctl->cache, size))
+ ret = -EFAULT;
+
+ mutex_unlock(&ctl->dsp->pwr_lock);
+
+ return ret;
+}
+
struct wmfw_ctl_work {
struct wm_adsp *dsp;
struct wm_coeff_ctl *ctl;
struct work_struct work;
};
+static unsigned int wmfw_convert_flags(unsigned int in, unsigned int len)
+{
+ unsigned int out, rd, wr, vol;
+
+ if (len > ADSP_MAX_STD_CTRL_SIZE) {
+ rd = SNDRV_CTL_ELEM_ACCESS_TLV_READ;
+ wr = SNDRV_CTL_ELEM_ACCESS_TLV_WRITE;
+ vol = SNDRV_CTL_ELEM_ACCESS_VOLATILE;
+
+ out = SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
+ } else {
+ rd = SNDRV_CTL_ELEM_ACCESS_READ;
+ wr = SNDRV_CTL_ELEM_ACCESS_WRITE;
+ vol = SNDRV_CTL_ELEM_ACCESS_VOLATILE;
+
+ out = 0;
+ }
+
+ if (in) {
+ if (in & WMFW_CTL_FLAG_READABLE)
+ out |= rd;
+ if (in & WMFW_CTL_FLAG_WRITEABLE)
+ out |= wr;
+ if (in & WMFW_CTL_FLAG_VOLATILE)
+ out |= vol;
+ } else {
+ out |= rd | wr | vol;
+ }
+
+ return out;
+}
+
static int wmfw_add_ctl(struct wm_adsp *dsp, struct wm_coeff_ctl *ctl)
{
struct snd_kcontrol_new *kcontrol;
kcontrol->info = wm_coeff_info;
kcontrol->get = wm_coeff_get;
kcontrol->put = wm_coeff_put;
- kcontrol->private_value = (unsigned long)ctl;
+ kcontrol->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ kcontrol->tlv.c = snd_soc_bytes_tlv_callback;
+ kcontrol->private_value = (unsigned long)&ctl->bytes_ext;
- if (ctl->flags) {
- if (ctl->flags & WMFW_CTL_FLAG_WRITEABLE)
- kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_WRITE;
- if (ctl->flags & WMFW_CTL_FLAG_READABLE)
- kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_READ;
- if (ctl->flags & WMFW_CTL_FLAG_VOLATILE)
- kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_VOLATILE;
- } else {
- kcontrol->access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
- kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_VOLATILE;
- }
+ ctl->bytes_ext.max = ctl->len;
+ ctl->bytes_ext.get = wm_coeff_tlv_get;
+ ctl->bytes_ext.put = wm_coeff_tlv_put;
+
+ kcontrol->access = wmfw_convert_flags(ctl->flags, ctl->len);
ret = snd_soc_add_card_controls(dsp->card, kcontrol, 1);
if (ret < 0)
kfree(ctl_work);
}
+ static void wm_adsp_free_ctl_blk(struct wm_coeff_ctl *ctl)
+ {
+ kfree(ctl->cache);
+ kfree(ctl->name);
+ kfree(ctl);
+ }
+
static int wm_adsp_create_control(struct wm_adsp *dsp,
const struct wm_adsp_alg_region *alg_region,
unsigned int offset, unsigned int len,
ctl->flags = flags;
ctl->offset = offset;
- if (len > 512) {
- adsp_warn(dsp, "Truncating control %s from %d\n",
- ctl->name, len);
- len = 512;
- }
ctl->len = len;
ctl->cache = kzalloc(ctl->len, GFP_KERNEL);
if (!ctl->cache) {
return alg_region;
}
+ static void wm_adsp_free_alg_regions(struct wm_adsp *dsp)
+ {
+ struct wm_adsp_alg_region *alg_region;
+
+ while (!list_empty(&dsp->alg_regions)) {
+ alg_region = list_first_entry(&dsp->alg_regions,
+ struct wm_adsp_alg_region,
+ list);
+ list_del(&alg_region->list);
+ kfree(alg_region);
+ }
+ }
+
static int wm_adsp1_setup_algs(struct wm_adsp *dsp)
{
struct wmfw_adsp1_id_hdr adsp1_id;
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
struct wm_adsp *dsp = &dsps[w->shift];
- struct wm_adsp_alg_region *alg_region;
struct wm_coeff_ctl *ctl;
int ret;
unsigned int val;
list_for_each_entry(ctl, &dsp->ctl_list, list)
ctl->enabled = 0;
- while (!list_empty(&dsp->alg_regions)) {
- alg_region = list_first_entry(&dsp->alg_regions,
- struct wm_adsp_alg_region,
- list);
- list_del(&alg_region->list);
- kfree(alg_region);
- }
+
+ wm_adsp_free_alg_regions(dsp);
break;
default:
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
struct wm_adsp *dsp = &dsps[w->shift];
- struct wm_adsp_alg_region *alg_region;
struct wm_coeff_ctl *ctl;
int ret;
if (ret != 0)
goto err;
+ mutex_lock(&dsp->pwr_lock);
+
if (wm_adsp_fw[dsp->fw].num_caps != 0)
ret = wm_adsp_buffer_init(dsp);
+ mutex_unlock(&dsp->pwr_lock);
+
break;
case SND_SOC_DAPM_PRE_PMD:
list_for_each_entry(ctl, &dsp->ctl_list, list)
ctl->enabled = 0;
- while (!list_empty(&dsp->alg_regions)) {
- alg_region = list_first_entry(&dsp->alg_regions,
- struct wm_adsp_alg_region,
- list);
- list_del(&alg_region->list);
- kfree(alg_region);
- }
+ wm_adsp_free_alg_regions(dsp);
if (wm_adsp_fw[dsp->fw].num_caps != 0)
wm_adsp_buffer_free(dsp);
}
EXPORT_SYMBOL_GPL(wm_adsp2_init);
+ void wm_adsp2_remove(struct wm_adsp *dsp)
+ {
+ struct wm_coeff_ctl *ctl;
+
+ while (!list_empty(&dsp->ctl_list)) {
+ ctl = list_first_entry(&dsp->ctl_list, struct wm_coeff_ctl,
+ list);
+ list_del(&ctl->list);
+ wm_adsp_free_ctl_blk(ctl);
+ }
+ }
+ EXPORT_SYMBOL_GPL(wm_adsp2_remove);
+
int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream)
{
struct wm_adsp_compr *compr;
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->read_index, write_index, avail * WM_ADSP_DATA_WORD_SIZE);
buf->avail = avail;
return 0;
}
+static int wm_adsp_buffer_get_error(struct wm_adsp_compr_buf *buf)
+{
+ int ret;
+
+ ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(error), &buf->error);
+ if (ret < 0) {
+ adsp_err(buf->dsp, "Failed to check buffer error: %d\n", ret);
+ return ret;
+ }
+ if (buf->error != 0) {
+ adsp_err(buf->dsp, "Buffer error occurred: %d\n", buf->error);
+ return -EIO;
+ }
+
+ return 0;
+}
+
int wm_adsp_compr_handle_irq(struct wm_adsp *dsp)
{
- struct wm_adsp_compr_buf *buf = dsp->buffer;
- struct wm_adsp_compr *compr = dsp->compr;
+ struct wm_adsp_compr_buf *buf;
+ struct wm_adsp_compr *compr;
int ret = 0;
mutex_lock(&dsp->pwr_lock);
+ buf = dsp->buffer;
+ compr = dsp->compr;
+
if (!buf) {
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_get_error(buf);
+ if (ret < 0)
+ goto out_notify; /* Wake poll to report error */
ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(irq_count),
&buf->irq_count);
goto out;
}
+out_notify:
if (compr && compr->stream)
snd_compr_fragment_elapsed(compr->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;
+ struct wm_adsp_compr_buf *buf;
int ret = 0;
adsp_dbg(dsp, "Pointer request\n");
mutex_lock(&dsp->pwr_lock);
+ buf = compr->buf;
+
if (!compr->buf) {
ret = -ENXIO;
goto out;
* DSP to inform us once a whole fragment is available.
*/
if (buf->avail < wm_adsp_compr_frag_words(compr)) {
+ ret = wm_adsp_buffer_get_error(buf);
+ if (ret < 0)
+ goto out;
+
ret = wm_adsp_buffer_reenable_irq(buf);
if (ret < 0) {
adsp_err(dsp,