You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
kernel_samsung_sm7125/sound/soc/codecs/cs47l96-ao.c

1117 lines
35 KiB

/*
* cs47l96-ao.c -- ALSA SoC AO Audio driver for CS47L96/CS47L97 codecs
*
* Copyright 2018 Cirrus Logic, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/completion.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/device.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/tlv.h>
#include <linux/irqchip/irq-tacna.h>
#include <linux/mfd/tacna/core.h>
#include <linux/mfd/tacna/registers.h>
#include "tacna.h"
#include "wm_adsp.h"
#define CS47L96_AO_NUM_DSP 1
#define CS47L96_AO_DSP_N_RX_CHANNELS 8
#define CS47L96_AO_DSP_N_TX_CHANNELS 8
#define TACNA_NUM_MIXER_AO_INPUTS 30
#define TACNA_AO_MUX_ENUM_DECL(name, reg) \
SOC_VALUE_ENUM_SINGLE_AUTODISABLE_DECL( \
name, reg, 0, TACNA_MIXER_SRC_MASK, \
cs47l96_ao_mixer_texts, cs47l96_ao_mixer_values)
#define TACNA_AO_MUX_ENUMS(name, base_reg) \
static TACNA_AO_MUX_ENUM_DECL(name##_enum, base_reg); \
static TACNA_MUX_CTL_DECL(name)
#define TACNA_DSPAO_ROUTES_1_8(name) \
{ name, NULL, name " Preloader" }, \
{ name " Preloader", NULL, "SYSCLKAO" }, \
{ name " Preload", NULL, name " Preloader" }, \
TACNA_MUX_ROUTES(name, name "RX1"), \
TACNA_MUX_ROUTES(name, name "RX2"), \
TACNA_MUX_ROUTES(name, name "RX3"), \
TACNA_MUX_ROUTES(name, name "RX4"), \
TACNA_MUX_ROUTES(name, name "RX5"), \
TACNA_MUX_ROUTES(name, name "RX6"), \
TACNA_MUX_ROUTES(name, name "RX7"), \
TACNA_MUX_ROUTES(name, name "RX8") \
struct cs47l96_ao {
struct tacna_priv core;
struct tacna_fll fll;
};
static const struct wm_adsp_region cs47l96_dsp1ao_regions[] = {
{ .type = WMFW_HALO_PM_PACKED, .base = 0x5800000 },
{ .type = WMFW_HALO_XM_PACKED, .base = 0x4000000 },
{ .type = WMFW_ADSP2_XM, .base = 0x4800000 },
{ .type = WMFW_HALO_YM_PACKED, .base = 0x4C00000 },
{ .type = WMFW_ADSP2_YM, .base = 0x5400000 },
};
static const struct soc_enum cs47l96_ao_sample_rate[] = {
SOC_VALUE_ENUM_SINGLE(TACNA_AO_SAMPLE_RATE1,
TACNA_AO_SAMPLE_RATE_1_SHIFT,
TACNA_AO_SAMPLE_RATE_1_MASK >>
TACNA_AO_SAMPLE_RATE_1_SHIFT,
TACNA_SAMPLE_RATE_ENUM_SIZE,
tacna_sample_rate_text,
tacna_sample_rate_val),
SOC_VALUE_ENUM_SINGLE(TACNA_AO_SAMPLE_RATE2,
TACNA_AO_SAMPLE_RATE_2_SHIFT,
TACNA_AO_SAMPLE_RATE_2_MASK >>
TACNA_AO_SAMPLE_RATE_2_SHIFT,
TACNA_SAMPLE_RATE_ENUM_SIZE,
tacna_sample_rate_text,
tacna_sample_rate_val),
SOC_VALUE_ENUM_SINGLE(TACNA_AO_SAMPLE_RATE3,
TACNA_AO_SAMPLE_RATE_3_SHIFT,
TACNA_AO_SAMPLE_RATE_3_MASK >>
TACNA_AO_SAMPLE_RATE_3_SHIFT,
TACNA_SAMPLE_RATE_ENUM_SIZE,
tacna_sample_rate_text,
tacna_sample_rate_val),
};
static int cs47l96_ao_in_rate_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned int reg, shift;
int ret = 0;
snd_soc_dapm_mutex_lock(dapm);
/* Cannot change rate on an active input */
reg = snd_soc_read(codec, TACNA_AO_INPUT_CONTROL);
shift = (e->reg - TACNA_IN5LAO_CONTROL1) / 0x20;
shift ^= 0x1; /* Flip bottom bit for channel order */
if ((reg) & (1 << shift)) {
ret = -EBUSY;
goto exit;
}
ret = snd_soc_put_enum_double(kcontrol, ucontrol);
exit:
snd_soc_dapm_mutex_unlock(dapm);
return ret;
}
static SOC_ENUM_SINGLE_DECL(cs47l96_ao_in_vd_ramp,
TACNA_AO_INPUT_VOL_CONTROL,
TACNA_AO_IN_VD_RAMP_SHIFT,
tacna_vol_ramp_text);
static SOC_ENUM_SINGLE_DECL(cs47l96_ao_in_vi_ramp,
TACNA_AO_INPUT_VOL_CONTROL,
TACNA_AO_IN_VI_RAMP_SHIFT,
tacna_vol_ramp_text);
static SOC_ENUM_SINGLE_DECL(cs47l96_ao_in_hpf_cut_enum,
TACNA_AO_INPUT_HPF_CONTROL,
TACNA_AO_IN_HPF_CUT_SHIFT,
tacna_in_hpf_cut_text);
static const struct soc_enum cs47l96_ao_in_dmic_osr[] = {
SOC_ENUM_SINGLE(TACNA_INPUT5AO_CONTROL1,
TACNA_IN5AO_OSR_SHIFT,
TACNA_OSR_ENUM_SIZE,
tacna_in_dmic_osr_text),
SOC_ENUM_SINGLE(TACNA_INPUT6AO_CONTROL1,
TACNA_IN6AO_OSR_SHIFT,
TACNA_OSR_ENUM_SIZE,
tacna_in_dmic_osr_text),
SOC_ENUM_SINGLE(TACNA_INPUT7AO_CONTROL1,
TACNA_IN7AO_OSR_SHIFT,
TACNA_OSR_ENUM_SIZE,
tacna_in_dmic_osr_text),
};
static const struct soc_enum cs47l96_ao_input_rate[] = {
SOC_VALUE_ENUM_SINGLE(TACNA_IN5LAO_CONTROL1,
TACNA_IN5LAO_RATE_SHIFT,
TACNA_IN5LAO_RATE_MASK >> TACNA_IN5LAO_RATE_SHIFT,
TACNA_AO_RATE_ENUM_SIZE,
tacna_ao_rate_text,
tacna_ao_rate_val),
SOC_VALUE_ENUM_SINGLE(TACNA_IN5RAO_CONTROL1,
TACNA_IN5RAO_RATE_SHIFT,
TACNA_IN5RAO_RATE_MASK >> TACNA_IN5RAO_RATE_SHIFT,
TACNA_AO_RATE_ENUM_SIZE,
tacna_ao_rate_text,
tacna_ao_rate_val),
SOC_VALUE_ENUM_SINGLE(TACNA_IN6LAO_CONTROL1,
TACNA_IN6LAO_RATE_SHIFT,
TACNA_IN6LAO_RATE_MASK >> TACNA_IN6LAO_RATE_SHIFT,
TACNA_AO_RATE_ENUM_SIZE,
tacna_ao_rate_text,
tacna_ao_rate_val),
SOC_VALUE_ENUM_SINGLE(TACNA_IN6RAO_CONTROL1,
TACNA_IN6RAO_RATE_SHIFT,
TACNA_IN6RAO_RATE_MASK >> TACNA_IN6RAO_RATE_SHIFT,
TACNA_AO_RATE_ENUM_SIZE,
tacna_ao_rate_text,
tacna_ao_rate_val),
SOC_VALUE_ENUM_SINGLE(TACNA_IN7LAO_CONTROL1,
TACNA_IN7LAO_RATE_SHIFT,
TACNA_IN7LAO_RATE_MASK >> TACNA_IN7LAO_RATE_SHIFT,
TACNA_AO_RATE_ENUM_SIZE,
tacna_ao_rate_text,
tacna_ao_rate_val),
SOC_VALUE_ENUM_SINGLE(TACNA_IN7RAO_CONTROL1,
TACNA_IN7RAO_RATE_SHIFT,
TACNA_IN7RAO_RATE_MASK >> TACNA_IN7RAO_RATE_SHIFT,
TACNA_AO_RATE_ENUM_SIZE,
tacna_ao_rate_text,
tacna_ao_rate_val),
};
static const struct snd_kcontrol_new cs47l96_ao_snd_controls[] = {
SOC_ENUM("IN5AO OSR", cs47l96_ao_in_dmic_osr[0]),
SOC_ENUM("IN6AO OSR", cs47l96_ao_in_dmic_osr[1]),
SOC_ENUM("IN7AO OSR", cs47l96_ao_in_dmic_osr[2]),
SOC_ENUM("AO IN HPF Cutoff Frequency", cs47l96_ao_in_hpf_cut_enum),
SOC_SINGLE("IN5LAO HPF Switch", TACNA_IN5LAO_CONTROL1, TACNA_IN5LAO_HPF_SHIFT,
1, 0),
SOC_SINGLE("IN5RAO HPF Switch", TACNA_IN5RAO_CONTROL1, TACNA_IN5RAO_HPF_SHIFT,
1, 0),
SOC_SINGLE("IN6LAO HPF Switch", TACNA_IN6LAO_CONTROL1, TACNA_IN6LAO_HPF_SHIFT,
1, 0),
SOC_SINGLE("IN6RAO HPF Switch", TACNA_IN6RAO_CONTROL1, TACNA_IN6RAO_HPF_SHIFT,
1, 0),
SOC_SINGLE("IN7LAO HPF Switch", TACNA_IN7LAO_CONTROL1, TACNA_IN7LAO_HPF_SHIFT,
1, 0),
SOC_SINGLE("IN7RAO HPF Switch", TACNA_IN7RAO_CONTROL1, TACNA_IN7RAO_HPF_SHIFT,
1, 0),
SOC_SINGLE_EXT_TLV("IN5LAO Digital Volume", TACNA_IN5L_CONTROL2,
TACNA_IN5LAO_VOL_SHIFT, 0xbf, 0,
snd_soc_get_volsw, tacna_in_put_volsw,
tacna_digital_tlv),
SOC_SINGLE_EXT_TLV("IN5RAO Digital Volume", TACNA_IN5R_CONTROL2,
TACNA_IN5RAO_VOL_SHIFT, 0xbf, 0,
snd_soc_get_volsw, tacna_in_put_volsw,
tacna_digital_tlv),
SOC_SINGLE_EXT_TLV("IN6LAO Digital Volume", TACNA_IN6L_CONTROL2,
TACNA_IN6LAO_VOL_SHIFT, 0xbf, 0,
snd_soc_get_volsw, tacna_in_put_volsw,
tacna_digital_tlv),
SOC_SINGLE_EXT_TLV("IN6RAO Digital Volume", TACNA_IN6R_CONTROL2,
TACNA_IN6RAO_VOL_SHIFT, 0xbf, 0,
snd_soc_get_volsw, tacna_in_put_volsw,
tacna_digital_tlv),
SOC_SINGLE_EXT_TLV("IN7LAO Digital Volume", TACNA_IN7L_CONTROL2,
TACNA_IN7LAO_VOL_SHIFT, 0xbf, 0,
snd_soc_get_volsw, tacna_in_put_volsw,
tacna_digital_tlv),
SOC_SINGLE_EXT_TLV("IN7RAO Digital Volume", TACNA_IN7R_CONTROL2,
TACNA_IN7RAO_VOL_SHIFT, 0xbf, 0,
snd_soc_get_volsw, tacna_in_put_volsw,
tacna_digital_tlv),
SOC_ENUM("AO Input Ramp Up", cs47l96_ao_in_vi_ramp),
SOC_ENUM("AO Input Ramp Down", cs47l96_ao_in_vd_ramp),
SOC_ENUM("AO Sample Rate 1", cs47l96_ao_sample_rate[0]),
SOC_ENUM("AO Sample Rate 2", cs47l96_ao_sample_rate[1]),
SOC_ENUM("AO Sample Rate 3", cs47l96_ao_sample_rate[2]),
SOC_ENUM_EXT("IN5LAO Rate", cs47l96_ao_input_rate[0],
snd_soc_get_enum_double, cs47l96_ao_in_rate_put),
SOC_ENUM_EXT("IN5RAO Rate", cs47l96_ao_input_rate[1],
snd_soc_get_enum_double, cs47l96_ao_in_rate_put),
SOC_ENUM_EXT("IN6LAO Rate", cs47l96_ao_input_rate[2],
snd_soc_get_enum_double, cs47l96_ao_in_rate_put),
SOC_ENUM_EXT("IN6RAO Rate", cs47l96_ao_input_rate[3],
snd_soc_get_enum_double, cs47l96_ao_in_rate_put),
SOC_ENUM_EXT("IN7LAO Rate", cs47l96_ao_input_rate[4],
snd_soc_get_enum_double, cs47l96_ao_in_rate_put),
SOC_ENUM_EXT("IN7RAO Rate", cs47l96_ao_input_rate[5],
snd_soc_get_enum_double, cs47l96_ao_in_rate_put),
WM_ADSP2_PRELOAD_SWITCH("DSP1AO", 1),
};
static const char * const cs47l96_ao_mixer_texts[] = {
"None",
"ASP1AORX1",
"ASP1AORX2",
"ASP1AORX3",
"ASP1AORX4",
"DSP1AO.1",
"DSP1AO.2",
"DSP1AO.3",
"DSP1AO.4",
"DSP1AO.5",
"DSP1AO.6",
"DSP1AO.7",
"DSP1AO.8",
"Ultrasonic 1",
"Ultrasonic 2",
"Ultrasonic 3",
"IN5LAO",
"IN5RAO",
"IN6LAO",
"IN6RAO",
"IN7LAO",
"IN7RAO",
"AOBRIDGE2IN1",
"AOBRIDGE2IN2",
"AOBRIDGE2IN3",
"AOBRIDGE2IN4",
"AOBRIDGE2IN5",
"AOBRIDGE2IN6",
"AOBRIDGE2IN7",
"AOBRIDGE2IN8",
};
static unsigned int cs47l96_ao_mixer_values[] = {
0x000, /* Silence (mute) */
0x001, /* ASP_AO1 RX1 */
0x002, /* ASP_AO1 RX2 */
0x003, /* ASP_AO1 RX3 */
0x004, /* ASP_AO1 RX4 */
0x005, /* DSP1AO channel 1 */
0x006, /* DSP1AO channel 2 */
0x007, /* DSP1AO channel 3 */
0x008, /* DSP1AO channel 4 */
0x009, /* DSP1AO channel 5 */
0x00A, /* DSP1AO channel 6 */
0x00B, /* DSP1AO channel 7 */
0x00C, /* DSP1AO channel 8 */
0x00D, /* Ultrasonic 1 */
0x00E, /* Ultrasonic 2 */
0x00F, /* Ultrasonic 3 */
0x010, /* IN5LAO signal path */
0x011, /* IN5RAO signal path */
0x012, /* IN6LAO signal path */
0x013, /* IN6RAO signal path */
0x014, /* IN7LAO signal path */
0x015, /* IN7RAO signal path */
0x016, /* AO Bridge 2 channel 1 */
0x017, /* AO Bridge 2 channel 2 */
0x018, /* AO Bridge 2 channel 3 */
0x019, /* AO Bridge 2 channel 4 */
0x01A, /* AO Bridge 2 channel 5 */
0x01B, /* AO Bridge 2 channel 6 */
0x01C, /* AO Bridge 2 channel 7 */
0x01D, /* AO Bridge 2 channel 8 */
};
TACNA_AO_MUX_ENUMS(ASP1AOTX1, TACNA_ASP1AOTX1_INPUT1);
TACNA_AO_MUX_ENUMS(ASP1AOTX2, TACNA_ASP1AOTX2_INPUT1);
TACNA_AO_MUX_ENUMS(ASP1AOTX3, TACNA_ASP1AOTX3_INPUT1);
TACNA_AO_MUX_ENUMS(ASP1AOTX4, TACNA_ASP1AOTX4_INPUT1);
TACNA_AO_MUX_ENUMS(DSP1AORX1, TACNA_DSP1AORX1_INPUT1);
TACNA_AO_MUX_ENUMS(DSP1AORX2, TACNA_DSP1AORX2_INPUT1);
TACNA_AO_MUX_ENUMS(DSP1AORX3, TACNA_DSP1AORX3_INPUT1);
TACNA_AO_MUX_ENUMS(DSP1AORX4, TACNA_DSP1AORX4_INPUT1);
TACNA_AO_MUX_ENUMS(DSP1AORX5, TACNA_DSP1AORX5_INPUT1);
TACNA_AO_MUX_ENUMS(DSP1AORX6, TACNA_DSP1AORX6_INPUT1);
TACNA_AO_MUX_ENUMS(DSP1AORX7, TACNA_DSP1AORX7_INPUT1);
TACNA_AO_MUX_ENUMS(DSP1AORX8, TACNA_DSP1AORX8_INPUT1);
TACNA_AO_MUX_ENUMS(AOBRIDGE1IN1, TACNA_AOBRIDGE1_IN1_INPUT1);
TACNA_AO_MUX_ENUMS(AOBRIDGE1IN2, TACNA_AOBRIDGE1_IN2_INPUT1);
TACNA_AO_MUX_ENUMS(AOBRIDGE1IN3, TACNA_AOBRIDGE1_IN3_INPUT1);
TACNA_AO_MUX_ENUMS(AOBRIDGE1IN4, TACNA_AOBRIDGE1_IN4_INPUT1);
TACNA_AO_MUX_ENUMS(AOBRIDGE1IN5, TACNA_AOBRIDGE1_IN5_INPUT1);
TACNA_AO_MUX_ENUMS(AOBRIDGE1IN6, TACNA_AOBRIDGE1_IN6_INPUT1);
TACNA_AO_MUX_ENUMS(AOBRIDGE1IN7, TACNA_AOBRIDGE1_IN7_INPUT1);
TACNA_AO_MUX_ENUMS(AOBRIDGE1IN8, TACNA_AOBRIDGE1_IN8_INPUT1);
static int cs47l96_ao_in_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 tacna_priv *priv = snd_soc_codec_get_drvdata(codec);
unsigned int reg;
if (w->shift % 2)
reg = TACNA_IN1LAO_CONTROL2 + ((w->shift / 2) * 0x40);
else
reg = TACNA_IN1RAO_CONTROL2 + ((w->shift / 2) * 0x40);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
priv->in_up_pending++;
break;
case SND_SOC_DAPM_POST_PMU:
priv->in_up_pending--;
snd_soc_update_bits(codec, reg, TACNA_IN1LAO_MUTE, 0);
/* Uncached write-only register, no need for update_bits */
if (!priv->in_up_pending)
snd_soc_write(codec, priv->in_vu_reg, TACNA_IN_VU);
break;
case SND_SOC_DAPM_PRE_PMD:
snd_soc_update_bits(codec, reg,
TACNA_IN1LAO_MUTE, TACNA_IN1LAO_MUTE);
snd_soc_write(codec, priv->in_vu_reg, TACNA_IN_VU);
break;
default:
break;
}
return 0;
}
static const struct snd_kcontrol_new cs47l96_ao_dspao_trigger_output_mux[] = {
SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0),
};
static int cs47l96_ao_dsp_freq_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
switch (event) {
case SND_SOC_DAPM_POST_PMU:
return tacna_dsp_freq_update(w, TACNA_SYSTEM_CLOCK2AO,
TACNA_SYSTEM_CLOCK1AO);
default:
return 0;
}
}
static const struct snd_soc_dapm_widget cs47l96_ao_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("SYSCLKAO", TACNA_SYSTEM_CLOCK1AO, TACNA_SYSCLKAO_EN_SHIFT,
0, NULL, 0),
TACNA_DSP_FREQ_WIDGET_EV("DSP1AO", 0, cs47l96_ao_dsp_freq_ev),
SND_SOC_DAPM_INPUT("IN5AO_PDMCLK"),
SND_SOC_DAPM_INPUT("IN5AO_PDMDATA"),
SND_SOC_DAPM_INPUT("IN6AO_PDMCLK"),
SND_SOC_DAPM_INPUT("IN6AO_PDMDATA"),
SND_SOC_DAPM_INPUT("IN7AO_PDMCLK"),
SND_SOC_DAPM_INPUT("IN7AO_PDMDATA"),
SND_SOC_DAPM_OUTPUT("DSPAO Trigger Out"),
SND_SOC_DAPM_AIF_OUT("ASP1AOTX1", NULL, 0, TACNA_ASP1AO_ENABLES1,
TACNA_ASP1AO_TX1_EN_SHIFT, 0),
SND_SOC_DAPM_AIF_OUT("ASP1AOTX2", NULL, 0, TACNA_ASP1AO_ENABLES1,
TACNA_ASP1AO_TX2_EN_SHIFT, 0),
SND_SOC_DAPM_AIF_OUT("ASP1AOTX3", NULL, 0, TACNA_ASP1AO_ENABLES1,
TACNA_ASP1AO_TX3_EN_SHIFT, 0),
SND_SOC_DAPM_AIF_OUT("ASP1AOTX4", NULL, 0, TACNA_ASP1AO_ENABLES1,
TACNA_ASP1AO_TX4_EN_SHIFT, 0),
/* mux_in widgets : arranged in the order of sources
* specified in TACNA_MIXER_INPUT_ROUTES
*/
SND_SOC_DAPM_PGA_E("IN5LAO PGA", TACNA_AO_INPUT_CONTROL, TACNA_IN5LAO_EN_SHIFT,
0, NULL, 0, cs47l96_ao_in_ev,
SND_SOC_DAPM_PRE_PMD |
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_PGA_E("IN5RAO PGA", TACNA_AO_INPUT_CONTROL, TACNA_IN5RAO_EN_SHIFT,
0, NULL, 0, cs47l96_ao_in_ev,
SND_SOC_DAPM_PRE_PMD |
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_PGA_E("IN6LAO PGA", TACNA_AO_INPUT_CONTROL, TACNA_IN6LAO_EN_SHIFT,
0, NULL, 0, cs47l96_ao_in_ev,
SND_SOC_DAPM_PRE_PMD |
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_PGA_E("IN6RAO PGA", TACNA_AO_INPUT_CONTROL, TACNA_IN6RAO_EN_SHIFT,
0, NULL, 0, cs47l96_ao_in_ev,
SND_SOC_DAPM_PRE_PMD |
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_PGA_E("IN7LAO PGA", TACNA_AO_INPUT_CONTROL, TACNA_IN7LAO_EN_SHIFT,
0, NULL, 0, cs47l96_ao_in_ev,
SND_SOC_DAPM_PRE_PMD |
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_PGA_E("IN7RAO PGA", TACNA_AO_INPUT_CONTROL, TACNA_IN7RAO_EN_SHIFT,
0, NULL, 0, cs47l96_ao_in_ev,
SND_SOC_DAPM_PRE_PMD |
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_AIF_IN("ASP1AORX1", NULL, 0, TACNA_ASP1AO_ENABLES1,
TACNA_ASP1AO_RX1_EN_SHIFT, 0),
SND_SOC_DAPM_AIF_IN("ASP1AORX2", NULL, 0, TACNA_ASP1AO_ENABLES1,
TACNA_ASP1AO_RX2_EN_SHIFT, 0),
SND_SOC_DAPM_AIF_IN("ASP1AORX3", NULL, 0, TACNA_ASP1AO_ENABLES1,
TACNA_ASP1AO_RX3_EN_SHIFT, 0),
SND_SOC_DAPM_AIF_IN("ASP1AORX4", NULL, 0, TACNA_ASP1AO_ENABLES1,
TACNA_ASP1AO_RX4_EN_SHIFT, 0),
WM_HALO("DSP1AO", 0, wm_halo_early_event),
/* end of ordered widget list */
TACNA_MUX_WIDGETS(ASP1AOTX1, "ASP1AOTX1"),
TACNA_MUX_WIDGETS(ASP1AOTX2, "ASP1AOTX2"),
TACNA_MUX_WIDGETS(ASP1AOTX3, "ASP1AOTX3"),
TACNA_MUX_WIDGETS(ASP1AOTX4, "ASP1AOTX4"),
TACNA_MUX_WIDGETS(DSP1AORX1, "DSP1AORX1"),
TACNA_MUX_WIDGETS(DSP1AORX2, "DSP1AORX2"),
TACNA_MUX_WIDGETS(DSP1AORX3, "DSP1AORX3"),
TACNA_MUX_WIDGETS(DSP1AORX4, "DSP1AORX4"),
TACNA_MUX_WIDGETS(DSP1AORX5, "DSP1AORX5"),
TACNA_MUX_WIDGETS(DSP1AORX6, "DSP1AORX6"),
TACNA_MUX_WIDGETS(DSP1AORX7, "DSP1AORX7"),
TACNA_MUX_WIDGETS(DSP1AORX8, "DSP1AORX8"),
TACNA_MUX_WIDGETS(AOBRIDGE1IN1, "AOBRIDGE1IN1"),
TACNA_MUX_WIDGETS(AOBRIDGE1IN2, "AOBRIDGE1IN2"),
TACNA_MUX_WIDGETS(AOBRIDGE1IN3, "AOBRIDGE1IN3"),
TACNA_MUX_WIDGETS(AOBRIDGE1IN4, "AOBRIDGE1IN4"),
TACNA_MUX_WIDGETS(AOBRIDGE1IN5, "AOBRIDGE1IN5"),
TACNA_MUX_WIDGETS(AOBRIDGE1IN6, "AOBRIDGE1IN6"),
TACNA_MUX_WIDGETS(AOBRIDGE1IN7, "AOBRIDGE1IN7"),
TACNA_MUX_WIDGETS(AOBRIDGE1IN8, "AOBRIDGE1IN8"),
SND_SOC_DAPM_SWITCH("DSP1AO Trigger Output", SND_SOC_NOPM, 0, 0,
&cs47l96_ao_dspao_trigger_output_mux[0]),
};
#define TACNA_MIXER_INPUT_ROUTES(name) \
{ name, "IN5LAO", "IN5LAO PGA" }, \
{ name, "IN5RAO", "IN5RAO PGA" }, \
{ name, "IN6LAO", "IN6LAO PGA" }, \
{ name, "IN6RAO", "IN6RAO PGA" }, \
{ name, "IN7LAO", "IN7LAO PGA" }, \
{ name, "IN7RAO", "IN7RAO PGA" }, \
{ name, "ASP1AORX1", "ASP1AORX1" }, \
{ name, "ASP1AORX2", "ASP1AORX2" }, \
{ name, "ASP1AORX3", "ASP1AORX3" }, \
{ name, "ASP1AORX4", "ASP1AORX4" }, \
{ name, "AOBRIDGE2IN1", "AOBRIDGE2IN1" }, \
{ name, "AOBRIDGE2IN2", "AOBRIDGE2IN2" }, \
{ name, "AOBRIDGE2IN3", "AOBRIDGE2IN3" }, \
{ name, "AOBRIDGE2IN4", "AOBRIDGE2IN4" }, \
{ name, "AOBRIDGE2IN5", "AOBRIDGE2IN5" }, \
{ name, "AOBRIDGE2IN6", "AOBRIDGE2IN6" }, \
{ name, "AOBRIDGE2IN7", "AOBRIDGE2IN7" }, \
{ name, "AOBRIDGE2IN8", "AOBRIDGE2IN8" }, \
{ name, "DSP1AO.1", "DSP1AO" }, \
{ name, "DSP1AO.2", "DSP1AO" }, \
{ name, "DSP1AO.3", "DSP1AO" }, \
{ name, "DSP1AO.4", "DSP1AO" }, \
{ name, "DSP1AO.5", "DSP1AO" }, \
{ name, "DSP1AO.6", "DSP1AO" }, \
{ name, "DSP1AO.7", "DSP1AO" }, \
{ name, "DSP1AO.8", "DSP1AO" }
static const struct snd_soc_dapm_route cs47l96_ao_dapm_routes[] = {
/* Internal clock domains */
{ "DSP1AO", NULL, "SYSCLKAO" },
{ "DSP1AO", NULL, "DSP1AOFREQ" },
{ "IN5AO_PDMCLK", NULL, "SYSCLKAO" },
{ "IN5AO_PDMDATA", NULL, "SYSCLKAO" },
{ "IN6AO_PDMCLK", NULL, "SYSCLKAO" },
{ "IN6AO_PDMDATA", NULL, "SYSCLKAO" },
{ "IN7AO_PDMCLK", NULL, "SYSCLKAO" },
{ "IN7AO_PDMDATA", NULL, "SYSCLKAO" },
{ "Audio Trace DSP", NULL, "DSP1AO" },
{ "Voice Ctrl DSP", NULL, "DSP1AO" },
{ "ASP1AO Capture", NULL, "ASP1AOTX1" },
{ "ASP1AO Capture", NULL, "ASP1AOTX2" },
{ "ASP1AO Capture", NULL, "ASP1AOTX3" },
{ "ASP1AO Capture", NULL, "ASP1AOTX4" },
{ "ASP1AORX1", NULL, "ASP1AO Playback" },
{ "ASP1AORX2", NULL, "ASP1AO Playback" },
{ "ASP1AORX3", NULL, "ASP1AO Playback" },
{ "ASP1AORX4", NULL, "ASP1AO Playback" },
{ "ASP1AO Playback", NULL, "SYSCLKAO" },
{ "ASP1AO Capture", NULL, "SYSCLKAO" },
{ "AOBRIDGE1IN1", NULL, "SYSCLKAO" },
{ "AOBRIDGE1IN2", NULL, "SYSCLKAO" },
{ "AOBRIDGE1IN3", NULL, "SYSCLKAO" },
{ "AOBRIDGE1IN4", NULL, "SYSCLKAO" },
{ "AOBRIDGE1IN5", NULL, "SYSCLKAO" },
{ "AOBRIDGE1IN6", NULL, "SYSCLKAO" },
{ "AOBRIDGE1IN7", NULL, "SYSCLKAO" },
{ "AOBRIDGE1IN8", NULL, "SYSCLKAO" },
{ "IN5LAO PGA", NULL, "IN5AO_PDMCLK" },
{ "IN5LAO PGA", NULL, "IN5AO_PDMDATA" },
{ "IN5RAO PGA", NULL, "IN5AO_PDMCLK" },
{ "IN5RAO PGA", NULL, "IN5AO_PDMDATA" },
{ "IN6LAO PGA", NULL, "IN6AO_PDMCLK" },
{ "IN6LAO PGA", NULL, "IN6AO_PDMDATA" },
{ "IN6RAO PGA", NULL, "IN6AO_PDMCLK" },
{ "IN6RAO PGA", NULL, "IN6AO_PDMDATA" },
{ "IN7LAO PGA", NULL, "IN7AO_PDMCLK" },
{ "IN7LAO PGA", NULL, "IN7AO_PDMDATA" },
{ "IN7RAO PGA", NULL, "IN7AO_PDMCLK" },
{ "IN7RAO PGA", NULL, "IN7AO_PDMDATA" },
TACNA_MUX_ROUTES("ASP1AOTX1", "ASP1AOTX1"),
TACNA_MUX_ROUTES("ASP1AOTX2", "ASP1AOTX2"),
TACNA_MUX_ROUTES("ASP1AOTX3", "ASP1AOTX3"),
TACNA_MUX_ROUTES("ASP1AOTX4", "ASP1AOTX4"),
TACNA_DSPAO_ROUTES_1_8("DSP1AO"),
TACNA_MUX_ROUTES("AOBRIDGE1IN1", "AOBRIDGE1IN1"),
TACNA_MUX_ROUTES("AOBRIDGE1IN2", "AOBRIDGE1IN2"),
TACNA_MUX_ROUTES("AOBRIDGE1IN3", "AOBRIDGE1IN3"),
TACNA_MUX_ROUTES("AOBRIDGE1IN4", "AOBRIDGE1IN4"),
TACNA_MUX_ROUTES("AOBRIDGE1IN5", "AOBRIDGE1IN5"),
TACNA_MUX_ROUTES("AOBRIDGE1IN6", "AOBRIDGE1IN6"),
TACNA_MUX_ROUTES("AOBRIDGE1IN7", "AOBRIDGE1IN7"),
TACNA_MUX_ROUTES("AOBRIDGE1IN8", "AOBRIDGE1IN8"),
{ "DSPAO Trigger Out", NULL, "DSP1AO Trigger Output" },
{ "DSP1AO Trigger Output", "Switch", "DSP1AO" },
};
static struct snd_soc_dai_driver cs47l96_ao_dai[] = {
{
.name = "cs47l96-ao-asp1",
.id = 1,
.base = TACNA_ASP1AO_ENABLES1,
.playback = {
.stream_name = "ASP1AO Playback",
.channels_min = 1,
.channels_max = 4,
.rates = TACNA_RATES,
.formats = TACNA_FORMATS,
},
.capture = {
.stream_name = "ASP1AO Capture",
.channels_min = 1,
.channels_max = 4,
.rates = TACNA_RATES,
.formats = TACNA_FORMATS,
},
.ops = &tacna_dai_ops,
.symmetric_rates = 1,
.symmetric_samplebits = 1,
},
{
.name = "cs47l96-ao-cpu-trace",
.capture = {
.stream_name = "Audio Trace CPU",
.channels_min = 1,
.channels_max = 6,
.rates = TACNA_RATES,
.formats = TACNA_FORMATS,
},
.compress_new = &snd_soc_new_compress,
},
{
.name = "cs47l96-ao-dsp-trace",
.capture = {
.stream_name = "Audio Trace DSP",
.channels_min = 1,
.channels_max = 6,
.rates = TACNA_RATES,
.formats = TACNA_FORMATS,
},
},
{
.name = "cs47l96-ao-cpu-voicectrl",
.capture = {
.stream_name = "Voice Ctrl CPU",
.channels_min = 1,
.channels_max = 4,
.rates = TACNA_RATES,
.formats = TACNA_FORMATS,
},
.compress_new = &snd_soc_new_compress,
},
{
.name = "cs47l96-ao-dsp-voicectrl",
.capture = {
.stream_name = "Voice Ctrl DSP",
.channels_min = 1,
.channels_max = 4,
.rates = TACNA_RATES,
.formats = TACNA_FORMATS,
},
},
};
static int cs47l96_ao_compr_open(struct snd_compr_stream *stream)
{
struct snd_soc_pcm_runtime *rtd = stream->private_data;
struct cs47l96_ao *cs47l96_ao = snd_soc_platform_get_drvdata(rtd->platform);
struct tacna_priv *priv = &cs47l96_ao->core;
if (strcmp(rtd->codec_dai->name, "cs47l96-ao-dsp-trace") &&
strcmp(rtd->codec_dai->name, "cs47l96-ao-dsp-voicectrl")) {
dev_err(priv->dev,
"No suitable compressed stream for DAI '%s'\n",
rtd->codec_dai->name);
return -EINVAL;
}
return wm_adsp_compr_open(&priv->dsp[0], stream);
}
static irqreturn_t cs47l96_dsp1ao_irq0(int irq, void *data)
{
struct cs47l96_ao *cs47l96_ao = data;
struct tacna_priv *priv = &cs47l96_ao->core;
int ret;
ret = wm_adsp_compr_handle_irq(&priv->dsp[0]);
if (ret == -ENODEV) {
dev_err(priv->dev, "Spurious compressed data IRQ\n");
return IRQ_NONE;
}
return IRQ_HANDLED;
}
static irqreturn_t cs47l96_ao_mpu_fault_irq(int irq, void *data)
{
struct wm_adsp *dsp = data;
return wm_halo_bus_error(dsp);
}
static const struct soc_enum cs47l96_ao_dsp1_rx_rate_enum[] = {
SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
0 | TACNA_DSP_RATE_CTL_DIR_RX,
TACNA_AO_RATE_ENUM_SIZE,
tacna_ao_rate_text, tacna_ao_rate_val),
SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
1 | TACNA_DSP_RATE_CTL_DIR_RX,
TACNA_AO_RATE_ENUM_SIZE,
tacna_ao_rate_text, tacna_ao_rate_val),
SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
2 | TACNA_DSP_RATE_CTL_DIR_RX,
TACNA_AO_RATE_ENUM_SIZE,
tacna_ao_rate_text, tacna_ao_rate_val),
SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
3 | TACNA_DSP_RATE_CTL_DIR_RX,
TACNA_AO_RATE_ENUM_SIZE,
tacna_ao_rate_text, tacna_ao_rate_val),
SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
4 | TACNA_DSP_RATE_CTL_DIR_RX,
TACNA_AO_RATE_ENUM_SIZE,
tacna_ao_rate_text, tacna_ao_rate_val),
SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
5 | TACNA_DSP_RATE_CTL_DIR_RX,
TACNA_AO_RATE_ENUM_SIZE,
tacna_ao_rate_text, tacna_ao_rate_val),
SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
6 | TACNA_DSP_RATE_CTL_DIR_RX,
TACNA_AO_RATE_ENUM_SIZE,
tacna_ao_rate_text, tacna_ao_rate_val),
SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
7 | TACNA_DSP_RATE_CTL_DIR_RX,
TACNA_AO_RATE_ENUM_SIZE,
tacna_ao_rate_text, tacna_ao_rate_val),
};
static const struct soc_enum cs47l96_ao_dsp1_tx_rate_enum[] = {
SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
0 | TACNA_DSP_RATE_CTL_DIR_TX,
TACNA_AO_RATE_ENUM_SIZE,
tacna_ao_rate_text, tacna_ao_rate_val),
SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
1 | TACNA_DSP_RATE_CTL_DIR_TX,
TACNA_AO_RATE_ENUM_SIZE,
tacna_ao_rate_text, tacna_ao_rate_val),
SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
2 | TACNA_DSP_RATE_CTL_DIR_TX,
TACNA_AO_RATE_ENUM_SIZE,
tacna_ao_rate_text, tacna_ao_rate_val),
SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
3 | TACNA_DSP_RATE_CTL_DIR_TX,
TACNA_AO_RATE_ENUM_SIZE,
tacna_ao_rate_text, tacna_ao_rate_val),
SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
4 | TACNA_DSP_RATE_CTL_DIR_TX,
TACNA_AO_RATE_ENUM_SIZE,
tacna_ao_rate_text, tacna_ao_rate_val),
SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
5 | TACNA_DSP_RATE_CTL_DIR_TX,
TACNA_AO_RATE_ENUM_SIZE,
tacna_ao_rate_text, tacna_ao_rate_val),
SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
6 | TACNA_DSP_RATE_CTL_DIR_TX,
TACNA_AO_RATE_ENUM_SIZE,
tacna_ao_rate_text, tacna_ao_rate_val),
SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
7 | TACNA_DSP_RATE_CTL_DIR_TX,
TACNA_AO_RATE_ENUM_SIZE,
tacna_ao_rate_text, tacna_ao_rate_val),
};
static const struct snd_kcontrol_new cs47l96_ao_dsp1ao_rate_controls[] = {
SOC_ENUM_EXT("DSP1AORX1 Rate", cs47l96_ao_dsp1_rx_rate_enum[0],
tacna_dsp_rate_get, tacna_dsp_rate_put),
SOC_ENUM_EXT("DSP1AORX2 Rate", cs47l96_ao_dsp1_rx_rate_enum[1],
tacna_dsp_rate_get, tacna_dsp_rate_put),
SOC_ENUM_EXT("DSP1AORX3 Rate", cs47l96_ao_dsp1_rx_rate_enum[2],
tacna_dsp_rate_get, tacna_dsp_rate_put),
SOC_ENUM_EXT("DSP1AORX4 Rate", cs47l96_ao_dsp1_rx_rate_enum[3],
tacna_dsp_rate_get, tacna_dsp_rate_put),
SOC_ENUM_EXT("DSP1AORX5 Rate", cs47l96_ao_dsp1_rx_rate_enum[4],
tacna_dsp_rate_get, tacna_dsp_rate_put),
SOC_ENUM_EXT("DSP1AORX6 Rate", cs47l96_ao_dsp1_rx_rate_enum[5],
tacna_dsp_rate_get, tacna_dsp_rate_put),
SOC_ENUM_EXT("DSP1AORX7 Rate", cs47l96_ao_dsp1_rx_rate_enum[6],
tacna_dsp_rate_get, tacna_dsp_rate_put),
SOC_ENUM_EXT("DSP1AORX8 Rate", cs47l96_ao_dsp1_rx_rate_enum[7],
tacna_dsp_rate_get, tacna_dsp_rate_put),
SOC_ENUM_EXT("DSP1AOTX1 Rate", cs47l96_ao_dsp1_tx_rate_enum[0],
tacna_dsp_rate_get, tacna_dsp_rate_put),
SOC_ENUM_EXT("DSP1AOTX2 Rate", cs47l96_ao_dsp1_tx_rate_enum[1],
tacna_dsp_rate_get, tacna_dsp_rate_put),
SOC_ENUM_EXT("DSP1AOTX3 Rate", cs47l96_ao_dsp1_tx_rate_enum[2],
tacna_dsp_rate_get, tacna_dsp_rate_put),
SOC_ENUM_EXT("DSP1AOTX4 Rate", cs47l96_ao_dsp1_tx_rate_enum[3],
tacna_dsp_rate_get, tacna_dsp_rate_put),
SOC_ENUM_EXT("DSP1AOTX5 Rate", cs47l96_ao_dsp1_tx_rate_enum[4],
tacna_dsp_rate_get, tacna_dsp_rate_put),
SOC_ENUM_EXT("DSP1AOTX6 Rate", cs47l96_ao_dsp1_tx_rate_enum[5],
tacna_dsp_rate_get, tacna_dsp_rate_put),
SOC_ENUM_EXT("DSP1AOTX7 Rate", cs47l96_ao_dsp1_tx_rate_enum[6],
tacna_dsp_rate_get, tacna_dsp_rate_put),
SOC_ENUM_EXT("DSP1AOTX8 Rate", cs47l96_ao_dsp1_tx_rate_enum[7],
tacna_dsp_rate_get, tacna_dsp_rate_put),
};
static int cs47l96_ao_codec_probe(struct snd_soc_codec *codec)
{
struct cs47l96_ao *cs47l96_ao = snd_soc_codec_get_drvdata(codec);
struct tacna_priv *priv = &cs47l96_ao->core;
struct tacna *tacna = cs47l96_ao->core.tacna;
int ret;
BUILD_BUG_ON(ARRAY_SIZE(cs47l96_ao_mixer_texts) !=
TACNA_NUM_MIXER_AO_INPUTS);
BUILD_BUG_ON(ARRAY_SIZE(cs47l96_ao_mixer_values) !=
TACNA_NUM_MIXER_AO_INPUTS);
tacna->dapm = snd_soc_codec_get_dapm(codec);
ret = snd_soc_add_codec_controls(codec,
cs47l96_ao_dsp1ao_rate_controls,
priv->dsp[0].n_rx_channels +
priv->dsp[0].n_tx_channels);
if (ret)
return ret;
wm_adsp2_codec_probe(&cs47l96_ao->core.dsp[0], codec);
return 0;
}
static int cs47l96_ao_codec_remove(struct snd_soc_codec *codec)
{
struct cs47l96_ao *cs47l96_ao = snd_soc_codec_get_drvdata(codec);
struct tacna *tacna = cs47l96_ao->core.tacna;
wm_adsp2_codec_remove(&cs47l96_ao->core.dsp[0], codec);
tacna->dapm = NULL;
return 0;
}
static int cs47l96_ao_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
unsigned int fref, unsigned int fout)
{
struct cs47l96_ao *cs47l96_ao = snd_soc_codec_get_drvdata(codec);
switch (fll_id) {
case TACNA_FLL1_REFCLK:
break;
default:
return -EINVAL;
}
if (fout > 49152000) {
dev_err(codec->dev, "%u not supported for FLL1AO\n", fout);
return -EINVAL;
}
return tacna_fllhj_set_refclk(&cs47l96_ao->fll, source, fref, fout);
}
static int cs47l96_ao_set_sysclk(struct snd_soc_codec *codec, int clk_id,
int source, unsigned int freq, int dir)
{
struct cs47l96_ao *cs47l96_ao = snd_soc_codec_get_drvdata(codec);
switch (clk_id) {
case TACNA_CLK_SYSCLKAO:
if (freq > 49152000) {
dev_err(codec->dev, "%u not supported for SYSCLKAO\n",
freq);
return -EINVAL;
}
return tacna_set_sysclk(codec, clk_id, source, freq, dir);
default:
dev_err(cs47l96_ao->core.dev, "Unknown clock id %u\n", clk_id);
return -EINVAL;
}
}
static struct regmap *cs47l96_ao_get_regmap(struct device *dev)
{
struct cs47l96_ao *cs47l96_ao = dev_get_drvdata(dev);
return cs47l96_ao->core.tacna->regmap;
}
static const struct snd_soc_codec_driver soc_codec_dev_cs47l96_ao = {
.probe = &cs47l96_ao_codec_probe,
.remove = &cs47l96_ao_codec_remove,
.get_regmap = &cs47l96_ao_get_regmap,
.idle_bias_off = true,
.set_sysclk = &cs47l96_ao_set_sysclk,
.set_pll = &cs47l96_ao_set_fll,
.component_driver = {
.controls = cs47l96_ao_snd_controls,
.num_controls = ARRAY_SIZE(cs47l96_ao_snd_controls),
.dapm_widgets = cs47l96_ao_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(cs47l96_ao_dapm_widgets),
.dapm_routes = cs47l96_ao_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(cs47l96_ao_dapm_routes),
},
};
static const struct snd_compr_ops cs47l96_ao_compr_ops = {
.open = &cs47l96_ao_compr_open,
.free = &wm_adsp_compr_free,
.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 const struct snd_soc_platform_driver cs47l96_ao_compr_platform = {
.compr_ops = &cs47l96_ao_compr_ops,
};
static int cs47l96_ao_probe(struct platform_device *pdev)
{
struct tacna *tacna = dev_get_drvdata(pdev->dev.parent);
struct cs47l96_ao *cs47l96_ao;
struct wm_adsp *dsp;
int i, ret;
BUILD_BUG_ON(ARRAY_SIZE(cs47l96_ao_dai) > TACNA_MAX_DAI);
/* quick exit if tacna irqchip driver hasn't completed probe */
if (!tacna->irq_dev) {
dev_dbg(&pdev->dev, "irqchip driver not ready\n");
return -EPROBE_DEFER;
}
/* Source CLK32KAO from MCLK2 */
ret = regmap_update_bits(tacna->regmap, TACNA_CLOCK32KAO,
TACNA_CLK_32KAO_SRC_MASK,
TACNA_CLK_SRC_MCLK2);
if (ret) {
dev_err(tacna->dev, "Failed to init AO 32k clock: %d\n", ret);
return ret;
}
/* Slave SYSCLKAO to SYSCLK */
ret = regmap_update_bits(tacna->regmap, TACNA_SYSTEM_CLOCK6AO,
TACNA_SYSAO_FRAME_SLV_MASK,
1 << TACNA_SYSAO_FRAME_SLV_SHIFT);
if (ret) {
dev_err(tacna->dev, "Failed to init FRAME_SLV: %d\n", ret);
return ret;
}
cs47l96_ao = devm_kzalloc(&pdev->dev, sizeof(struct cs47l96_ao),
GFP_KERNEL);
if (!cs47l96_ao)
return -ENOMEM;
platform_set_drvdata(pdev, cs47l96_ao);
cs47l96_ao->core.tacna = tacna;
cs47l96_ao->core.dev = &pdev->dev;
cs47l96_ao->core.num_inputs = 6;
cs47l96_ao->core.max_analogue_inputs = 0;
cs47l96_ao->core.in_vu_reg = TACNA_AO_INPUT_CONTROL3;
mutex_init(&cs47l96_ao->core.rate_lock);
/* DSP1AO uses the DSP2 interrupt registers */
ret = tacna_request_irq(tacna, TACNA_IRQ_DSP2_IRQ0,
"DSP1AO Buffer IRQ", cs47l96_dsp1ao_irq0,
cs47l96_ao);
if (ret != 0) {
dev_err(&pdev->dev, "Failed to request DSP2_IRQ0: %d\n", ret);
return ret;
}
ret = tacna_set_irq_wake(tacna, TACNA_IRQ_DSP2_IRQ0, 1);
if(ret)
dev_warn(&pdev->dev, "Failed to set DSP IRQ wake: %d\n", ret);
dsp = &cs47l96_ao->core.dsp[0];
dsp->part = "cs47l96_ao";
dsp->num = 1;
dsp->suffix = "AO";
dsp->type = WMFW_HALO;
dsp->rev = 0;
dsp->dev = tacna->dev;
dsp->regmap = tacna->dsp_regmap[1];
dsp->ao_dsp = true;
dsp->base = TACNA_DSP2_CLOCK_FREQ;
dsp->base_sysinfo = TACNA_DSP2_SYS_INFO_ID;
dsp->mem = cs47l96_dsp1ao_regions;
dsp->num_mems = ARRAY_SIZE(cs47l96_dsp1ao_regions);
dsp->n_rx_channels = CS47L96_AO_DSP_N_RX_CHANNELS;
dsp->n_tx_channels = CS47L96_AO_DSP_N_TX_CHANNELS;
ret = wm_halo_init(dsp, &cs47l96_ao->core.rate_lock);
if (ret != 0)
goto error_dsp1ao_irq0;
ret = tacna_request_irq(tacna, TACNA_IRQ_DSP2_MPU_ERR,
"DSP1AO MPU", cs47l96_ao_mpu_fault_irq,
&cs47l96_ao->core.dsp[0]);
if (ret) {
dev_warn(&pdev->dev, "Failed to get DSP1AO MPU IRQ: %d\n",
ret);
goto error_dsp_core;
}
ret = tacna_request_irq(tacna, TACNA_IRQ_DSP2_WDT_EXPIRE,
"DSP1AO WDT", wm_halo_wdt_expire,
&cs47l96_ao->core.dsp[0]);
if (ret) {
dev_warn(&pdev->dev, "Failed to get DSP1AO WDT IRQ: %d\n",
ret);
goto error_mpu_irq;
}
cs47l96_ao->fll.tacna_priv = &cs47l96_ao->core;
cs47l96_ao->fll.id = 1;
cs47l96_ao->fll.base = TACNA_FLL1AO_CONTROL1;
cs47l96_ao->fll.sts_addr = TACNA_IRQ1_STS_6;
cs47l96_ao->fll.sts_mask = TACNA_FLL1AO_LOCK_STS1_MASK;
cs47l96_ao->fll.max_fref = 100000;
cs47l96_ao->fll.integer_only = 1;
cs47l96_ao->fll.has_lp = 1;
tacna_init_fll(&cs47l96_ao->fll);
for (i = 0; i < ARRAY_SIZE(cs47l96_ao_dai); i++)
tacna_init_dai(&cs47l96_ao->core, i);
pm_runtime_enable(&pdev->dev);
pm_runtime_idle(&pdev->dev);
ret = snd_soc_register_platform(&pdev->dev,
&cs47l96_ao_compr_platform);
if (ret < 0) {
dev_err(&pdev->dev, "Failed to register platform: %d\n", ret);
goto error_wdt_irq;
}
ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_cs47l96_ao,
cs47l96_ao_dai,
ARRAY_SIZE(cs47l96_ao_dai));
if (ret < 0) {
dev_err(&pdev->dev, "Failed to register codec: %d\n", ret);
goto error_unregister_platform;
}
return ret;
error_unregister_platform:
snd_soc_unregister_platform(&pdev->dev);
error_wdt_irq:
tacna_free_irq(tacna, TACNA_IRQ_DSP2_WDT_EXPIRE,
&cs47l96_ao->core.dsp[0]);
error_mpu_irq:
tacna_free_irq(tacna, TACNA_IRQ_DSP2_MPU_ERR,
&cs47l96_ao->core.dsp[0]);
error_dsp_core:
wm_adsp2_remove(&cs47l96_ao->core.dsp[0]);
error_dsp1ao_irq0:
tacna_set_irq_wake(tacna, TACNA_IRQ_DSP2_IRQ0, 0);
tacna_free_irq(tacna, TACNA_IRQ_DSP2_IRQ0, cs47l96_ao);
return ret;
}
static int cs47l96_ao_remove(struct platform_device *pdev)
{
struct cs47l96_ao *cs47l96_ao = platform_get_drvdata(pdev);
struct tacna *tacna = cs47l96_ao->core.tacna;
snd_soc_unregister_platform(&pdev->dev);
snd_soc_unregister_codec(&pdev->dev);
pm_runtime_disable(&pdev->dev);
tacna_free_irq(tacna, TACNA_IRQ_DSP2_WDT_EXPIRE,
&cs47l96_ao->core.dsp[0]);
tacna_free_irq(tacna, TACNA_IRQ_DSP2_MPU_ERR,
&cs47l96_ao->core.dsp[0]);
tacna_set_irq_wake(tacna, TACNA_IRQ_DSP2_IRQ0, 0);
tacna_free_irq(tacna, TACNA_IRQ_DSP2_IRQ0, cs47l96_ao);
wm_adsp2_remove(&cs47l96_ao->core.dsp[0]);
return 0;
}
static struct platform_driver cs47l96_ao_codec_driver = {
.driver = {
.name = "cs47l96-ao-codec",
.owner = THIS_MODULE,
},
.probe = &cs47l96_ao_probe,
.remove = &cs47l96_ao_remove,
};
module_platform_driver(cs47l96_ao_codec_driver);
MODULE_DESCRIPTION("ASoC CS47L96 AO driver");
MODULE_AUTHOR("Stuart Henderson <stuarth@opensource.cirrus.com>");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:cs47l96-ao-codec");