aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/omap/omap-abe-dsp.c
diff options
context:
space:
mode:
authorIliyan Malchev <malchev@google.com>2011-08-04 16:45:04 -0700
committerIliyan Malchev <malchev@google.com>2011-08-04 16:45:04 -0700
commit8a8d0b78174dc18ec9f4d1743117d933e61e3790 (patch)
tree6078823cb7bbe7327b4e212d986ae578f25b1f00 /sound/soc/omap/omap-abe-dsp.c
parent9d0452856e4f0bf57d3020a632ad5bad819b8071 (diff)
parentc51693927bd88684cc50c1b278bf724aa96139a8 (diff)
downloadkernel_samsung_tuna-8a8d0b78174dc18ec9f4d1743117d933e61e3790.zip
kernel_samsung_tuna-8a8d0b78174dc18ec9f4d1743117d933e61e3790.tar.gz
kernel_samsung_tuna-8a8d0b78174dc18ec9f4d1743117d933e61e3790.tar.bz2
Merge branch 'android-omap-3.0' into android-omap-tuna-3.0
Conflicts: drivers/misc/Kconfig drivers/misc/Makefile sound/soc/omap/abe/abe_port.c Signed-off-by: Iliyan Malchev <malchev@google.com>
Diffstat (limited to 'sound/soc/omap/omap-abe-dsp.c')
-rw-r--r--sound/soc/omap/omap-abe-dsp.c290
1 files changed, 232 insertions, 58 deletions
diff --git a/sound/soc/omap/omap-abe-dsp.c b/sound/soc/omap/omap-abe-dsp.c
index a4c7fc0..0992b3f 100644
--- a/sound/soc/omap/omap-abe-dsp.c
+++ b/sound/soc/omap/omap-abe-dsp.c
@@ -41,6 +41,7 @@
#include <linux/wait.h>
#include <linux/firmware.h>
#include <linux/debugfs.h>
+#include <linux/opp.h>
#include <plat/omap_hwmod.h>
#include <plat/omap_device.h>
@@ -60,9 +61,6 @@
#include "abe/abe_main.h"
#include "abe/port_mgr.h"
-#warning need omap_device_set_rate
-#define omap_device_set_rate(x, y, z)
-
static const char *abe_memory_bank[5] = {
"dmem",
"cmem",
@@ -114,16 +112,20 @@ struct abe_data {
void __iomem *io_base[5];
int irq;
int opp;
+ unsigned long opp_freqs[OMAP_ABE_OPP_COUNT];
int active;
/* coefficients */
struct fw_header hdr;
+ u32 *firmware;
s32 *equ[ABE_MAX_EQU];
int equ_profile[ABE_MAX_EQU];
struct soc_enum equalizer_enum[ABE_MAX_EQU];
struct snd_kcontrol_new equalizer_control[ABE_MAX_EQU];
struct coeff_config *equ_texts;
+ int mono_mix[ABE_NUM_MONO_MIXERS];
+
/* DAPM mixer config - TODO: some of this can be replaced with HAL update */
u32 widget_opp[ABE_NUM_DAPM_REG + 1];
@@ -233,12 +235,21 @@ EXPORT_SYMBOL_GPL(abe_dsp_pm_put);
void abe_dsp_shutdown(void)
{
+ struct omap4_abe_dsp_pdata *pdata = the_abe->abe_pdata;
+ int ret;
+
if (!the_abe->active && !abe_check_activity()) {
abe_set_opp_processing(ABE_OPP25);
the_abe->opp = 25;
abe_stop_event_generator();
udelay(250);
- omap_device_set_rate(the_abe->dev, the_abe->dev, 0);
+ if (pdata && pdata->device_scale) {
+ ret = pdata->device_scale(the_abe->dev, the_abe->dev,
+ the_abe->opp_freqs[0]);
+ if (ret)
+ dev_err(the_abe->dev,
+ "failed to scale to lowest OPP\n");
+ }
}
}
EXPORT_SYMBOL_GPL(abe_dsp_shutdown);
@@ -442,6 +453,55 @@ static int abe_get_mixer(struct snd_kcontrol *kcontrol,
return 0;
}
+static int abe_dsp_set_mono_mixer(int id, int enable)
+{
+ int mixer;
+
+ switch (id) {
+ case MIX_DL1_MONO:
+ mixer = MIXDL1;
+ break;
+ case MIX_DL2_MONO:
+ mixer = MIXDL2;
+ break;
+ case MIX_AUDUL_MONO:
+ mixer = MIXAUDUL;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ pm_runtime_get_sync(the_abe->dev);
+ abe_mono_mixer(mixer, enable);
+ pm_runtime_put_sync(the_abe->dev);
+
+ return 0;
+}
+
+static int abe_put_mono_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ int id = mc->shift - MIX_DL1_MONO;
+
+ the_abe->mono_mix[id] = ucontrol->value.integer.value[0];
+ abe_dsp_set_mono_mixer(mc->shift, the_abe->mono_mix[id]);
+
+ return 1;
+}
+
+static int abe_get_mono_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ int id = mc->shift - MIX_DL1_MONO;
+
+ ucontrol->value.integer.value[0] = the_abe->mono_mix[id];
+ return 0;
+}
+
/* router IDs that match our mixer strings */
static const abe_router_t router[] = {
ZERO_labelID, /* strangely this is not 0 */
@@ -724,38 +784,50 @@ static int volume_get_gain(struct snd_kcontrol *kcontrol,
return 0;
}
+static int abe_dsp_set_equalizer(unsigned int id, unsigned int profile)
+{
+ abe_equ_t equ_params;
+ int len;
+
+ if (id >= the_abe->hdr.num_equ)
+ return -EINVAL;
+
+ if (profile >= the_abe->equ_texts[id].count)
+ return -EINVAL;
+
+ len = the_abe->equ_texts[id].coeff;
+ equ_params.equ_length = len;
+ memcpy(equ_params.coef.type1, the_abe->equ[id] + profile * len,
+ len * sizeof(u32));
+ the_abe->equ_profile[id] = profile;
+
+ pm_runtime_get_sync(the_abe->dev);
+ abe_write_equalizer(id + 1, &equ_params);
+ pm_runtime_put_sync(the_abe->dev);
+
+ return 0;
+}
+
static int abe_get_equalizer(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
-#if defined(CONFIG_SND_OMAP_SOC_ABE_DSP_MODULE)
struct soc_enum *eqc = (struct soc_enum *)kcontrol->private_value;
ucontrol->value.integer.value[0] = the_abe->equ_profile[eqc->reg];
-#endif
return 0;
}
static int abe_put_equalizer(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
-#if defined(CONFIG_SND_OMAP_SOC_ABE_DSP_MODULE)
struct soc_enum *eqc = (struct soc_enum *)kcontrol->private_value;
u16 val = ucontrol->value.enumerated.item[0];
- abe_equ_t equ_params;
- int size;
+ int ret;
- if (val >= the_abe->hdr.num_equ)
- return -EINVAL;
-
- equ_params.equ_length = the_abe->equ_texts[eqc->reg].coeff;
- size = the_abe->equ_texts[eqc->reg].coeff * sizeof(s32);
- memcpy(equ_params.coef.type1, the_abe->equ[eqc->reg] + val * size, size);
- the_abe->equ_profile[eqc->reg] = val;
+ ret = abe_dsp_set_equalizer(eqc->reg, val);
+ if (ret < 0)
+ return ret;
- pm_runtime_get_sync(the_abe->dev);
- abe_write_equalizer(eqc->reg, &equ_params);
- pm_runtime_put_sync(the_abe->dev);
-#endif
return 1;
}
@@ -1000,6 +1072,13 @@ static const struct snd_kcontrol_new abe_controls[] = {
SOC_DOUBLE_EXT_TLV("BT UL Volume",
GAINS_BTUL, GAIN_LEFT_OFFSET, GAIN_RIGHT_OFFSET, 149, 0,
volume_get_gain, volume_put_gain, btul_tlv),
+
+ SOC_SINGLE_EXT("DL1 Mono Mixer", MIXDL1, MIX_DL1_MONO, 1, 0,
+ abe_get_mono_mixer, abe_put_mono_mixer),
+ SOC_SINGLE_EXT("DL2 Mono Mixer", MIXDL2, MIX_DL2_MONO, 1, 0,
+ abe_get_mono_mixer, abe_put_mono_mixer),
+ SOC_SINGLE_EXT("AUDUL Mono Mixer", MIXAUDUL, MIX_AUDUL_MONO, 1, 0,
+ abe_get_mono_mixer, abe_put_mono_mixer),
};
static const struct snd_soc_dapm_widget abe_dapm_widgets[] = {
@@ -1730,7 +1809,8 @@ static const struct snd_pcm_hardware omap_abe_hardware = {
static int abe_set_opp_mode(struct abe_data *abe)
{
- int i, opp = 0;
+ struct omap4_abe_dsp_pdata *pdata = abe->abe_pdata;
+ int i, opp = 0, ret = 0;
/* now calculate OPP level based upon DAPM widget status */
for (i = 0; i < ABE_NUM_WIDGETS; i++) {
@@ -1748,29 +1828,54 @@ static int abe_set_opp_mode(struct abe_data *abe)
case 25:
abe_set_opp_processing(ABE_OPP25);
udelay(250);
- omap_device_set_rate(abe->dev, abe->dev, 49150000);
+ if (pdata && pdata->device_scale) {
+ ret = pdata->device_scale(abe->dev, abe->dev,
+ abe->opp_freqs[OMAP_ABE_OPP25]);
+ if (ret)
+ goto err_scale;
+ }
break;
case 50:
default:
abe_set_opp_processing(ABE_OPP50);
udelay(250);
- omap_device_set_rate(abe->dev, abe->dev, 98300000);
+ if (pdata && pdata->device_scale) {
+ ret = pdata->device_scale(abe->dev, abe->dev,
+ abe->opp_freqs[OMAP_ABE_OPP50]);
+ if (ret)
+ goto err_scale;
+ }
break;
}
} else if (abe->opp < opp) {
/* Increase OPP mode */
switch (opp) {
case 25:
- omap_device_set_rate(abe->dev, abe->dev, 49000000);
+ if (pdata && pdata->device_scale) {
+ pdata->device_scale(abe->dev, abe->dev,
+ abe->opp_freqs[OMAP_ABE_OPP25]);
+ if (ret)
+ goto err_scale;
+ }
abe_set_opp_processing(ABE_OPP25);
break;
case 50:
- omap_device_set_rate(abe->dev, abe->dev, 98300000);
+ if (pdata && pdata->device_scale) {
+ ret = pdata->device_scale(abe->dev, abe->dev,
+ abe->opp_freqs[OMAP_ABE_OPP50]);
+ if (ret)
+ goto err_scale;
+ }
abe_set_opp_processing(ABE_OPP50);
break;
case 100:
default:
- omap_device_set_rate(abe->dev, abe->dev, 196600000);
+ if (pdata && pdata->device_scale) {
+ ret = pdata->device_scale(abe->dev, abe->dev,
+ abe->opp_freqs[OMAP_ABE_OPP100]);
+ if (ret)
+ goto err_scale;
+ }
abe_set_opp_processing(ABE_OPP100);
break;
}
@@ -1779,6 +1884,10 @@ static int abe_set_opp_mode(struct abe_data *abe)
dev_dbg(abe->dev, "new OPP level is %d\n", opp);
return 0;
+
+err_scale:
+ dev_err(abe->dev, "failed to scale to OPP%d\n", opp);
+ return ret;
}
static int aess_set_runtime_opp_level(struct abe_data *abe)
@@ -1838,15 +1947,22 @@ static int aess_save_context(struct abe_data *abe)
static int aess_restore_context(struct abe_data *abe)
{
struct omap4_abe_dsp_pdata *pdata = abe->abe_pdata;
- int loss_count = 0;
-
- omap_device_set_rate(&abe->dev, &abe->dev, 98000000);
+ int i, loss_count = 0, ret;
+
+ if (pdata && pdata->device_scale) {
+ ret = pdata->device_scale(the_abe->dev, the_abe->dev,
+ abe->opp_freqs[OMAP_ABE_OPP50]);
+ if (ret) {
+ dev_err(abe->dev, "failed to scale to OPP50\n");
+ return ret;
+ }
+ }
if (pdata->get_context_loss_count)
loss_count = pdata->get_context_loss_count(abe->dev);
if (loss_count != the_abe->loss_count)
- abe_reload_fw();
+ abe_reload_fw(abe->firmware);
/* TODO: Find a better way to save/retore gains after dor OFF mode */
abe_unmute_gain(MIXSDT, MIX_SDT_INPUT_UP_MIXER);
@@ -1880,6 +1996,12 @@ static int aess_restore_context(struct abe_data *abe)
abe_set_router_configuration(UPROUTE, 0, (u32 *)abe->router);
+ for (i = 0; i < abe->hdr.num_equ; i++)
+ abe_dsp_set_equalizer(i, abe->equ_profile[i]);
+
+ for (i = 0; i < ABE_NUM_MONO_MIXERS; i++)
+ abe_dsp_set_mono_mixer(MIX_DL1_MONO + i, abe->mono_mix[i]);
+
return 0;
}
@@ -2079,7 +2201,6 @@ static int abe_add_widgets(struct snd_soc_platform *platform)
struct fw_header *hdr = &abe->hdr;
int i, j;
-#if defined(CONFIG_SND_OMAP_SOC_ABE_DSP_MODULE)
/* create equalizer controls */
for (i = 0; i < hdr->num_equ; i++) {
struct soc_enum *equalizer_enum = &abe->equalizer_enum[i];
@@ -2107,7 +2228,6 @@ static int abe_add_widgets(struct snd_soc_platform *platform)
snd_soc_add_platform_controls(platform, abe->equalizer_control,
hdr->num_equ);
-#endif
snd_soc_add_platform_controls(platform, abe_controls,
ARRAY_SIZE(abe_controls));
@@ -2180,7 +2300,7 @@ static int abe_resume(struct snd_soc_dai *dai)
{
struct abe_data *abe = the_abe;
struct omap4_abe_dsp_pdata *pdata = abe->abe_pdata;
- int loss_count = 0, ret = 0;
+ int i, loss_count = 0, ret = 0;
dev_dbg(dai->dev, "%s: %s active %d\n",
__func__, dai->name, dai->active);
@@ -2192,10 +2312,18 @@ static int abe_resume(struct snd_soc_dai *dai)
loss_count = pdata->get_context_loss_count(abe->dev);
pm_runtime_get_sync(abe->dev);
- omap_device_set_rate(&abe->dev, &abe->dev, 98000000);
+
+ if (pdata && pdata->device_scale) {
+ ret = pdata->device_scale(abe->dev, abe->dev,
+ abe->opp_freqs[OMAP_ABE_OPP50]);
+ if (ret) {
+ dev_err(abe->dev, "failed to scale to OPP50\n");
+ goto out;
+ }
+ }
if (loss_count != abe->loss_count)
- abe_reload_fw();
+ abe_reload_fw(abe->firmware);
switch (dai->id) {
case OMAP_ABE_DAI_PDM_UL:
@@ -2230,6 +2358,9 @@ static int abe_resume(struct snd_soc_dai *dai)
abe_set_router_configuration(UPROUTE, 0, (u32 *)abe->router);
+ for (i = 0; i < abe->hdr.num_equ; i++)
+ abe_dsp_set_equalizer(i, abe->equ_profile[i]);
+
out:
pm_runtime_put_sync(abe->dev);
return ret;
@@ -2242,11 +2373,13 @@ out:
static int abe_probe(struct snd_soc_platform *platform)
{
struct abe_data *abe = snd_soc_platform_get_drvdata(platform);
+ struct opp *opp;
+ const u8 *fw_data;
+ unsigned long freq = ULONG_MAX;
+ int ret = 0, i, opp_count, offset = 0;
+#if defined(CONFIG_SND_OMAP_SOC_ABE_DSP_MODULE)
const struct firmware *fw;
-#ifndef CONFIG_PM_RUNTIME
- struct omap4_abe_dsp_pdata *pdata = priv->abe_pdata;
#endif
- int ret = 0, i, offset = 0;
abe->platform = platform;
@@ -2260,14 +2393,18 @@ static int abe_probe(struct snd_soc_platform *platform)
dev_err(abe->dev, "Failed to load firmware: %d\n", ret);
return ret;
}
+ fw_data = fw->data;
+#else
+ fw_data = (u8 *)abe_get_default_fw();
+#endif
/* get firmware and coefficients header info */
- memcpy(&abe->hdr, fw->data, sizeof(struct fw_header));
+ memcpy(&abe->hdr, fw_data, sizeof(struct fw_header));
if (abe->hdr.firmware_size > ABE_MAX_FW_SIZE) {
- dev_err(abe->dev, "Firmware too large at %d bytes: %d\n",
+ dev_err(abe->dev, "Firmware too large at %d bytes: %d\n",
abe->hdr.firmware_size, ret);
- ret = -EINVAL;
- goto err_fw;
+ ret = -EINVAL;
+ goto err_fw;
}
dev_dbg(abe->dev, "ABE firmware size %d bytes\n", abe->hdr.firmware_size);
@@ -2292,7 +2429,7 @@ static int abe_probe(struct snd_soc_platform *platform)
goto err_fw;
}
offset = sizeof(struct fw_header);
- memcpy(abe->equ_texts, fw->data + offset,
+ memcpy(abe->equ_texts, fw_data + offset,
abe->hdr.num_equ * sizeof(struct coeff_config));
/* get coefficients from firmware */
@@ -2302,7 +2439,7 @@ static int abe_probe(struct snd_soc_platform *platform)
goto err_equ;
}
offset += abe->hdr.num_equ * sizeof(struct coeff_config);
- memcpy(abe->equ[0], fw->data + offset, abe->hdr.coeff_size);
+ memcpy(abe->equ[0], fw_data + offset, abe->hdr.coeff_size);
/* allocate coefficient mixer texts */
dev_dbg(abe->dev, "loaded %d equalizers\n", abe->hdr.num_equ);
@@ -2326,16 +2463,55 @@ static int abe_probe(struct snd_soc_platform *platform)
/* initialise coefficient equalizers */
for (i = 1; i < abe->hdr.num_equ; i++) {
abe->equ[i] = abe->equ[i - 1] +
- abe->equ_texts[i - 1].count * abe->equ_texts[i - 1].coeff * sizeof(s32);
+ abe->equ_texts[i - 1].count * abe->equ_texts[i - 1].coeff;
}
-#endif
+
+ /* store ABE firmware for later context restore */
+ abe->firmware = kzalloc(abe->hdr.firmware_size, GFP_KERNEL);
+ if (abe->firmware == NULL) {
+ ret = -ENOMEM;
+ goto err_texts;
+ }
+
+ memcpy(abe->firmware,
+ fw_data + sizeof(struct fw_header) + abe->hdr.coeff_size,
+ abe->hdr.firmware_size);
+
ret = request_irq(abe->irq, abe_irq_handler, 0, "ABE", (void *)abe);
if (ret) {
dev_err(platform->dev, "request for ABE IRQ %d failed %d\n",
abe->irq, ret);
- goto err_texts;
+ goto err_irq;
+ }
+
+ /* query supported opps */
+ rcu_read_lock();
+ opp_count = opp_get_opp_count(abe->dev);
+ if (opp_count <= 0) {
+ dev_err(abe->dev, "invalid OPP data\n");
+ ret = opp_count;
+ goto err_opp;
+ } else if (opp_count > OMAP_ABE_OPP_COUNT) {
+ dev_err(abe->dev, "unsupported OPP count %d (max:%d)\n",
+ opp_count, OMAP_ABE_OPP_COUNT);
+ ret = -EINVAL;
+ goto err_opp;
}
+ /* assume provided opps are always higher */
+ for (i = OMAP_ABE_OPP_COUNT - 1; i >= 0; i--) {
+ opp = opp_find_freq_floor(abe->dev, &freq);
+ if (IS_ERR_OR_NULL(opp))
+ break;
+ abe->opp_freqs[i] = freq;
+ /* prepare to obtain next available opp */
+ freq--;
+ }
+ /* use lowest available opp for non-populated items */
+ for (freq++; i >= 0; i--)
+ abe->opp_freqs[i] = freq;
+ rcu_read_unlock();
+
/* aess_clk has to be enabled to access hal register.
* Disable the clk after it has been used.
*/
@@ -2345,14 +2521,7 @@ static int abe_probe(struct snd_soc_platform *platform)
abe_reset_hal();
-#if 0
-#warning fixup load fw args
- //abe_load_fw(fw->data + sizeof(struct fw_header) + abe->hdr.coeff_size);
-#else
- abe_load_fw();
-#endif
- /* Config OPP 100 for now */
- abe_set_opp_processing(ABE_OPP100);
+ abe_load_fw(abe->firmware);
/* "tick" of the audio engine */
abe_write_event_generator(EVENT_TIMER);
@@ -2368,14 +2537,19 @@ static int abe_probe(struct snd_soc_platform *platform)
#endif
return ret;
+err_opp:
+ rcu_read_unlock();
+ free_irq(abe->irq, (void *)abe);
+err_irq:
+ kfree(abe->firmware);
err_texts:
-#if defined(CONFIG_SND_OMAP_SOC_ABE_DSP_MODULE)
for (i = 0; i < abe->hdr.num_equ; i++)
kfree(abe->equalizer_enum[i].texts);
kfree(abe->equ[0]);
err_equ:
kfree(abe->equ_texts);
err_fw:
+#if defined(CONFIG_SND_OMAP_SOC_ABE_DSP_MODULE)
release_firmware(fw);
#endif
return ret;
@@ -2388,13 +2562,13 @@ static int abe_remove(struct snd_soc_platform *platform)
free_irq(abe->irq, (void *)abe);
-#if defined(CONFIG_SND_OMAP_SOC_ABE_DSP_MODULE)
for (i = 0; i < abe->hdr.num_equ; i++)
kfree(abe->equalizer_enum[i].texts);
kfree(abe->equ[0]);
kfree(abe->equ_texts);
-#endif
+ kfree(abe->firmware);
+
pm_runtime_disable(abe->dev);
return 0;