24576000,
};
----- static const unsigned int arizona_48k_rates[] = {
----- 12000,
----- 24000,
----- 48000,
----- 96000,
----- 192000,
----- 384000,
----- 768000,
----- 4000,
----- 8000,
----- 16000,
----- 32000,
----- 64000,
----- 128000,
----- 256000,
----- 512000,
----- };
-----
----- static const struct snd_pcm_hw_constraint_list arizona_48k_constraint = {
----- .count = ARRAY_SIZE(arizona_48k_rates),
----- .list = arizona_48k_rates,
----- };
-----
static const int arizona_44k1_bclk_rates[] = {
-1,
44100,
22579200,
};
----- static const unsigned int arizona_44k1_rates[] = {
----- 11025,
----- 22050,
----- 44100,
----- 88200,
----- 176400,
----- 352800,
----- 705600,
----- };
-----
----- static const struct snd_pcm_hw_constraint_list arizona_44k1_constraint = {
----- .count = ARRAY_SIZE(arizona_44k1_rates),
----- .list = arizona_44k1_rates,
----- };
-----
----- static int arizona_sr_vals[] = {
+++++ static const unsigned int arizona_sr_vals[] = {
0,
12000,
24000,
512000,
};
+++++ #define ARIZONA_48K_RATE_MASK 0x0F003E
+++++ #define ARIZONA_44K1_RATE_MASK 0x003E00
+++++ #define ARIZONA_RATE_MASK (ARIZONA_48K_RATE_MASK | ARIZONA_44K1_RATE_MASK)
+++++
+++++ static const struct snd_pcm_hw_constraint_list arizona_constraint = {
+++++ .count = ARRAY_SIZE(arizona_sr_vals),
+++++ .list = arizona_sr_vals,
+++++ };
+++++
static int arizona_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
struct arizona_dai_priv *dai_priv = &priv->dai[dai->id - 1];
----- const struct snd_pcm_hw_constraint_list *constraint;
unsigned int base_rate;
+ if (!substream->runtime)
+ return 0;
+
switch (dai_priv->clk) {
case ARIZONA_CLK_SYSCLK:
base_rate = priv->sysclk;
}
if (base_rate == 0)
----- return 0;
-----
----- if (base_rate % 8000)
----- constraint = &arizona_44k1_constraint;
+++++ dai_priv->constraint.mask = ARIZONA_RATE_MASK;
+++++ else if (base_rate % 8000)
+++++ dai_priv->constraint.mask = ARIZONA_44K1_RATE_MASK;
else
----- constraint = &arizona_48k_constraint;
+++++ dai_priv->constraint.mask = ARIZONA_48K_RATE_MASK;
return snd_pcm_hw_constraint_list(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_RATE,
----- constraint);
+++++ &dai_priv->constraint);
}
static void arizona_wm5102_set_dac_comp(struct snd_soc_codec *codec,
bool reconfig;
unsigned int aif_tx_state, aif_rx_state;
- if (params_rate(params) % 8000)
+ if (params_rate(params) % 4000)
rates = &arizona_44k1_bclk_rates[0];
else
rates = &arizona_48k_bclk_rates[0];
struct arizona_dai_priv *dai_priv = &priv->dai[id];
dai_priv->clk = ARIZONA_CLK_SYSCLK;
+++++ dai_priv->constraint = arizona_constraint;
return 0;
}
{ 1000000, 13500000, 0, 1 },
};
+++++static const unsigned int pseudo_fref_max[ARIZONA_FLL_MAX_FRATIO] = {
+++++ 13500000,
+++++ 6144000,
+++++ 6144000,
+++++ 3072000,
+++++ 3072000,
+++++ 2822400,
+++++ 2822400,
+++++ 1536000,
+++++ 1536000,
+++++ 1536000,
+++++ 1536000,
+++++ 1536000,
+++++ 1536000,
+++++ 1536000,
+++++ 1536000,
+++++ 768000,
+++++};
+++++
static struct {
unsigned int min;
unsigned int max;
/* Adjust FRATIO/refdiv to avoid integer mode if possible */
refdiv = cfg->refdiv;
+++++ arizona_fll_dbg(fll, "pseudo: initial ratio=%u fref=%u refdiv=%u\n",
+++++ init_ratio, Fref, refdiv);
+++++
while (div <= ARIZONA_FLL_MAX_REFDIV) {
for (ratio = init_ratio; ratio <= ARIZONA_FLL_MAX_FRATIO;
ratio++) {
if ((ARIZONA_FLL_VCO_CORNER / 2) /
----- (fll->vco_mult * ratio) < Fref)
+++++ (fll->vco_mult * ratio) < Fref) {
+++++ arizona_fll_dbg(fll, "pseudo: hit VCO corner\n");
break;
+++++ }
+++++
+++++ if (Fref > pseudo_fref_max[ratio - 1]) {
+++++ arizona_fll_dbg(fll,
+++++ "pseudo: exceeded max fref(%u) for ratio=%u\n",
+++++ pseudo_fref_max[ratio - 1],
+++++ ratio);
+++++ break;
+++++ }
if (target % (ratio * Fref)) {
cfg->refdiv = refdiv;
cfg->fratio = ratio - 1;
+++++ arizona_fll_dbg(fll,
+++++ "pseudo: found fref=%u refdiv=%d(%d) ratio=%d\n",
+++++ Fref, refdiv, div, ratio);
return ratio;
}
}
if (target % (ratio * Fref)) {
cfg->refdiv = refdiv;
cfg->fratio = ratio - 1;
+++++ arizona_fll_dbg(fll,
+++++ "pseudo: found fref=%u refdiv=%d(%d) ratio=%d\n",
+++++ Fref, refdiv, div, ratio);
return ratio;
}
}
Fref /= 2;
refdiv++;
init_ratio = arizona_find_fratio(Fref, NULL);
+++++ arizona_fll_dbg(fll,
+++++ "pseudo: change fref=%u refdiv=%d(%d) ratio=%u\n",
+++++ Fref, refdiv, div, init_ratio);
}
arizona_fll_warn(fll, "Falling back to integer mode operation\n");
return -EINVAL;
}
----- arizona_fll_dbg(fll, "N=%x THETA=%x LAMBDA=%x\n",
+++++ arizona_fll_dbg(fll, "N=%d THETA=%d LAMBDA=%d\n",
cfg->n, cfg->theta, cfg->lambda);
----- arizona_fll_dbg(fll, "FRATIO=%x(%d) OUTDIV=%x REFCLK_DIV=%x\n",
----- cfg->fratio, cfg->fratio, cfg->outdiv, cfg->refdiv);
----- arizona_fll_dbg(fll, "GAIN=%d\n", cfg->gain);
+++++ arizona_fll_dbg(fll, "FRATIO=0x%x(%d) OUTDIV=%d REFCLK_DIV=0x%x(%d)\n",
+++++ cfg->fratio, ratio, cfg->outdiv,
+++++ cfg->refdiv, 1 << cfg->refdiv);
+++++ arizona_fll_dbg(fll, "GAIN=0x%x(%d)\n", cfg->gain, 1 << cfg->gain);
return 0;
return 0;
}
+++++ static int wm5110_adsp_power_ev(struct snd_soc_dapm_widget *w,
+++++ struct snd_kcontrol *kcontrol, int event)
+++++ {
+++++ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+++++ struct arizona *arizona = dev_get_drvdata(codec->dev->parent);
+++++ unsigned int v;
+++++ int ret;
+++++
+++++ ret = regmap_read(arizona->regmap, ARIZONA_SYSTEM_CLOCK_1, &v);
+++++ if (ret != 0) {
+++++ dev_err(codec->dev, "Failed to read SYSCLK state: %d\n", ret);
+++++ return ret;
+++++ }
+++++
+++++ v = (v & ARIZONA_SYSCLK_FREQ_MASK) >> ARIZONA_SYSCLK_FREQ_SHIFT;
+++++
+++++ return wm_adsp2_early_event(w, kcontrol, event, v);
+++++ }
+++++
static const struct reg_sequence wm5110_no_dre_left_enable[] = {
{ 0x3024, 0xE410 },
{ 0x3025, 0x0056 },
static int wm5110_clear_pga_volume(struct arizona *arizona, int output)
{
- struct reg_sequence clear_pga = {
- ARIZONA_OUTPUT_PATH_CONFIG_1L + output * 4, 0x80
- };
+ unsigned int reg = ARIZONA_OUTPUT_PATH_CONFIG_1L + output * 4;
int ret;
- ret = regmap_multi_reg_write_bypassed(arizona->regmap, &clear_pga, 1);
+ ret = regmap_write(arizona->regmap, reg, 0x80);
if (ret)
dev_err(arizona->dev, "Failed to clear PGA (0x%x): %d\n",
- clear_pga.reg, ret);
+ reg, ret);
return ret;
}
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- struct snd_soc_card *card = dapm->card;
int ret;
/*
* PGA Volume is also used as part of the enable sequence, so
* usage of it should be avoided whilst that is running.
*/
- mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+ snd_soc_dapm_mutex_lock(dapm);
ret = snd_soc_get_volsw_range(kcontrol, ucontrol);
- mutex_unlock(&card->dapm_mutex);
+ snd_soc_dapm_mutex_unlock(dapm);
return ret;
}
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- struct snd_soc_card *card = dapm->card;
int ret;
/*
* PGA Volume is also used as part of the enable sequence, so
* usage of it should be avoided whilst that is running.
*/
- mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+ snd_soc_dapm_mutex_lock(dapm);
ret = snd_soc_put_volsw_range(kcontrol, ucontrol);
- mutex_unlock(&card->dapm_mutex);
+ snd_soc_dapm_mutex_unlock(dapm);
return ret;
}
SND_SOC_DAPM_PGA("ASRC2R", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2R_ENA_SHIFT, 0,
NULL, 0),
----- WM_ADSP2("DSP1", 0),
----- WM_ADSP2("DSP2", 1),
----- WM_ADSP2("DSP3", 2),
----- WM_ADSP2("DSP4", 3),
+++++ WM_ADSP2("DSP1", 0, wm5110_adsp_power_ev),
+++++ WM_ADSP2("DSP2", 1, wm5110_adsp_power_ev),
+++++ WM_ADSP2("DSP3", 2, wm5110_adsp_power_ev),
+++++ WM_ADSP2("DSP4", 3, wm5110_adsp_power_ev),
SND_SOC_DAPM_PGA("ISRC1INT1", ARIZONA_ISRC_1_CTRL_3,
ARIZONA_ISRC1_INT0_ENA_SHIFT, 0, NULL, 0),
{ "Voice Control DSP", NULL, "DSP3" },
{ "Voice Control DSP", NULL, "SYSCLK" },
+++++ { "Audio Trace DSP", NULL, "DSP1" },
+++++ { "Audio Trace DSP", NULL, "SYSCLK" },
+++++
{ "IN1L PGA", NULL, "IN1L" },
{ "IN1R PGA", NULL, "IN1R" },
}
}
----- #define WM5110_RATES SNDRV_PCM_RATE_8000_192000
+++++ #define WM5110_RATES SNDRV_PCM_RATE_KNOT
#define WM5110_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
.formats = WM5110_FORMATS,
},
},
+++++ {
+++++ .name = "wm5110-cpu-trace",
+++++ .capture = {
+++++ .stream_name = "Audio Trace CPU",
+++++ .channels_min = 1,
+++++ .channels_max = 6,
+++++ .rates = WM5110_RATES,
+++++ .formats = WM5110_FORMATS,
+++++ },
+++++ .compress_new = snd_soc_new_compress,
+++++ },
+++++ {
+++++ .name = "wm5110-dsp-trace",
+++++ .capture = {
+++++ .stream_name = "Audio Trace DSP",
+++++ .channels_min = 1,
+++++ .channels_max = 6,
+++++ .rates = WM5110_RATES,
+++++ .formats = WM5110_FORMATS,
+++++ },
+++++ },
};
static int wm5110_open(struct snd_compr_stream *stream)
if (strcmp(rtd->codec_dai->name, "wm5110-dsp-voicectrl") == 0) {
n_adsp = 2;
+++++ } else if (strcmp(rtd->codec_dai->name, "wm5110-dsp-trace") == 0) {
+++++ n_adsp = 0;
} else {
dev_err(arizona->dev,
"No suitable compressed stream for DAI '%s'\n",
return wm_adsp_compr_open(&priv->core.adsp[n_adsp], stream);
}
- --- struct wm5110_priv *florida = data;
- --- int ret;
+ static irqreturn_t wm5110_adsp2_irq(int irq, void *data)
+ {
- --- ret = wm_adsp_compr_handle_irq(&florida->core.adsp[2]);
- --- if (ret == -ENODEV)
+++++ struct wm5110_priv *priv = data;
+++++ struct arizona *arizona = priv->core.arizona;
+++++ int serviced = 0;
+++++ int i, ret;
+
+++++ for (i = 0; i < WM5110_NUM_ADSP; ++i) {
+++++ ret = wm_adsp_compr_handle_irq(&priv->core.adsp[i]);
+++++ if (ret != -ENODEV)
+++++ serviced++;
+++++ }
+++++
+++++ if (!serviced) {
+++++ dev_err(arizona->dev, "Spurious compressed data IRQ\n");
+ return IRQ_NONE;
+++++ }
+
+ return IRQ_HANDLED;
+ }
+
static int wm5110_codec_probe(struct snd_soc_codec *codec)
{
struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
struct wm5110_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct arizona *arizona = priv->core.arizona;
int i, ret;
priv->core.arizona->dapm = dapm;
arizona_init_gpio(codec);
arizona_init_mono(codec);
+ ret = arizona_request_irq(arizona, ARIZONA_IRQ_DSP_IRQ1,
+ "ADSP2 Compressed IRQ", wm5110_adsp2_irq,
+ priv);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to request DSP IRQ: %d\n", ret);
+ return ret;
+ }
+
for (i = 0; i < WM5110_NUM_ADSP; ++i) {
ret = wm_adsp2_codec_probe(&priv->core.adsp[i], codec);
if (ret)
for (--i; i >= 0; --i)
wm_adsp2_codec_remove(&priv->core.adsp[i], codec);
+ arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, priv);
+
return ret;
}
static int wm5110_codec_remove(struct snd_soc_codec *codec)
{
struct wm5110_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct arizona *arizona = priv->core.arizona;
int i;
for (i = 0; i < WM5110_NUM_ADSP; ++i)
priv->core.arizona->dapm = NULL;
+ arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, priv);
+
return 0;
}
.set_params = wm_adsp_compr_set_params,
.get_caps = wm_adsp_compr_get_caps,
.trigger = wm_adsp_compr_trigger,
+ .pointer = wm_adsp_compr_pointer,
+ .copy = wm_adsp_compr_copy,
};
static struct snd_soc_platform_driver wm5110_compr_platform = {
ret = snd_soc_register_platform(&pdev->dev, &wm5110_compr_platform);
if (ret < 0) {
dev_err(&pdev->dev, "Failed to register platform: %d\n", ret);
----- goto error;
+++++ return ret;
}
ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm5110,
snd_soc_unregister_platform(&pdev->dev);
}
----- error:
return ret;
}
static int wm5110_remove(struct platform_device *pdev)
{
+++++ snd_soc_unregister_platform(&pdev->dev);
snd_soc_unregister_codec(&pdev->dev);
pm_runtime_disable(&pdev->dev);
#include <sound/initval.h>
#include <sound/tlv.h>
----- #include <linux/mfd/arizona/registers.h>
-----
----- #include "arizona.h"
#include "wm_adsp.h"
#define adsp_crit(_dsp, fmt, ...) \
struct wm_adsp_buffer_region *regions;
u32 host_buf_ptr;
+
+ u32 error;
+ u32 irq_count;
+ int read_index;
+ int avail;
};
struct wm_adsp_compr {
struct snd_compr_stream *stream;
struct snd_compressed_buffer size;
+
+ u32 *raw_buf;
+ unsigned int copied_total;
++++ +
++++ + unsigned int sample_rate;
};
#define WM_ADSP_DATA_WORD_SIZE 3
unsigned int size_offset;
};
----- static struct wm_adsp_buffer_region_def ez2control_regions[] = {
+++++ static const struct wm_adsp_buffer_region_def default_regions[] = {
{
.mem_type = WMFW_ADSP2_XM,
.base_offset = HOST_BUFFER_FIELD(X_buf_base),
u32 id;
struct snd_codec_desc desc;
int num_regions;
----- struct wm_adsp_buffer_region_def *region_defs;
+++++ const struct wm_adsp_buffer_region_def *region_defs;
};
----- static const struct wm_adsp_fw_caps ez2control_caps[] = {
+++++ static const struct wm_adsp_fw_caps ctrl_caps[] = {
{
.id = SND_AUDIOCODEC_BESPOKE,
.desc = {
.num_sample_rates = 1,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
----- .num_regions = ARRAY_SIZE(ez2control_regions),
----- .region_defs = ez2control_regions,
+++++ .num_regions = ARRAY_SIZE(default_regions),
+++++ .region_defs = default_regions,
+++++ },
+++++ };
+++++
+++++ static const struct wm_adsp_fw_caps trace_caps[] = {
+++++ {
+++++ .id = SND_AUDIOCODEC_BESPOKE,
+++++ .desc = {
+++++ .max_ch = 8,
+++++ .sample_rates = {
+++++ 4000, 8000, 11025, 12000, 16000, 22050,
+++++ 24000, 32000, 44100, 48000, 64000, 88200,
+++++ 96000, 176400, 192000
+++++ },
+++++ .num_sample_rates = 15,
+++++ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+++++ },
+++++ .num_regions = ARRAY_SIZE(default_regions),
+++++ .region_defs = default_regions,
},
};
[WM_ADSP_FW_CTRL] = {
.file = "ctrl",
.compr_direction = SND_COMPRESS_CAPTURE,
----- .num_caps = ARRAY_SIZE(ez2control_caps),
----- .caps = ez2control_caps,
+++++ .num_caps = ARRAY_SIZE(ctrl_caps),
+++++ .caps = ctrl_caps,
},
[WM_ADSP_FW_ASR] = { .file = "asr" },
----- [WM_ADSP_FW_TRACE] = { .file = "trace" },
+++++ [WM_ADSP_FW_TRACE] = {
+++++ .file = "trace",
+++++ .compr_direction = SND_COMPRESS_CAPTURE,
+++++ .num_caps = ARRAY_SIZE(trace_caps),
+++++ .caps = trace_caps,
+++++ },
[WM_ADSP_FW_SPK_PROT] = { .file = "spk-prot" },
[WM_ADSP_FW_MISC] = { .file = "misc" },
};
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
struct wm_adsp *dsp = snd_soc_codec_get_drvdata(codec);
----- ucontrol->value.integer.value[0] = dsp[e->shift_l].fw;
+++++ ucontrol->value.enumerated.item[0] = dsp[e->shift_l].fw;
return 0;
}
struct wm_adsp *dsp = snd_soc_codec_get_drvdata(codec);
int ret = 0;
----- if (ucontrol->value.integer.value[0] == dsp[e->shift_l].fw)
+++++ if (ucontrol->value.enumerated.item[0] == dsp[e->shift_l].fw)
return 0;
----- if (ucontrol->value.integer.value[0] >= WM_ADSP_NUM_FW)
+++++ if (ucontrol->value.enumerated.item[0] >= WM_ADSP_NUM_FW)
return -EINVAL;
mutex_lock(&dsp[e->shift_l].pwr_lock);
if (dsp[e->shift_l].running || dsp[e->shift_l].compr)
ret = -EBUSY;
else
----- dsp[e->shift_l].fw = ucontrol->value.integer.value[0];
+++++ dsp[e->shift_l].fw = ucontrol->value.enumerated.item[0];
mutex_unlock(&dsp[e->shift_l].pwr_lock);
reg = ctl->alg_region.base + ctl->offset;
reg = wm_adsp_region_to_reg(mem, reg);
---- - scratch = kmemdup(buf, ctl->len, GFP_KERNEL | GFP_DMA);
++++ + scratch = kmemdup(buf, len, GFP_KERNEL | GFP_DMA);
if (!scratch)
return -ENOMEM;
ret = regmap_raw_write(dsp->regmap, reg, scratch,
---- - ctl->len);
++++ + len);
if (ret) {
adsp_err(dsp, "Failed to write %zu bytes to %x: %d\n",
---- - ctl->len, reg, ret);
++++ + len, reg, ret);
kfree(scratch);
return ret;
}
---- - adsp_dbg(dsp, "Wrote %zu bytes to %x\n", ctl->len, reg);
++++ + adsp_dbg(dsp, "Wrote %zu bytes to %x\n", len, reg);
kfree(scratch);
reg = ctl->alg_region.base + ctl->offset;
reg = wm_adsp_region_to_reg(mem, reg);
---- - scratch = kmalloc(ctl->len, GFP_KERNEL | GFP_DMA);
++++ + scratch = kmalloc(len, GFP_KERNEL | GFP_DMA);
if (!scratch)
return -ENOMEM;
---- - ret = regmap_raw_read(dsp->regmap, reg, scratch, ctl->len);
++++ + ret = regmap_raw_read(dsp->regmap, reg, scratch, len);
if (ret) {
adsp_err(dsp, "Failed to read %zu bytes from %x: %d\n",
---- - ctl->len, reg, ret);
++++ + len, reg, ret);
kfree(scratch);
return ret;
}
---- - adsp_dbg(dsp, "Read %zu bytes from %x\n", ctl->len, reg);
++++ + adsp_dbg(dsp, "Read %zu bytes from %x\n", len, reg);
---- - memcpy(buf, scratch, ctl->len);
++++ + memcpy(buf, scratch, len);
kfree(scratch);
return 0;
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;
}
---- - ret = snd_soc_add_card_controls(dsp->card,
---- - kcontrol, 1);
++++ + ret = snd_soc_add_card_controls(dsp->card, kcontrol, 1);
if (ret < 0)
goto err_kcontrol;
kfree(kcontrol);
---- - ctl->kcontrol = snd_soc_card_get_kcontrol(dsp->card,
---- - ctl->name);
++++ + ctl->kcontrol = snd_soc_card_get_kcontrol(dsp->card, ctl->name);
return 0;
if (ctl->flags & WMFW_CTL_FLAG_VOLATILE)
continue;
---- - ret = wm_coeff_read_control(ctl,
---- - ctl->cache,
---- - ctl->len);
++++ + ret = wm_coeff_read_control(ctl, ctl->cache, ctl->len);
if (ret < 0)
return ret;
}
if (!ctl->enabled)
continue;
if (ctl->set && !(ctl->flags & WMFW_CTL_FLAG_VOLATILE)) {
---- - ret = wm_coeff_write_control(ctl,
---- - ctl->cache,
---- - ctl->len);
++++ + ret = wm_coeff_write_control(ctl, ctl->cache, ctl->len);
if (ret < 0)
return ret;
}
ret = regmap_raw_read(dsp->regmap, pos, alg, len * 2);
if (ret != 0) {
---- - adsp_err(dsp, "Failed to read algorithm list: %d\n",
---- - ret);
++++ + adsp_err(dsp, "Failed to read algorithm list: %d\n", ret);
kfree(alg);
return ERR_PTR(ret);
}
goto err_mutex;
}
---- - val = (val & dsp->sysclk_mask)
---- - >> dsp->sysclk_shift;
++++ + val = (val & dsp->sysclk_mask) >> dsp->sysclk_shift;
ret = regmap_update_bits(dsp->regmap,
dsp->base + ADSP1_CONTROL_31,
/* Wait for the RAM to start, should be near instantaneous */
for (count = 0; count < 10; ++count) {
---- - ret = regmap_read(dsp->regmap, dsp->base + ADSP2_STATUS1,
---- - &val);
++++ + ret = regmap_read(dsp->regmap, dsp->base + ADSP2_STATUS1, &val);
if (ret != 0)
return ret;
struct wm_adsp,
boot_work);
int ret;
----- unsigned int val;
mutex_lock(&dsp->pwr_lock);
----- /*
----- * For simplicity set the DSP clock rate to be the
----- * SYSCLK rate rather than making it configurable.
----- */
----- ret = regmap_read(dsp->regmap, ARIZONA_SYSTEM_CLOCK_1, &val);
----- if (ret != 0) {
----- adsp_err(dsp, "Failed to read SYSCLK state: %d\n", ret);
----- goto err_mutex;
----- }
----- val = (val & ARIZONA_SYSCLK_FREQ_MASK)
----- >> ARIZONA_SYSCLK_FREQ_SHIFT;
-----
----- ret = regmap_update_bits_async(dsp->regmap,
----- dsp->base + ADSP2_CLOCKING,
----- ADSP2_CLK_SEL_MASK, val);
----- if (ret != 0) {
----- adsp_err(dsp, "Failed to set clock rate: %d\n", ret);
----- goto err_mutex;
----- }
-----
ret = wm_adsp2_ena(dsp);
if (ret != 0)
goto err_mutex;
mutex_unlock(&dsp->pwr_lock);
}
+++++ static void wm_adsp2_set_dspclk(struct wm_adsp *dsp, unsigned int freq)
+++++ {
+++++ int ret;
+++++
+++++ ret = regmap_update_bits_async(dsp->regmap,
+++++ dsp->base + ADSP2_CLOCKING,
+++++ ADSP2_CLK_SEL_MASK,
+++++ freq << ADSP2_CLK_SEL_SHIFT);
+++++ if (ret != 0)
+++++ adsp_err(dsp, "Failed to set clock rate: %d\n", ret);
+++++ }
+++++
int wm_adsp2_early_event(struct snd_soc_dapm_widget *w,
----- struct snd_kcontrol *kcontrol, int event)
+++++ struct snd_kcontrol *kcontrol, int event,
+++++ unsigned int freq)
{
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
+++++ wm_adsp2_set_dspclk(dsp, freq);
queue_work(system_unbound_wq, &dsp->boot_work);
break;
default:
dsp->compr = NULL;
+ kfree(compr->raw_buf);
kfree(compr);
mutex_unlock(&dsp->pwr_lock);
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)
{
struct wm_adsp_compr *compr = stream->runtime->private_data;
+ unsigned int size;
int ret;
ret = wm_adsp_compr_check_params(stream, params);
adsp_dbg(compr->dsp, "fragment_size=%d fragments=%d\n",
compr->size.fragment_size, compr->size.fragments);
+ size = wm_adsp_compr_frag_words(compr) * sizeof(*compr->raw_buf);
+ compr->raw_buf = kmalloc(size, GFP_DMA | GFP_KERNEL);
+ if (!compr->raw_buf)
+ return -ENOMEM;
+
++++ + compr->sample_rate = params->codec.sample_rate;
++++ +
return 0;
}
EXPORT_SYMBOL_GPL(wm_adsp_compr_set_params);
return -ENOMEM;
buf->dsp = dsp;
+ buf->read_index = -1;
+ buf->irq_count = 0xFFFFFFFF;
ret = wm_adsp_buffer_locate(buf);
if (ret < 0) {
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;
}
EXPORT_SYMBOL_GPL(wm_adsp_compr_trigger);
- --- adsp_err(dsp, "Spurious buffer IRQ\n");
+ 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;
+ struct wm_adsp_compr *compr = dsp->compr;
+ int ret = 0;
+
+ mutex_lock(&dsp->pwr_lock);
+
+ if (!buf) {
- -- - if (compr->stream)
+ 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;
+ }
+
++++ + if (compr && compr->stream)
+ snd_compr_fragment_elapsed(compr->stream);
+
+ 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;
++++ + tstamp->sampling_rate = compr->sample_rate;
+
+ out:
+ mutex_unlock(&dsp->pwr_lock);
+
+ return ret;
+ }
+ EXPORT_SYMBOL_GPL(wm_adsp_compr_pointer);
+
+ static int wm_adsp_buffer_capture_block(struct wm_adsp_compr *compr, int target)
+ {
+ struct wm_adsp_compr_buf *buf = compr->buf;
+ u8 *pack_in = (u8 *)compr->raw_buf;
+ u8 *pack_out = (u8 *)compr->raw_buf;
+ unsigned int adsp_addr;
+ int mem_type, nwords, max_read;
+ int i, j, ret;
+
+ /* Calculate read parameters */
+ for (i = 0; i < wm_adsp_fw[buf->dsp->fw].caps->num_regions; ++i)
+ if (buf->read_index < buf->regions[i].cumulative_size)
+ break;
+
+ if (i == wm_adsp_fw[buf->dsp->fw].caps->num_regions)
+ return -EINVAL;
+
+ mem_type = buf->regions[i].mem_type;
+ adsp_addr = buf->regions[i].base_addr +
+ (buf->read_index - buf->regions[i].offset);
+
+ max_read = wm_adsp_compr_frag_words(compr);
+ nwords = buf->regions[i].cumulative_size - buf->read_index;
+
+ if (nwords > target)
+ nwords = target;
+ if (nwords > buf->avail)
+ nwords = buf->avail;
+ if (nwords > max_read)
+ nwords = max_read;
+ if (!nwords)
+ return 0;
+
+ /* Read data from DSP */
+ ret = wm_adsp_read_data_block(buf->dsp, mem_type, adsp_addr,
+ nwords, compr->raw_buf);
+ if (ret < 0)
+ return ret;
+
+ /* Remove the padding bytes from the data read from the DSP */
+ for (i = 0; i < nwords; i++) {
+ for (j = 0; j < WM_ADSP_DATA_WORD_SIZE; j++)
+ *pack_out++ = *pack_in++;
+
+ pack_in += sizeof(*(compr->raw_buf)) - WM_ADSP_DATA_WORD_SIZE;
+ }
+
+ /* update read index to account for words read */
+ buf->read_index += nwords;
+ if (buf->read_index == wm_adsp_buffer_size(buf))
+ buf->read_index = 0;
+
+ ret = wm_adsp_buffer_write(buf, HOST_BUFFER_FIELD(next_read_index),
+ buf->read_index);
+ if (ret < 0)
+ return ret;
+
+ /* update avail to account for words read */
+ buf->avail -= nwords;
+
+ return nwords;
+ }
+
+ static int wm_adsp_compr_read(struct wm_adsp_compr *compr,
+ char __user *buf, size_t count)
+ {
+ struct wm_adsp *dsp = compr->dsp;
+ int ntotal = 0;
+ int nwords, nbytes;
+
+ adsp_dbg(dsp, "Requested read of %zu bytes\n", count);
+
+ if (!compr->buf)
+ return -ENXIO;
+
+ if (compr->buf->error)
+ return -EIO;
+
+ count /= WM_ADSP_DATA_WORD_SIZE;
+
+ do {
+ nwords = wm_adsp_buffer_capture_block(compr, count);
+ if (nwords < 0) {
+ adsp_err(dsp, "Failed to capture block: %d\n", nwords);
+ return nwords;
+ }
+
+ nbytes = nwords * WM_ADSP_DATA_WORD_SIZE;
+
+ adsp_dbg(dsp, "Read %d bytes\n", nbytes);
+
+ if (copy_to_user(buf + ntotal, compr->raw_buf, nbytes)) {
+ adsp_err(dsp, "Failed to copy data to user: %d, %d\n",
+ ntotal, nbytes);
+ return -EFAULT;
+ }
+
+ count -= nwords;
+ ntotal += nbytes;
+ } while (nwords > 0 && count > 0);
+
+ compr->copied_total += ntotal;
+
+ return ntotal;
+ }
+
+ int wm_adsp_compr_copy(struct snd_compr_stream *stream, char __user *buf,
+ size_t count)
+ {
+ struct wm_adsp_compr *compr = stream->runtime->private_data;
+ struct wm_adsp *dsp = compr->dsp;
+ int ret;
+
+ mutex_lock(&dsp->pwr_lock);
+
+ if (stream->direction == SND_COMPRESS_CAPTURE)
+ ret = wm_adsp_compr_read(compr, buf, count);
+ else
+ ret = -ENOTSUPP;
+
+ mutex_unlock(&dsp->pwr_lock);
+
+ return ret;
+ }
+ EXPORT_SYMBOL_GPL(wm_adsp_compr_copy);
+
MODULE_LICENSE("GPL v2");