1
0
Fork 0

Merge remote-tracking branches 'asoc/topic/twl4030', 'asoc/topic/twl6040', 'asoc/topic/uda134x', 'asoc/topic/uda1380' and 'asoc/topic/uniphier' into asoc-next

hifive-unleashed-5.1
Mark Brown 2018-03-28 10:31:10 +08:00
No known key found for this signature in database
GPG Key ID: 24D68B725D5487D0
18 changed files with 4622 additions and 522 deletions

View File

@ -0,0 +1,45 @@
Socionext UniPhier SoC audio driver
The Socionext UniPhier audio subsystem consists of I2S and S/PDIF blocks in
the same register space.
Required properties:
- compatible : should be one of the following:
"socionext,uniphier-ld11-aio"
"socionext,uniphier-ld20-aio"
"socionext,uniphier-pxs2-aio"
- reg : offset and length of the register set for the device.
- interrupts : should contain I2S or S/PDIF interrupt.
- pinctrl-names : should be "default".
- pinctrl-0 : defined I2S signal pins for an external codec chip.
- clock-names : should include following entries:
"aio"
- clocks : a list of phandle, should contain an entry for each
entry in clock-names.
- reset-names : should include following entries:
"aio"
- resets : a list of phandle, should contain an entry for each
entry in reset-names.
- #sound-dai-cells: should be 1.
Optional properties:
- socionext,syscon: a phandle, should contain soc-glue.
The soc-glue is used for changing mode of S/PDIF signal pin
to Output from Hi-Z. This property is optional if you use
I2S signal pins only.
Example:
audio {
compatible = "socionext,uniphier-ld20-aio";
reg = <0x56000000 0x80000>;
interrupts = <0 144 4>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_aout>;
clock-names = "aio";
clocks = <&sys_clk 40>;
reset-names = "aio";
resets = <&sys_rst 40>;
#sound-dai-cells = <1>;
socionext,syscon = <&sg>;
};

File diff suppressed because it is too large Load Diff

View File

@ -76,7 +76,7 @@ struct twl6040_data {
unsigned int clk_in;
unsigned int sysclk;
struct twl6040_jack_data hs_jack;
struct snd_soc_codec *codec;
struct snd_soc_component *component;
struct mutex mutex;
};
@ -106,12 +106,12 @@ static const struct snd_pcm_hw_constraint_list sysclk_constraints[] = {
{ .count = ARRAY_SIZE(hp_rates), .list = hp_rates, },
};
#define to_twl6040(codec) dev_get_drvdata((codec)->dev->parent)
#define to_twl6040(component) dev_get_drvdata((component)->dev->parent)
static unsigned int twl6040_read(struct snd_soc_codec *codec, unsigned int reg)
static unsigned int twl6040_read(struct snd_soc_component *component, unsigned int reg)
{
struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
struct twl6040 *twl6040 = to_twl6040(codec);
struct twl6040_data *priv = snd_soc_component_get_drvdata(component);
struct twl6040 *twl6040 = to_twl6040(component);
u8 value;
if (reg >= TWL6040_CACHEREGNUM)
@ -133,10 +133,10 @@ static unsigned int twl6040_read(struct snd_soc_codec *codec, unsigned int reg)
return value;
}
static bool twl6040_can_write_to_chip(struct snd_soc_codec *codec,
static bool twl6040_can_write_to_chip(struct snd_soc_component *component,
unsigned int reg)
{
struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
struct twl6040_data *priv = snd_soc_component_get_drvdata(component);
switch (reg) {
case TWL6040_REG_HSLCTL:
@ -152,10 +152,10 @@ static bool twl6040_can_write_to_chip(struct snd_soc_codec *codec,
}
}
static inline void twl6040_update_dl12_cache(struct snd_soc_codec *codec,
static inline void twl6040_update_dl12_cache(struct snd_soc_component *component,
u8 reg, u8 value)
{
struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
struct twl6040_data *priv = snd_soc_component_get_drvdata(component);
switch (reg) {
case TWL6040_REG_HSLCTL:
@ -170,54 +170,54 @@ static inline void twl6040_update_dl12_cache(struct snd_soc_codec *codec,
}
}
static int twl6040_write(struct snd_soc_codec *codec,
static int twl6040_write(struct snd_soc_component *component,
unsigned int reg, unsigned int value)
{
struct twl6040 *twl6040 = to_twl6040(codec);
struct twl6040 *twl6040 = to_twl6040(component);
if (reg >= TWL6040_CACHEREGNUM)
return -EIO;
twl6040_update_dl12_cache(codec, reg, value);
if (twl6040_can_write_to_chip(codec, reg))
twl6040_update_dl12_cache(component, reg, value);
if (twl6040_can_write_to_chip(component, reg))
return twl6040_reg_write(twl6040, reg, value);
else
return 0;
}
static void twl6040_init_chip(struct snd_soc_codec *codec)
static void twl6040_init_chip(struct snd_soc_component *component)
{
twl6040_read(codec, TWL6040_REG_TRIM1);
twl6040_read(codec, TWL6040_REG_TRIM2);
twl6040_read(codec, TWL6040_REG_TRIM3);
twl6040_read(codec, TWL6040_REG_HSOTRIM);
twl6040_read(codec, TWL6040_REG_HFOTRIM);
twl6040_read(component, TWL6040_REG_TRIM1);
twl6040_read(component, TWL6040_REG_TRIM2);
twl6040_read(component, TWL6040_REG_TRIM3);
twl6040_read(component, TWL6040_REG_HSOTRIM);
twl6040_read(component, TWL6040_REG_HFOTRIM);
/* Change chip defaults */
/* No imput selected for microphone amplifiers */
twl6040_write(codec, TWL6040_REG_MICLCTL, 0x18);
twl6040_write(codec, TWL6040_REG_MICRCTL, 0x18);
twl6040_write(component, TWL6040_REG_MICLCTL, 0x18);
twl6040_write(component, TWL6040_REG_MICRCTL, 0x18);
/*
* We need to lower the default gain values, so the ramp code
* can work correctly for the first playback.
* This reduces the pop noise heard at the first playback.
*/
twl6040_write(codec, TWL6040_REG_HSGAIN, 0xff);
twl6040_write(codec, TWL6040_REG_EARCTL, 0x1e);
twl6040_write(codec, TWL6040_REG_HFLGAIN, 0x1d);
twl6040_write(codec, TWL6040_REG_HFRGAIN, 0x1d);
twl6040_write(codec, TWL6040_REG_LINEGAIN, 0);
twl6040_write(component, TWL6040_REG_HSGAIN, 0xff);
twl6040_write(component, TWL6040_REG_EARCTL, 0x1e);
twl6040_write(component, TWL6040_REG_HFLGAIN, 0x1d);
twl6040_write(component, TWL6040_REG_HFRGAIN, 0x1d);
twl6040_write(component, TWL6040_REG_LINEGAIN, 0);
}
/* set headset dac and driver power mode */
static int headset_power_mode(struct snd_soc_codec *codec, int high_perf)
static int headset_power_mode(struct snd_soc_component *component, int high_perf)
{
int hslctl, hsrctl;
int mask = TWL6040_HSDRVMODE | TWL6040_HSDACMODE;
hslctl = twl6040_read(codec, TWL6040_REG_HSLCTL);
hsrctl = twl6040_read(codec, TWL6040_REG_HSRCTL);
hslctl = twl6040_read(component, TWL6040_REG_HSLCTL);
hsrctl = twl6040_read(component, TWL6040_REG_HSRCTL);
if (high_perf) {
hslctl &= ~mask;
@ -227,8 +227,8 @@ static int headset_power_mode(struct snd_soc_codec *codec, int high_perf)
hsrctl |= mask;
}
twl6040_write(codec, TWL6040_REG_HSLCTL, hslctl);
twl6040_write(codec, TWL6040_REG_HSRCTL, hsrctl);
twl6040_write(component, TWL6040_REG_HSLCTL, hslctl);
twl6040_write(component, TWL6040_REG_HSRCTL, hsrctl);
return 0;
}
@ -236,7 +236,7 @@ static int headset_power_mode(struct snd_soc_codec *codec, int high_perf)
static int twl6040_hs_dac_event(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 snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
u8 hslctl, hsrctl;
/*
@ -244,8 +244,8 @@ static int twl6040_hs_dac_event(struct snd_soc_dapm_widget *w,
* Both HS DAC need to be turned on (before the HS driver) and off at
* the same time.
*/
hslctl = twl6040_read(codec, TWL6040_REG_HSLCTL);
hsrctl = twl6040_read(codec, TWL6040_REG_HSRCTL);
hslctl = twl6040_read(component, TWL6040_REG_HSLCTL);
hsrctl = twl6040_read(component, TWL6040_REG_HSRCTL);
if (SND_SOC_DAPM_EVENT_ON(event)) {
hslctl |= TWL6040_HSDACENA;
hsrctl |= TWL6040_HSDACENA;
@ -253,8 +253,8 @@ static int twl6040_hs_dac_event(struct snd_soc_dapm_widget *w,
hslctl &= ~TWL6040_HSDACENA;
hsrctl &= ~TWL6040_HSDACENA;
}
twl6040_write(codec, TWL6040_REG_HSLCTL, hslctl);
twl6040_write(codec, TWL6040_REG_HSRCTL, hsrctl);
twl6040_write(component, TWL6040_REG_HSLCTL, hslctl);
twl6040_write(component, TWL6040_REG_HSRCTL, hsrctl);
msleep(1);
return 0;
@ -263,17 +263,17 @@ static int twl6040_hs_dac_event(struct snd_soc_dapm_widget *w,
static int twl6040_ep_drv_event(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 twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
struct twl6040_data *priv = snd_soc_component_get_drvdata(component);
int ret = 0;
if (SND_SOC_DAPM_EVENT_ON(event)) {
/* Earphone doesn't support low power mode */
priv->hs_power_mode_locked = 1;
ret = headset_power_mode(codec, 1);
ret = headset_power_mode(component, 1);
} else {
priv->hs_power_mode_locked = 0;
ret = headset_power_mode(codec, priv->hs_power_mode);
ret = headset_power_mode(component, priv->hs_power_mode);
}
msleep(1);
@ -281,16 +281,16 @@ static int twl6040_ep_drv_event(struct snd_soc_dapm_widget *w,
return ret;
}
static void twl6040_hs_jack_report(struct snd_soc_codec *codec,
static void twl6040_hs_jack_report(struct snd_soc_component *component,
struct snd_soc_jack *jack, int report)
{
struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
struct twl6040_data *priv = snd_soc_component_get_drvdata(component);
int status;
mutex_lock(&priv->mutex);
/* Sync status */
status = twl6040_read(codec, TWL6040_REG_STATUS);
status = twl6040_read(component, TWL6040_REG_STATUS);
if (status & TWL6040_PLUGCOMP)
snd_soc_jack_report(jack, report, report);
else
@ -299,16 +299,16 @@ static void twl6040_hs_jack_report(struct snd_soc_codec *codec,
mutex_unlock(&priv->mutex);
}
void twl6040_hs_jack_detect(struct snd_soc_codec *codec,
void twl6040_hs_jack_detect(struct snd_soc_component *component,
struct snd_soc_jack *jack, int report)
{
struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
struct twl6040_data *priv = snd_soc_component_get_drvdata(component);
struct twl6040_jack_data *hs_jack = &priv->hs_jack;
hs_jack->jack = jack;
hs_jack->report = report;
twl6040_hs_jack_report(codec, hs_jack->jack, hs_jack->report);
twl6040_hs_jack_report(component, hs_jack->jack, hs_jack->report);
}
EXPORT_SYMBOL_GPL(twl6040_hs_jack_detect);
@ -316,17 +316,17 @@ static void twl6040_accessory_work(struct work_struct *work)
{
struct twl6040_data *priv = container_of(work,
struct twl6040_data, hs_jack.work.work);
struct snd_soc_codec *codec = priv->codec;
struct snd_soc_component *component = priv->component;
struct twl6040_jack_data *hs_jack = &priv->hs_jack;
twl6040_hs_jack_report(codec, hs_jack->jack, hs_jack->report);
twl6040_hs_jack_report(component, hs_jack->jack, hs_jack->report);
}
/* audio interrupt handler */
static irqreturn_t twl6040_audio_handler(int irq, void *data)
{
struct snd_soc_codec *codec = data;
struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
struct snd_soc_component *component = data;
struct twl6040_data *priv = snd_soc_component_get_drvdata(component);
queue_delayed_work(system_power_efficient_wq,
&priv->hs_jack.work, msecs_to_jiffies(200));
@ -337,12 +337,12 @@ static irqreturn_t twl6040_audio_handler(int irq, void *data)
static int twl6040_soc_dapm_put_vibra_enum(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
struct snd_soc_component *component = snd_soc_dapm_kcontrol_component(kcontrol);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned int val;
/* Do not allow changes while Input/FF efect is running */
val = twl6040_read(codec, e->reg);
val = twl6040_read(component, e->reg);
if (val & TWL6040_VIBENA && !(val & TWL6040_VIBSEL))
return -EBUSY;
@ -486,8 +486,8 @@ static SOC_ENUM_SINGLE_EXT_DECL(twl6040_power_mode_enum,
static int twl6040_headset_power_get_enum(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
struct twl6040_data *priv = snd_soc_component_get_drvdata(component);
ucontrol->value.enumerated.item[0] = priv->hs_power_mode;
@ -497,13 +497,13 @@ static int twl6040_headset_power_get_enum(struct snd_kcontrol *kcontrol,
static int twl6040_headset_power_put_enum(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
struct twl6040_data *priv = snd_soc_component_get_drvdata(component);
int high_perf = ucontrol->value.enumerated.item[0];
int ret = 0;
if (!priv->hs_power_mode_locked)
ret = headset_power_mode(codec, high_perf);
ret = headset_power_mode(component, high_perf);
if (!ret)
priv->hs_power_mode = high_perf;
@ -514,8 +514,8 @@ static int twl6040_headset_power_put_enum(struct snd_kcontrol *kcontrol,
static int twl6040_pll_get_enum(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
struct twl6040_data *priv = snd_soc_component_get_drvdata(component);
ucontrol->value.enumerated.item[0] = priv->pll_power_mode;
@ -525,17 +525,17 @@ static int twl6040_pll_get_enum(struct snd_kcontrol *kcontrol,
static int twl6040_pll_put_enum(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
struct twl6040_data *priv = snd_soc_component_get_drvdata(component);
priv->pll_power_mode = ucontrol->value.enumerated.item[0];
return 0;
}
int twl6040_get_dl1_gain(struct snd_soc_codec *codec)
int twl6040_get_dl1_gain(struct snd_soc_component *component)
{
struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
if (snd_soc_dapm_get_pin_status(dapm, "EP"))
return -1; /* -1dB */
@ -543,7 +543,7 @@ int twl6040_get_dl1_gain(struct snd_soc_codec *codec)
if (snd_soc_dapm_get_pin_status(dapm, "HSOR") ||
snd_soc_dapm_get_pin_status(dapm, "HSOL")) {
u8 val = twl6040_read(codec, TWL6040_REG_HSLCTL);
u8 val = twl6040_read(component, TWL6040_REG_HSLCTL);
if (val & TWL6040_HSDACMODE)
/* HSDACL in LP mode */
return -8; /* -8dB */
@ -555,26 +555,26 @@ int twl6040_get_dl1_gain(struct snd_soc_codec *codec)
}
EXPORT_SYMBOL_GPL(twl6040_get_dl1_gain);
int twl6040_get_clk_id(struct snd_soc_codec *codec)
int twl6040_get_clk_id(struct snd_soc_component *component)
{
struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
struct twl6040_data *priv = snd_soc_component_get_drvdata(component);
return priv->pll_power_mode;
}
EXPORT_SYMBOL_GPL(twl6040_get_clk_id);
int twl6040_get_trim_value(struct snd_soc_codec *codec, enum twl6040_trim trim)
int twl6040_get_trim_value(struct snd_soc_component *component, enum twl6040_trim trim)
{
if (unlikely(trim >= TWL6040_TRIM_INVAL))
return -EINVAL;
return twl6040_read(codec, TWL6040_REG_TRIM1 + trim);
return twl6040_read(component, TWL6040_REG_TRIM1 + trim);
}
EXPORT_SYMBOL_GPL(twl6040_get_trim_value);
int twl6040_get_hs_step_size(struct snd_soc_codec *codec)
int twl6040_get_hs_step_size(struct snd_soc_component *component)
{
struct twl6040 *twl6040 = to_twl6040(codec);
struct twl6040 *twl6040 = to_twl6040(component);
if (twl6040_get_revid(twl6040) < TWL6040_REV_ES1_3)
/* For ES under ES_1.3 HS step is 2 mV */
@ -829,11 +829,11 @@ static const struct snd_soc_dapm_route intercon[] = {
{"VIBRAR", NULL, "Vibra Right Driver"},
};
static int twl6040_set_bias_level(struct snd_soc_codec *codec,
static int twl6040_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
struct twl6040 *twl6040 = to_twl6040(codec);
struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
struct twl6040 *twl6040 = to_twl6040(component);
struct twl6040_data *priv = snd_soc_component_get_drvdata(component);
int ret = 0;
switch (level) {
@ -856,7 +856,7 @@ static int twl6040_set_bias_level(struct snd_soc_codec *codec,
priv->codec_powered = 1;
/* Set external boost GPO */
twl6040_write(codec, TWL6040_REG_GPOCTL, 0x02);
twl6040_write(component, TWL6040_REG_GPOCTL, 0x02);
break;
case SND_SOC_BIAS_OFF:
if (!priv->codec_powered)
@ -873,8 +873,8 @@ static int twl6040_set_bias_level(struct snd_soc_codec *codec,
static int twl6040_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
struct snd_soc_component *component = dai->component;
struct twl6040_data *priv = snd_soc_component_get_drvdata(component);
snd_pcm_hw_constraint_list(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_RATE,
@ -887,8 +887,8 @@ static int twl6040_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
struct snd_soc_component *component = dai->component;
struct twl6040_data *priv = snd_soc_component_get_drvdata(component);
int rate;
rate = params_rate(params);
@ -899,7 +899,7 @@ static int twl6040_hw_params(struct snd_pcm_substream *substream,
case 88200:
/* These rates are not supported when HPPLL is in use */
if (unlikely(priv->pll == TWL6040_SYSCLK_SEL_HPPLL)) {
dev_err(codec->dev, "HPPLL does not support rate %d\n",
dev_err(component->dev, "HPPLL does not support rate %d\n",
rate);
return -EINVAL;
}
@ -913,7 +913,7 @@ static int twl6040_hw_params(struct snd_pcm_substream *substream,
priv->sysclk = 19200000;
break;
default:
dev_err(codec->dev, "unsupported rate %d\n", rate);
dev_err(component->dev, "unsupported rate %d\n", rate);
return -EINVAL;
}
@ -923,20 +923,20 @@ static int twl6040_hw_params(struct snd_pcm_substream *substream,
static int twl6040_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
struct twl6040 *twl6040 = to_twl6040(codec);
struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
struct snd_soc_component *component = dai->component;
struct twl6040 *twl6040 = to_twl6040(component);
struct twl6040_data *priv = snd_soc_component_get_drvdata(component);
int ret;
if (!priv->sysclk) {
dev_err(codec->dev,
dev_err(component->dev,
"no mclk configured, call set_sysclk() on init\n");
return -EINVAL;
}
ret = twl6040_set_pll(twl6040, priv->pll, priv->clk_in, priv->sysclk);
if (ret) {
dev_err(codec->dev, "Can not set PLL (%d)\n", ret);
dev_err(component->dev, "Can not set PLL (%d)\n", ret);
return -EPERM;
}
@ -946,8 +946,8 @@ static int twl6040_prepare(struct snd_pcm_substream *substream,
static int twl6040_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = codec_dai->codec;
struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
struct snd_soc_component *component = codec_dai->component;
struct twl6040_data *priv = snd_soc_component_get_drvdata(component);
switch (clk_id) {
case TWL6040_SYSCLK_SEL_LPPLL:
@ -956,26 +956,26 @@ static int twl6040_set_dai_sysclk(struct snd_soc_dai *codec_dai,
priv->clk_in = freq;
break;
default:
dev_err(codec->dev, "unknown clk_id %d\n", clk_id);
dev_err(component->dev, "unknown clk_id %d\n", clk_id);
return -EINVAL;
}
return 0;
}
static void twl6040_mute_path(struct snd_soc_codec *codec, enum twl6040_dai_id id,
static void twl6040_mute_path(struct snd_soc_component *component, enum twl6040_dai_id id,
int mute)
{
struct twl6040 *twl6040 = to_twl6040(codec);
struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
struct twl6040 *twl6040 = to_twl6040(component);
struct twl6040_data *priv = snd_soc_component_get_drvdata(component);
int hslctl, hsrctl, earctl;
int hflctl, hfrctl;
switch (id) {
case TWL6040_DAI_DL1:
hslctl = twl6040_read(codec, TWL6040_REG_HSLCTL);
hsrctl = twl6040_read(codec, TWL6040_REG_HSRCTL);
earctl = twl6040_read(codec, TWL6040_REG_EARCTL);
hslctl = twl6040_read(component, TWL6040_REG_HSLCTL);
hsrctl = twl6040_read(component, TWL6040_REG_HSRCTL);
earctl = twl6040_read(component, TWL6040_REG_EARCTL);
if (mute) {
/* Power down drivers and DACs */
@ -991,8 +991,8 @@ static void twl6040_mute_path(struct snd_soc_codec *codec, enum twl6040_dai_id i
priv->dl1_unmuted = !mute;
break;
case TWL6040_DAI_DL2:
hflctl = twl6040_read(codec, TWL6040_REG_HFLCTL);
hfrctl = twl6040_read(codec, TWL6040_REG_HFRCTL);
hflctl = twl6040_read(component, TWL6040_REG_HFLCTL);
hfrctl = twl6040_read(component, TWL6040_REG_HFRCTL);
if (mute) {
/* Power down drivers and DACs */
@ -1015,12 +1015,12 @@ static int twl6040_digital_mute(struct snd_soc_dai *dai, int mute)
{
switch (dai->id) {
case TWL6040_DAI_LEGACY:
twl6040_mute_path(dai->codec, TWL6040_DAI_DL1, mute);
twl6040_mute_path(dai->codec, TWL6040_DAI_DL2, mute);
twl6040_mute_path(dai->component, TWL6040_DAI_DL1, mute);
twl6040_mute_path(dai->component, TWL6040_DAI_DL2, mute);
break;
case TWL6040_DAI_DL1:
case TWL6040_DAI_DL2:
twl6040_mute_path(dai->codec, dai->id, mute);
twl6040_mute_path(dai->component, dai->id, mute);
break;
default:
break;
@ -1107,23 +1107,23 @@ static struct snd_soc_dai_driver twl6040_dai[] = {
},
};
static int twl6040_probe(struct snd_soc_codec *codec)
static int twl6040_probe(struct snd_soc_component *component)
{
struct twl6040_data *priv;
struct platform_device *pdev = to_platform_device(codec->dev);
struct platform_device *pdev = to_platform_device(component->dev);
int ret = 0;
priv = devm_kzalloc(codec->dev, sizeof(*priv), GFP_KERNEL);
priv = devm_kzalloc(component->dev, sizeof(*priv), GFP_KERNEL);
if (priv == NULL)
return -ENOMEM;
snd_soc_codec_set_drvdata(codec, priv);
snd_soc_component_set_drvdata(component, priv);
priv->codec = codec;
priv->component = component;
priv->plug_irq = platform_get_irq(pdev, 0);
if (priv->plug_irq < 0) {
dev_err(codec->dev, "invalid irq: %d\n", priv->plug_irq);
dev_err(component->dev, "invalid irq: %d\n", priv->plug_irq);
return priv->plug_irq;
}
@ -1134,64 +1134,55 @@ static int twl6040_probe(struct snd_soc_codec *codec)
ret = request_threaded_irq(priv->plug_irq, NULL,
twl6040_audio_handler,
IRQF_NO_SUSPEND | IRQF_ONESHOT,
"twl6040_irq_plug", codec);
"twl6040_irq_plug", component);
if (ret) {
dev_err(codec->dev, "PLUG IRQ request failed: %d\n", ret);
dev_err(component->dev, "PLUG IRQ request failed: %d\n", ret);
return ret;
}
snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY);
twl6040_init_chip(codec);
snd_soc_component_force_bias_level(component, SND_SOC_BIAS_STANDBY);
twl6040_init_chip(component);
return 0;
}
static int twl6040_remove(struct snd_soc_codec *codec)
static void twl6040_remove(struct snd_soc_component *component)
{
struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
struct twl6040_data *priv = snd_soc_component_get_drvdata(component);
free_irq(priv->plug_irq, codec);
return 0;
free_irq(priv->plug_irq, component);
}
static const struct snd_soc_codec_driver soc_codec_dev_twl6040 = {
.probe = twl6040_probe,
.remove = twl6040_remove,
.read = twl6040_read,
.write = twl6040_write,
.set_bias_level = twl6040_set_bias_level,
.suspend_bias_off = true,
.ignore_pmdown_time = true,
.component_driver = {
.controls = twl6040_snd_controls,
.num_controls = ARRAY_SIZE(twl6040_snd_controls),
.dapm_widgets = twl6040_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(twl6040_dapm_widgets),
.dapm_routes = intercon,
.num_dapm_routes = ARRAY_SIZE(intercon),
},
static const struct snd_soc_component_driver soc_component_dev_twl6040 = {
.probe = twl6040_probe,
.remove = twl6040_remove,
.read = twl6040_read,
.write = twl6040_write,
.set_bias_level = twl6040_set_bias_level,
.controls = twl6040_snd_controls,
.num_controls = ARRAY_SIZE(twl6040_snd_controls),
.dapm_widgets = twl6040_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(twl6040_dapm_widgets),
.dapm_routes = intercon,
.num_dapm_routes = ARRAY_SIZE(intercon),
.suspend_bias_off = 1,
.idle_bias_on = 1,
.endianness = 1,
.non_legacy_dai_naming = 1,
};
static int twl6040_codec_probe(struct platform_device *pdev)
{
return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_twl6040,
return devm_snd_soc_register_component(&pdev->dev,
&soc_component_dev_twl6040,
twl6040_dai, ARRAY_SIZE(twl6040_dai));
}
static int twl6040_codec_remove(struct platform_device *pdev)
{
snd_soc_unregister_codec(&pdev->dev);
return 0;
}
static struct platform_driver twl6040_codec_driver = {
.driver = {
.name = "twl6040-codec",
},
.probe = twl6040_codec_probe,
.remove = twl6040_codec_remove,
};
module_platform_driver(twl6040_codec_driver);

View File

@ -34,11 +34,11 @@ enum twl6040_trim {
#define TWL6040_HSF_TRIM_LEFT(x) (x & 0x0f)
#define TWL6040_HSF_TRIM_RIGHT(x) ((x >> 4) & 0x0f)
int twl6040_get_dl1_gain(struct snd_soc_codec *codec);
void twl6040_hs_jack_detect(struct snd_soc_codec *codec,
int twl6040_get_dl1_gain(struct snd_soc_component *component);
void twl6040_hs_jack_detect(struct snd_soc_component *component,
struct snd_soc_jack *jack, int report);
int twl6040_get_clk_id(struct snd_soc_codec *codec);
int twl6040_get_trim_value(struct snd_soc_codec *codec, enum twl6040_trim trim);
int twl6040_get_hs_step_size(struct snd_soc_codec *codec);
int twl6040_get_clk_id(struct snd_soc_component *component);
int twl6040_get_trim_value(struct snd_soc_component *component, enum twl6040_trim trim);
int twl6040_get_hs_step_size(struct snd_soc_component *component);
#endif /* End of __TWL6040_H__ */

View File

@ -110,9 +110,9 @@ static int uda134x_regmap_write(void *context, unsigned int reg,
return 0;
}
static inline void uda134x_reset(struct snd_soc_codec *codec)
static inline void uda134x_reset(struct snd_soc_component *component)
{
struct uda134x_priv *uda134x = snd_soc_codec_get_drvdata(codec);
struct uda134x_priv *uda134x = snd_soc_component_get_drvdata(component);
unsigned int mask = 1<<6;
regmap_update_bits(uda134x->regmap, UDA134X_STATUS0, mask, mask);
@ -122,7 +122,7 @@ static inline void uda134x_reset(struct snd_soc_codec *codec)
static int uda134x_mute(struct snd_soc_dai *dai, int mute)
{
struct uda134x_priv *uda134x = snd_soc_codec_get_drvdata(dai->codec);
struct uda134x_priv *uda134x = snd_soc_component_get_drvdata(dai->component);
unsigned int mask = 1<<2;
unsigned int val;
@ -139,8 +139,8 @@ static int uda134x_mute(struct snd_soc_dai *dai, int mute)
static int uda134x_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
struct uda134x_priv *uda134x = snd_soc_codec_get_drvdata(codec);
struct snd_soc_component *component = dai->component;
struct uda134x_priv *uda134x = snd_soc_component_get_drvdata(component);
struct snd_pcm_runtime *master_runtime;
if (uda134x->master_substream) {
@ -168,8 +168,8 @@ static int uda134x_startup(struct snd_pcm_substream *substream,
static void uda134x_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
struct uda134x_priv *uda134x = snd_soc_codec_get_drvdata(codec);
struct snd_soc_component *component = dai->component;
struct uda134x_priv *uda134x = snd_soc_component_get_drvdata(component);
if (uda134x->master_substream == substream)
uda134x->master_substream = uda134x->slave_substream;
@ -181,8 +181,8 @@ static int uda134x_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
struct uda134x_priv *uda134x = snd_soc_codec_get_drvdata(codec);
struct snd_soc_component *component = dai->component;
struct uda134x_priv *uda134x = snd_soc_component_get_drvdata(component);
unsigned int hw_params = 0;
if (substream == uda134x->slave_substream) {
@ -248,8 +248,8 @@ static int uda134x_hw_params(struct snd_pcm_substream *substream,
static int uda134x_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = codec_dai->codec;
struct uda134x_priv *uda134x = snd_soc_codec_get_drvdata(codec);
struct snd_soc_component *component = codec_dai->component;
struct uda134x_priv *uda134x = snd_soc_component_get_drvdata(component);
pr_debug("%s clk_id: %d, freq: %u, dir: %d\n", __func__,
clk_id, freq, dir);
@ -270,8 +270,8 @@ static int uda134x_set_dai_sysclk(struct snd_soc_dai *codec_dai,
static int uda134x_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
struct uda134x_priv *uda134x = snd_soc_codec_get_drvdata(codec);
struct snd_soc_component *component = codec_dai->component;
struct uda134x_priv *uda134x = snd_soc_component_get_drvdata(component);
pr_debug("%s fmt: %08X\n", __func__, fmt);
@ -294,10 +294,10 @@ static int uda134x_set_dai_fmt(struct snd_soc_dai *codec_dai,
return 0;
}
static int uda134x_set_bias_level(struct snd_soc_codec *codec,
static int uda134x_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
struct uda134x_priv *uda134x = snd_soc_codec_get_drvdata(codec);
struct uda134x_priv *uda134x = snd_soc_component_get_drvdata(component);
struct uda134x_platform_data *pd = uda134x->pd;
pr_debug("%s bias level %d\n", __func__, level);
@ -446,10 +446,10 @@ static struct snd_soc_dai_driver uda134x_dai = {
.ops = &uda134x_dai_ops,
};
static int uda134x_soc_probe(struct snd_soc_codec *codec)
static int uda134x_soc_probe(struct snd_soc_component *component)
{
struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
struct uda134x_priv *uda134x = snd_soc_codec_get_drvdata(codec);
struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
struct uda134x_priv *uda134x = snd_soc_component_get_drvdata(component);
struct uda134x_platform_data *pd = uda134x->pd;
const struct snd_soc_dapm_widget *widgets;
unsigned num_widgets;
@ -473,7 +473,7 @@ static int uda134x_soc_probe(struct snd_soc_codec *codec)
if (pd->power)
pd->power(1);
uda134x_reset(codec);
uda134x_reset(component);
if (pd->model == UDA134X_UDA1341) {
widgets = uda1341_dapm_widgets;
@ -493,15 +493,15 @@ static int uda134x_soc_probe(struct snd_soc_codec *codec)
switch (pd->model) {
case UDA134X_UDA1340:
case UDA134X_UDA1344:
ret = snd_soc_add_codec_controls(codec, uda1340_snd_controls,
ret = snd_soc_add_component_controls(component, uda1340_snd_controls,
ARRAY_SIZE(uda1340_snd_controls));
break;
case UDA134X_UDA1341:
ret = snd_soc_add_codec_controls(codec, uda1341_snd_controls,
ret = snd_soc_add_component_controls(component, uda1341_snd_controls,
ARRAY_SIZE(uda1341_snd_controls));
break;
case UDA134X_UDA1345:
ret = snd_soc_add_codec_controls(codec, uda1345_snd_controls,
ret = snd_soc_add_component_controls(component, uda1345_snd_controls,
ARRAY_SIZE(uda1345_snd_controls));
break;
default:
@ -518,17 +518,18 @@ static int uda134x_soc_probe(struct snd_soc_codec *codec)
return 0;
}
static const struct snd_soc_codec_driver soc_codec_dev_uda134x = {
.probe = uda134x_soc_probe,
.set_bias_level = uda134x_set_bias_level,
.suspend_bias_off = true,
.component_driver = {
.dapm_widgets = uda134x_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(uda134x_dapm_widgets),
.dapm_routes = uda134x_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(uda134x_dapm_routes),
},
static const struct snd_soc_component_driver soc_component_dev_uda134x = {
.probe = uda134x_soc_probe,
.set_bias_level = uda134x_set_bias_level,
.dapm_widgets = uda134x_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(uda134x_dapm_widgets),
.dapm_routes = uda134x_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(uda134x_dapm_routes),
.suspend_bias_off = 1,
.idle_bias_on = 1,
.use_pmdown_time = 1,
.endianness = 1,
.non_legacy_dai_naming = 1,
};
static const struct regmap_config uda134x_regmap_config = {
@ -571,14 +572,8 @@ static int uda134x_codec_probe(struct platform_device *pdev)
if (IS_ERR(uda134x->regmap))
return PTR_ERR(uda134x->regmap);
return snd_soc_register_codec(&pdev->dev,
&soc_codec_dev_uda134x, &uda134x_dai, 1);
}
static int uda134x_codec_remove(struct platform_device *pdev)
{
snd_soc_unregister_codec(&pdev->dev);
return 0;
return devm_snd_soc_register_component(&pdev->dev,
&soc_component_dev_uda134x, &uda134x_dai, 1);
}
static struct platform_driver uda134x_codec_driver = {
@ -586,7 +581,6 @@ static struct platform_driver uda134x_codec_driver = {
.name = "uda134x-codec",
},
.probe = uda134x_codec_probe,
.remove = uda134x_codec_remove,
};
module_platform_driver(uda134x_codec_driver);

View File

@ -34,7 +34,7 @@
/* codec private data */
struct uda1380_priv {
struct snd_soc_codec *codec;
struct snd_soc_component *component;
unsigned int dac_clk;
struct work_struct work;
struct i2c_client *i2c;
@ -61,10 +61,10 @@ static unsigned long uda1380_cache_dirty;
/*
* read uda1380 register cache
*/
static inline unsigned int uda1380_read_reg_cache(struct snd_soc_codec *codec,
static inline unsigned int uda1380_read_reg_cache(struct snd_soc_component *component,
unsigned int reg)
{
struct uda1380_priv *uda1380 = snd_soc_codec_get_drvdata(codec);
struct uda1380_priv *uda1380 = snd_soc_component_get_drvdata(component);
u16 *cache = uda1380->reg_cache;
if (reg == UDA1380_RESET)
@ -77,10 +77,10 @@ static inline unsigned int uda1380_read_reg_cache(struct snd_soc_codec *codec,
/*
* write uda1380 register cache
*/
static inline void uda1380_write_reg_cache(struct snd_soc_codec *codec,
static inline void uda1380_write_reg_cache(struct snd_soc_component *component,
u16 reg, unsigned int value)
{
struct uda1380_priv *uda1380 = snd_soc_codec_get_drvdata(codec);
struct uda1380_priv *uda1380 = snd_soc_component_get_drvdata(component);
u16 *cache = uda1380->reg_cache;
if (reg >= UDA1380_CACHEREGNUM)
@ -93,10 +93,10 @@ static inline void uda1380_write_reg_cache(struct snd_soc_codec *codec,
/*
* write to the UDA1380 register space
*/
static int uda1380_write(struct snd_soc_codec *codec, unsigned int reg,
static int uda1380_write(struct snd_soc_component *component, unsigned int reg,
unsigned int value)
{
struct uda1380_priv *uda1380 = snd_soc_codec_get_drvdata(codec);
struct uda1380_priv *uda1380 = snd_soc_component_get_drvdata(component);
u8 data[3];
/* data is
@ -108,12 +108,12 @@ static int uda1380_write(struct snd_soc_codec *codec, unsigned int reg,
data[1] = (value & 0xff00) >> 8;
data[2] = value & 0x00ff;
uda1380_write_reg_cache(codec, reg, value);
uda1380_write_reg_cache(component, reg, value);
/* the interpolator & decimator regs must only be written when the
* codec DAI is active.
*/
if (!snd_soc_codec_is_active(codec) && (reg >= UDA1380_MVOL))
if (!snd_soc_component_is_active(component) && (reg >= UDA1380_MVOL))
return 0;
pr_debug("uda1380: hw write %x val %x\n", reg, value);
if (i2c_master_send(uda1380->i2c, data, 3) == 3) {
@ -133,9 +133,9 @@ static int uda1380_write(struct snd_soc_codec *codec, unsigned int reg,
return -EIO;
}
static void uda1380_sync_cache(struct snd_soc_codec *codec)
static void uda1380_sync_cache(struct snd_soc_component *component)
{
struct uda1380_priv *uda1380 = snd_soc_codec_get_drvdata(codec);
struct uda1380_priv *uda1380 = snd_soc_component_get_drvdata(component);
int reg;
u8 data[3];
u16 *cache = uda1380->reg_cache;
@ -146,15 +146,15 @@ static void uda1380_sync_cache(struct snd_soc_codec *codec)
data[1] = (cache[reg] & 0xff00) >> 8;
data[2] = cache[reg] & 0x00ff;
if (i2c_master_send(uda1380->i2c, data, 3) != 3)
dev_err(codec->dev, "%s: write to reg 0x%x failed\n",
dev_err(component->dev, "%s: write to reg 0x%x failed\n",
__func__, reg);
}
}
static int uda1380_reset(struct snd_soc_codec *codec)
static int uda1380_reset(struct snd_soc_component *component)
{
struct uda1380_platform_data *pdata = codec->dev->platform_data;
struct uda1380_priv *uda1380 = snd_soc_codec_get_drvdata(codec);
struct uda1380_platform_data *pdata = component->dev->platform_data;
struct uda1380_priv *uda1380 = snd_soc_component_get_drvdata(component);
if (gpio_is_valid(pdata->gpio_reset)) {
gpio_set_value(pdata->gpio_reset, 1);
@ -168,7 +168,7 @@ static int uda1380_reset(struct snd_soc_codec *codec)
data[2] = 0;
if (i2c_master_send(uda1380->i2c, data, 3) != 3) {
dev_err(codec->dev, "%s: failed\n", __func__);
dev_err(component->dev, "%s: failed\n", __func__);
return -EIO;
}
}
@ -179,15 +179,15 @@ static int uda1380_reset(struct snd_soc_codec *codec)
static void uda1380_flush_work(struct work_struct *work)
{
struct uda1380_priv *uda1380 = container_of(work, struct uda1380_priv, work);
struct snd_soc_codec *uda1380_codec = uda1380->codec;
struct snd_soc_component *uda1380_component = uda1380->component;
int bit, reg;
for_each_set_bit(bit, &uda1380_cache_dirty, UDA1380_CACHEREGNUM - 0x10) {
reg = 0x10 + bit;
pr_debug("uda1380: flush reg %x val %x:\n", reg,
uda1380_read_reg_cache(uda1380_codec, reg));
uda1380_write(uda1380_codec, reg,
uda1380_read_reg_cache(uda1380_codec, reg));
uda1380_read_reg_cache(uda1380_component, reg));
uda1380_write(uda1380_component, reg,
uda1380_read_reg_cache(uda1380_component, reg));
clear_bit(bit, &uda1380_cache_dirty);
}
@ -420,11 +420,11 @@ static const struct snd_soc_dapm_route uda1380_dapm_routes[] = {
static int uda1380_set_dai_fmt_both(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
struct snd_soc_component *component = codec_dai->component;
int iface;
/* set up DAI based upon fmt */
iface = uda1380_read_reg_cache(codec, UDA1380_IFACE);
iface = uda1380_read_reg_cache(component, UDA1380_IFACE);
iface &= ~(R01_SFORI_MASK | R01_SIM | R01_SFORO_MASK);
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
@ -442,7 +442,7 @@ static int uda1380_set_dai_fmt_both(struct snd_soc_dai *codec_dai,
if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS)
return -EINVAL;
uda1380_write_reg_cache(codec, UDA1380_IFACE, iface);
uda1380_write_reg_cache(component, UDA1380_IFACE, iface);
return 0;
}
@ -450,11 +450,11 @@ static int uda1380_set_dai_fmt_both(struct snd_soc_dai *codec_dai,
static int uda1380_set_dai_fmt_playback(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
struct snd_soc_component *component = codec_dai->component;
int iface;
/* set up DAI based upon fmt */
iface = uda1380_read_reg_cache(codec, UDA1380_IFACE);
iface = uda1380_read_reg_cache(component, UDA1380_IFACE);
iface &= ~R01_SFORI_MASK;
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
@ -472,7 +472,7 @@ static int uda1380_set_dai_fmt_playback(struct snd_soc_dai *codec_dai,
if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS)
return -EINVAL;
uda1380_write(codec, UDA1380_IFACE, iface);
uda1380_write(component, UDA1380_IFACE, iface);
return 0;
}
@ -480,11 +480,11 @@ static int uda1380_set_dai_fmt_playback(struct snd_soc_dai *codec_dai,
static int uda1380_set_dai_fmt_capture(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
struct snd_soc_component *component = codec_dai->component;
int iface;
/* set up DAI based upon fmt */
iface = uda1380_read_reg_cache(codec, UDA1380_IFACE);
iface = uda1380_read_reg_cache(component, UDA1380_IFACE);
iface &= ~(R01_SIM | R01_SFORO_MASK);
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
@ -501,7 +501,7 @@ static int uda1380_set_dai_fmt_capture(struct snd_soc_dai *codec_dai,
if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) == SND_SOC_DAIFMT_CBM_CFM)
iface |= R01_SIM;
uda1380_write(codec, UDA1380_IFACE, iface);
uda1380_write(component, UDA1380_IFACE, iface);
return 0;
}
@ -509,20 +509,20 @@ static int uda1380_set_dai_fmt_capture(struct snd_soc_dai *codec_dai,
static int uda1380_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
struct uda1380_priv *uda1380 = snd_soc_codec_get_drvdata(codec);
int mixer = uda1380_read_reg_cache(codec, UDA1380_MIXER);
struct snd_soc_component *component = dai->component;
struct uda1380_priv *uda1380 = snd_soc_component_get_drvdata(component);
int mixer = uda1380_read_reg_cache(component, UDA1380_MIXER);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
uda1380_write_reg_cache(codec, UDA1380_MIXER,
uda1380_write_reg_cache(component, UDA1380_MIXER,
mixer & ~R14_SILENCE);
schedule_work(&uda1380->work);
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
uda1380_write_reg_cache(codec, UDA1380_MIXER,
uda1380_write_reg_cache(component, UDA1380_MIXER,
mixer | R14_SILENCE);
schedule_work(&uda1380->work);
break;
@ -534,13 +534,13 @@ static int uda1380_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
u16 clk = uda1380_read_reg_cache(codec, UDA1380_CLK);
struct snd_soc_component *component = dai->component;
u16 clk = uda1380_read_reg_cache(component, UDA1380_CLK);
/* set WSPLL power and divider if running from this clock */
if (clk & R00_DAC_CLK) {
int rate = params_rate(params);
u16 pm = uda1380_read_reg_cache(codec, UDA1380_PM);
u16 pm = uda1380_read_reg_cache(component, UDA1380_PM);
clk &= ~0x3; /* clear SEL_LOOP_DIV */
switch (rate) {
case 6250 ... 12500:
@ -556,7 +556,7 @@ static int uda1380_pcm_hw_params(struct snd_pcm_substream *substream,
clk |= 0x3;
break;
}
uda1380_write(codec, UDA1380_PM, R02_PON_PLL | pm);
uda1380_write(component, UDA1380_PM, R02_PON_PLL | pm);
}
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
@ -564,20 +564,20 @@ static int uda1380_pcm_hw_params(struct snd_pcm_substream *substream,
else
clk |= R00_EN_ADC | R00_EN_DEC;
uda1380_write(codec, UDA1380_CLK, clk);
uda1380_write(component, UDA1380_CLK, clk);
return 0;
}
static void uda1380_pcm_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
u16 clk = uda1380_read_reg_cache(codec, UDA1380_CLK);
struct snd_soc_component *component = dai->component;
u16 clk = uda1380_read_reg_cache(component, UDA1380_CLK);
/* shut down WSPLL power if running from this clock */
if (clk & R00_DAC_CLK) {
u16 pm = uda1380_read_reg_cache(codec, UDA1380_PM);
uda1380_write(codec, UDA1380_PM, ~R02_PON_PLL & pm);
u16 pm = uda1380_read_reg_cache(component, UDA1380_PM);
uda1380_write(component, UDA1380_PM, ~R02_PON_PLL & pm);
}
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
@ -585,33 +585,33 @@ static void uda1380_pcm_shutdown(struct snd_pcm_substream *substream,
else
clk &= ~(R00_EN_ADC | R00_EN_DEC);
uda1380_write(codec, UDA1380_CLK, clk);
uda1380_write(component, UDA1380_CLK, clk);
}
static int uda1380_set_bias_level(struct snd_soc_codec *codec,
static int uda1380_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
int pm = uda1380_read_reg_cache(codec, UDA1380_PM);
int pm = uda1380_read_reg_cache(component, UDA1380_PM);
int reg;
struct uda1380_platform_data *pdata = codec->dev->platform_data;
struct uda1380_platform_data *pdata = component->dev->platform_data;
switch (level) {
case SND_SOC_BIAS_ON:
case SND_SOC_BIAS_PREPARE:
/* ADC, DAC on */
uda1380_write(codec, UDA1380_PM, R02_PON_BIAS | pm);
uda1380_write(component, UDA1380_PM, R02_PON_BIAS | pm);
break;
case SND_SOC_BIAS_STANDBY:
if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) {
if (gpio_is_valid(pdata->gpio_power)) {
gpio_set_value(pdata->gpio_power, 1);
mdelay(1);
uda1380_reset(codec);
uda1380_reset(component);
}
uda1380_sync_cache(codec);
uda1380_sync_cache(component);
}
uda1380_write(codec, UDA1380_PM, 0x0);
uda1380_write(component, UDA1380_PM, 0x0);
break;
case SND_SOC_BIAS_OFF:
if (!gpio_is_valid(pdata->gpio_power))
@ -694,16 +694,16 @@ static struct snd_soc_dai_driver uda1380_dai[] = {
},
};
static int uda1380_probe(struct snd_soc_codec *codec)
static int uda1380_probe(struct snd_soc_component *component)
{
struct uda1380_platform_data *pdata =codec->dev->platform_data;
struct uda1380_priv *uda1380 = snd_soc_codec_get_drvdata(codec);
struct uda1380_platform_data *pdata =component->dev->platform_data;
struct uda1380_priv *uda1380 = snd_soc_component_get_drvdata(component);
int ret;
uda1380->codec = codec;
uda1380->component = component;
if (!gpio_is_valid(pdata->gpio_power)) {
ret = uda1380_reset(codec);
ret = uda1380_reset(component);
if (ret)
return ret;
}
@ -713,10 +713,10 @@ static int uda1380_probe(struct snd_soc_codec *codec)
/* set clock input */
switch (pdata->dac_clk) {
case UDA1380_DAC_CLK_SYSCLK:
uda1380_write_reg_cache(codec, UDA1380_CLK, 0);
uda1380_write_reg_cache(component, UDA1380_CLK, 0);
break;
case UDA1380_DAC_CLK_WSPLL:
uda1380_write_reg_cache(codec, UDA1380_CLK,
uda1380_write_reg_cache(component, UDA1380_CLK,
R00_DAC_CLK);
break;
}
@ -724,21 +724,22 @@ static int uda1380_probe(struct snd_soc_codec *codec)
return 0;
}
static const struct snd_soc_codec_driver soc_codec_dev_uda1380 = {
.probe = uda1380_probe,
.read = uda1380_read_reg_cache,
.write = uda1380_write,
.set_bias_level = uda1380_set_bias_level,
.suspend_bias_off = true,
.component_driver = {
.controls = uda1380_snd_controls,
.num_controls = ARRAY_SIZE(uda1380_snd_controls),
.dapm_widgets = uda1380_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(uda1380_dapm_widgets),
.dapm_routes = uda1380_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(uda1380_dapm_routes),
},
static const struct snd_soc_component_driver soc_component_dev_uda1380 = {
.probe = uda1380_probe,
.read = uda1380_read_reg_cache,
.write = uda1380_write,
.set_bias_level = uda1380_set_bias_level,
.controls = uda1380_snd_controls,
.num_controls = ARRAY_SIZE(uda1380_snd_controls),
.dapm_widgets = uda1380_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(uda1380_dapm_widgets),
.dapm_routes = uda1380_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(uda1380_dapm_routes),
.suspend_bias_off = 1,
.idle_bias_on = 1,
.use_pmdown_time = 1,
.endianness = 1,
.non_legacy_dai_naming = 1,
};
static int uda1380_i2c_probe(struct i2c_client *i2c,
@ -780,17 +781,11 @@ static int uda1380_i2c_probe(struct i2c_client *i2c,
i2c_set_clientdata(i2c, uda1380);
uda1380->i2c = i2c;
ret = snd_soc_register_codec(&i2c->dev,
&soc_codec_dev_uda1380, uda1380_dai, ARRAY_SIZE(uda1380_dai));
ret = devm_snd_soc_register_component(&i2c->dev,
&soc_component_dev_uda1380, uda1380_dai, ARRAY_SIZE(uda1380_dai));
return ret;
}
static int uda1380_i2c_remove(struct i2c_client *i2c)
{
snd_soc_unregister_codec(&i2c->dev);
return 0;
}
static const struct i2c_device_id uda1380_i2c_id[] = {
{ "uda1380", 0 },
{ }
@ -809,7 +804,6 @@ static struct i2c_driver uda1380_i2c_driver = {
.of_match_table = uda1380_of_match,
},
.probe = uda1380_i2c_probe,
.remove = uda1380_i2c_remove,
.id_table = uda1380_i2c_id,
};

View File

@ -52,7 +52,7 @@ static int omap_abe_hw_params(struct snd_pcm_substream *substream,
int clk_id, freq;
int ret;
clk_id = twl6040_get_clk_id(rtd->codec);
clk_id = twl6040_get_clk_id(codec_dai->component);
if (clk_id == TWL6040_SYSCLK_SEL_HPPLL)
freq = priv->mclk_freq;
else if (clk_id == TWL6040_SYSCLK_SEL_LPPLL)
@ -166,7 +166,7 @@ static const struct snd_soc_dapm_route audio_map[] = {
static int omap_abe_twl6040_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_codec *codec = rtd->codec;
struct snd_soc_component *component = rtd->codec_dai->component;
struct snd_soc_card *card = rtd->card;
struct abe_twl6040 *priv = snd_soc_card_get_drvdata(card);
int hs_trim;
@ -176,7 +176,7 @@ static int omap_abe_twl6040_init(struct snd_soc_pcm_runtime *rtd)
* Configure McPDM offset cancellation based on the HSOTRIM value from
* twl6040.
*/
hs_trim = twl6040_get_trim_value(codec, TWL6040_TRIM_HSOTRIM);
hs_trim = twl6040_get_trim_value(component, TWL6040_TRIM_HSOTRIM);
omap_mcpdm_configure_dn_offsets(rtd, TWL6040_HSF_TRIM_LEFT(hs_trim),
TWL6040_HSF_TRIM_RIGHT(hs_trim));
@ -189,7 +189,7 @@ static int omap_abe_twl6040_init(struct snd_soc_pcm_runtime *rtd)
if (ret)
return ret;
twl6040_hs_jack_detect(codec, &hs_jack, SND_JACK_HEADSET);
twl6040_hs_jack_detect(component, &hs_jack, SND_JACK_HEADSET);
}
return 0;

View File

@ -8,6 +8,39 @@ config SND_SOC_UNIPHIER
audio interfaces to support below.
If unsure select "N".
config SND_SOC_UNIPHIER_AIO
tristate "UniPhier AIO DAI Driver"
select REGMAP_MMIO
select SND_SOC_COMPRESS
depends on SND_SOC_UNIPHIER
help
This adds ASoC driver support for Socionext UniPhier
'AIO' Audio Input/Output subsystem.
Select Y if you use such device.
If unsure select "N".
config SND_SOC_UNIPHIER_LD11
tristate "UniPhier LD11/LD20 Device Driver"
depends on SND_SOC_UNIPHIER
select SND_SOC_UNIPHIER_AIO
select SND_SOC_UNIPHIER_AIO_DMA
help
This adds ASoC driver for Socionext UniPhier LD11/LD20
input and output that can be used with other codecs.
Select Y if you use such device.
If unsure select "N".
config SND_SOC_UNIPHIER_PXS2
tristate "UniPhier PXs2 Device Driver"
depends on SND_SOC_UNIPHIER
select SND_SOC_UNIPHIER_AIO
select SND_SOC_UNIPHIER_AIO_DMA
help
This adds ASoC driver for Socionext UniPhier PXs2
input and output that can be used with other codecs.
Select Y if you use such device.
If unsure select "N".
config SND_SOC_UNIPHIER_EVEA_CODEC
tristate "UniPhier SoC internal audio codec"
depends on SND_SOC_UNIPHIER

View File

@ -1,3 +1,11 @@
# SPDX-License-Identifier: GPL-2.0
snd-soc-uniphier-aio-cpu-objs := aio-core.o aio-dma.o aio-cpu.o aio-compress.o
snd-soc-uniphier-aio-ld11-objs := aio-ld11.o
snd-soc-uniphier-aio-pxs2-objs := aio-pxs2.o
obj-$(CONFIG_SND_SOC_UNIPHIER_AIO) += snd-soc-uniphier-aio-cpu.o
obj-$(CONFIG_SND_SOC_UNIPHIER_LD11) += snd-soc-uniphier-aio-ld11.o
obj-$(CONFIG_SND_SOC_UNIPHIER_PXS2) += snd-soc-uniphier-aio-pxs2.o
snd-soc-uniphier-evea-objs := evea.o
obj-$(CONFIG_SND_SOC_UNIPHIER_EVEA_CODEC) += snd-soc-uniphier-evea.o

View File

@ -0,0 +1,438 @@
// SPDX-License-Identifier: GPL-2.0
//
// Socionext UniPhier AIO Compress Audio driver.
//
// Copyright (c) 2017-2018 Socionext Inc.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; version 2
// of the License.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, see <http://www.gnu.org/licenses/>.
#include <linux/bitfield.h>
#include <linux/circ_buf.h>
#include <linux/dma-mapping.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/soc.h>
#include "aio.h"
static int uniphier_aio_compr_prepare(struct snd_compr_stream *cstream);
static int uniphier_aio_compr_hw_free(struct snd_compr_stream *cstream);
static int uniphier_aio_comprdma_new(struct snd_soc_pcm_runtime *rtd)
{
struct snd_compr *compr = rtd->compr;
struct device *dev = compr->card->dev;
struct uniphier_aio *aio = uniphier_priv(rtd->cpu_dai);
struct uniphier_aio_sub *sub = &aio->sub[compr->direction];
size_t size = AUD_RING_SIZE;
int dma_dir = DMA_FROM_DEVICE, ret;
ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(33));
if (ret)
return ret;
sub->compr_area = kzalloc(size, GFP_KERNEL);
if (!sub->compr_area)
return -ENOMEM;
if (sub->swm->dir == PORT_DIR_OUTPUT)
dma_dir = DMA_TO_DEVICE;
sub->compr_addr = dma_map_single(dev, sub->compr_area, size, dma_dir);
if (dma_mapping_error(dev, sub->compr_addr)) {
kfree(sub->compr_area);
sub->compr_area = NULL;
return -ENOMEM;
}
sub->compr_bytes = size;
return 0;
}
static int uniphier_aio_comprdma_free(struct snd_soc_pcm_runtime *rtd)
{
struct snd_compr *compr = rtd->compr;
struct device *dev = compr->card->dev;
struct uniphier_aio *aio = uniphier_priv(rtd->cpu_dai);
struct uniphier_aio_sub *sub = &aio->sub[compr->direction];
int dma_dir = DMA_FROM_DEVICE;
if (sub->swm->dir == PORT_DIR_OUTPUT)
dma_dir = DMA_TO_DEVICE;
dma_unmap_single(dev, sub->compr_addr, sub->compr_bytes, dma_dir);
kfree(sub->compr_area);
sub->compr_area = NULL;
return 0;
}
static int uniphier_aio_compr_open(struct snd_compr_stream *cstream)
{
struct snd_soc_pcm_runtime *rtd = cstream->private_data;
struct uniphier_aio *aio = uniphier_priv(rtd->cpu_dai);
struct uniphier_aio_sub *sub = &aio->sub[cstream->direction];
int ret;
if (sub->cstream)
return -EBUSY;
sub->cstream = cstream;
sub->pass_through = 1;
sub->use_mmap = false;
ret = uniphier_aio_comprdma_new(rtd);
if (ret)
return ret;
ret = aio_init(sub);
if (ret)
return ret;
return 0;
}
static int uniphier_aio_compr_free(struct snd_compr_stream *cstream)
{
struct snd_soc_pcm_runtime *rtd = cstream->private_data;
struct uniphier_aio *aio = uniphier_priv(rtd->cpu_dai);
struct uniphier_aio_sub *sub = &aio->sub[cstream->direction];
int ret;
ret = uniphier_aio_compr_hw_free(cstream);
if (ret)
return ret;
ret = uniphier_aio_comprdma_free(rtd);
if (ret)
return ret;
sub->cstream = NULL;
return 0;
}
static int uniphier_aio_compr_get_params(struct snd_compr_stream *cstream,
struct snd_codec *params)
{
struct snd_soc_pcm_runtime *rtd = cstream->private_data;
struct uniphier_aio *aio = uniphier_priv(rtd->cpu_dai);
struct uniphier_aio_sub *sub = &aio->sub[cstream->direction];
*params = sub->cparams.codec;
return 0;
}
static int uniphier_aio_compr_set_params(struct snd_compr_stream *cstream,
struct snd_compr_params *params)
{
struct snd_soc_pcm_runtime *rtd = cstream->private_data;
struct uniphier_aio *aio = uniphier_priv(rtd->cpu_dai);
struct uniphier_aio_sub *sub = &aio->sub[cstream->direction];
struct device *dev = &aio->chip->pdev->dev;
int ret;
if (params->codec.id != SND_AUDIOCODEC_IEC61937) {
dev_err(dev, "Codec ID is not supported(%d)\n",
params->codec.id);
return -EINVAL;
}
if (params->codec.profile != SND_AUDIOPROFILE_IEC61937_SPDIF) {
dev_err(dev, "Codec profile is not supported(%d)\n",
params->codec.profile);
return -EINVAL;
}
/* IEC frame type will be changed after received valid data */
sub->iec_pc = IEC61937_PC_AAC;
sub->cparams = *params;
sub->setting = 1;
aio_port_reset(sub);
aio_src_reset(sub);
ret = uniphier_aio_compr_prepare(cstream);
if (ret)
return ret;
return 0;
}
static int uniphier_aio_compr_hw_free(struct snd_compr_stream *cstream)
{
struct snd_soc_pcm_runtime *rtd = cstream->private_data;
struct uniphier_aio *aio = uniphier_priv(rtd->cpu_dai);
struct uniphier_aio_sub *sub = &aio->sub[cstream->direction];
sub->setting = 0;
return 0;
}
static int uniphier_aio_compr_prepare(struct snd_compr_stream *cstream)
{
struct snd_soc_pcm_runtime *rtd = cstream->private_data;
struct snd_compr_runtime *runtime = cstream->runtime;
struct uniphier_aio *aio = uniphier_priv(rtd->cpu_dai);
struct uniphier_aio_sub *sub = &aio->sub[cstream->direction];
int bytes = runtime->fragment_size;
unsigned long flags;
int ret;
ret = aiodma_ch_set_param(sub);
if (ret)
return ret;
spin_lock_irqsave(&sub->lock, flags);
ret = aiodma_rb_set_buffer(sub, sub->compr_addr,
sub->compr_addr + sub->compr_bytes,
bytes);
spin_unlock_irqrestore(&sub->lock, flags);
if (ret)
return ret;
ret = aio_port_set_param(sub, sub->pass_through, &sub->params);
if (ret)
return ret;
ret = aio_oport_set_stream_type(sub, sub->iec_pc);
if (ret)
return ret;
aio_port_set_enable(sub, 1);
ret = aio_if_set_param(sub, sub->pass_through);
if (ret)
return ret;
return 0;
}
static int uniphier_aio_compr_trigger(struct snd_compr_stream *cstream,
int cmd)
{
struct snd_soc_pcm_runtime *rtd = cstream->private_data;
struct snd_compr_runtime *runtime = cstream->runtime;
struct uniphier_aio *aio = uniphier_priv(rtd->cpu_dai);
struct uniphier_aio_sub *sub = &aio->sub[cstream->direction];
struct device *dev = &aio->chip->pdev->dev;
int bytes = runtime->fragment_size, ret = 0;
unsigned long flags;
spin_lock_irqsave(&sub->lock, flags);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
aiodma_rb_sync(sub, sub->compr_addr, sub->compr_bytes, bytes);
aiodma_ch_set_enable(sub, 1);
sub->running = 1;
break;
case SNDRV_PCM_TRIGGER_STOP:
sub->running = 0;
aiodma_ch_set_enable(sub, 0);
break;
default:
dev_warn(dev, "Unknown trigger(%d)\n", cmd);
ret = -EINVAL;
}
spin_unlock_irqrestore(&sub->lock, flags);
return ret;
}
static int uniphier_aio_compr_pointer(struct snd_compr_stream *cstream,
struct snd_compr_tstamp *tstamp)
{
struct snd_soc_pcm_runtime *rtd = cstream->private_data;
struct snd_compr_runtime *runtime = cstream->runtime;
struct uniphier_aio *aio = uniphier_priv(rtd->cpu_dai);
struct uniphier_aio_sub *sub = &aio->sub[cstream->direction];
int bytes = runtime->fragment_size;
unsigned long flags;
u32 pos;
spin_lock_irqsave(&sub->lock, flags);
aiodma_rb_sync(sub, sub->compr_addr, sub->compr_bytes, bytes);
if (sub->swm->dir == PORT_DIR_OUTPUT) {
pos = sub->rd_offs;
/* Size of AIO output format is double of IEC61937 */
tstamp->copied_total = sub->rd_total / 2;
} else {
pos = sub->wr_offs;
tstamp->copied_total = sub->rd_total;
}
tstamp->byte_offset = pos;
spin_unlock_irqrestore(&sub->lock, flags);
return 0;
}
static int aio_compr_send_to_hw(struct uniphier_aio_sub *sub,
char __user *buf, size_t dstsize)
{
u32 __user *srcbuf = (u32 __user *)buf;
u32 *dstbuf = (u32 *)(sub->compr_area + sub->wr_offs);
int src = 0, dst = 0, ret;
u32 frm, frm_a, frm_b;
while (dstsize > 0) {
ret = get_user(frm, srcbuf + src);
if (ret)
return ret;
src++;
frm_a = frm & 0xffff;
frm_b = (frm >> 16) & 0xffff;
if (frm == IEC61937_HEADER_SIGN) {
frm_a |= 0x01000000;
/* Next data is Pc and Pd */
sub->iec_header = true;
} else {
u16 pc = be16_to_cpu((__be16)frm_a);
if (sub->iec_header && sub->iec_pc != pc) {
/* Force overwrite IEC frame type */
sub->iec_pc = pc;
ret = aio_oport_set_stream_type(sub, pc);
if (ret)
return ret;
}
sub->iec_header = false;
}
dstbuf[dst++] = frm_a;
dstbuf[dst++] = frm_b;
dstsize -= sizeof(u32) * 2;
}
return 0;
}
static int uniphier_aio_compr_copy(struct snd_compr_stream *cstream,
char __user *buf, size_t count)
{
struct snd_soc_pcm_runtime *rtd = cstream->private_data;
struct snd_compr_runtime *runtime = cstream->runtime;
struct device *carddev = rtd->compr->card->dev;
struct uniphier_aio *aio = uniphier_priv(rtd->cpu_dai);
struct uniphier_aio_sub *sub = &aio->sub[cstream->direction];
size_t cnt = min_t(size_t, count, aio_rb_space_to_end(sub) / 2);
int bytes = runtime->fragment_size;
unsigned long flags;
size_t s;
int ret;
if (cnt < sizeof(u32))
return 0;
if (sub->swm->dir == PORT_DIR_OUTPUT) {
dma_addr_t dmapos = sub->compr_addr + sub->wr_offs;
/* Size of AIO output format is double of IEC61937 */
s = cnt * 2;
dma_sync_single_for_cpu(carddev, dmapos, s, DMA_TO_DEVICE);
ret = aio_compr_send_to_hw(sub, buf, s);
dma_sync_single_for_device(carddev, dmapos, s, DMA_TO_DEVICE);
} else {
dma_addr_t dmapos = sub->compr_addr + sub->rd_offs;
s = cnt;
dma_sync_single_for_cpu(carddev, dmapos, s, DMA_FROM_DEVICE);
ret = copy_to_user(buf, sub->compr_area + sub->rd_offs, s);
dma_sync_single_for_device(carddev, dmapos, s, DMA_FROM_DEVICE);
}
if (ret)
return -EFAULT;
spin_lock_irqsave(&sub->lock, flags);
sub->threshold = 2 * bytes;
aiodma_rb_set_threshold(sub, sub->compr_bytes, 2 * bytes);
if (sub->swm->dir == PORT_DIR_OUTPUT) {
sub->wr_offs += s;
if (sub->wr_offs >= sub->compr_bytes)
sub->wr_offs -= sub->compr_bytes;
} else {
sub->rd_offs += s;
if (sub->rd_offs >= sub->compr_bytes)
sub->rd_offs -= sub->compr_bytes;
}
aiodma_rb_sync(sub, sub->compr_addr, sub->compr_bytes, bytes);
spin_unlock_irqrestore(&sub->lock, flags);
return cnt;
}
static int uniphier_aio_compr_get_caps(struct snd_compr_stream *cstream,
struct snd_compr_caps *caps)
{
caps->num_codecs = 1;
caps->min_fragment_size = AUD_MIN_FRAGMENT_SIZE;
caps->max_fragment_size = AUD_MAX_FRAGMENT_SIZE;
caps->min_fragments = AUD_MIN_FRAGMENT;
caps->max_fragments = AUD_MAX_FRAGMENT;
caps->codecs[0] = SND_AUDIOCODEC_IEC61937;
return 0;
}
static const struct snd_compr_codec_caps caps_iec = {
.num_descriptors = 1,
.descriptor[0].max_ch = 8,
.descriptor[0].num_sample_rates = 0,
.descriptor[0].num_bitrates = 0,
.descriptor[0].profiles = SND_AUDIOPROFILE_IEC61937_SPDIF,
.descriptor[0].modes = SND_AUDIOMODE_IEC_AC3 |
SND_AUDIOMODE_IEC_MPEG1 |
SND_AUDIOMODE_IEC_MP3 |
SND_AUDIOMODE_IEC_DTS,
.descriptor[0].formats = 0,
};
static int uniphier_aio_compr_get_codec_caps(struct snd_compr_stream *stream,
struct snd_compr_codec_caps *codec)
{
if (codec->codec == SND_AUDIOCODEC_IEC61937)
*codec = caps_iec;
else
return -EINVAL;
return 0;
}
const struct snd_compr_ops uniphier_aio_compr_ops = {
.open = uniphier_aio_compr_open,
.free = uniphier_aio_compr_free,
.get_params = uniphier_aio_compr_get_params,
.set_params = uniphier_aio_compr_set_params,
.trigger = uniphier_aio_compr_trigger,
.pointer = uniphier_aio_compr_pointer,
.copy = uniphier_aio_compr_copy,
.get_caps = uniphier_aio_compr_get_caps,
.get_codec_caps = uniphier_aio_compr_get_codec_caps,
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,578 @@
// SPDX-License-Identifier: GPL-2.0
//
// Socionext UniPhier AIO ALSA CPU DAI driver.
//
// Copyright (c) 2016-2018 Socionext Inc.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; version 2
// of the License.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, see <http://www.gnu.org/licenses/>.
#include <linux/clk.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/reset.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include "aio.h"
static bool is_valid_pll(struct uniphier_aio_chip *chip, int pll_id)
{
struct device *dev = &chip->pdev->dev;
if (pll_id < 0 || chip->num_plls <= pll_id) {
dev_err(dev, "PLL(%d) is not supported\n", pll_id);
return false;
}
return chip->plls[pll_id].enable;
}
static bool match_spec(const struct uniphier_aio_spec *spec,
const char *name, int dir)
{
if (dir == SNDRV_PCM_STREAM_PLAYBACK &&
spec->swm.dir != PORT_DIR_OUTPUT) {
return false;
}
if (dir == SNDRV_PCM_STREAM_CAPTURE &&
spec->swm.dir != PORT_DIR_INPUT) {
return false;
}
if (spec->name && strcmp(spec->name, name) == 0)
return true;
if (spec->gname && strcmp(spec->gname, name) == 0)
return true;
return false;
}
/**
* find_spec - find HW specification info by name
* @aio: the AIO device pointer
* @name: name of device
* @direction: the direction of substream, SNDRV_PCM_STREAM_*
*
* Find hardware specification information from list by device name. This
* information is used for telling the difference of SoCs to driver.
*
* Specification list is array of 'struct uniphier_aio_spec' which is defined
* in each drivers (see: aio-i2s.c).
*
* Return: The pointer of hardware specification of AIO if successful,
* otherwise NULL on error.
*/
static const struct uniphier_aio_spec *find_spec(struct uniphier_aio *aio,
const char *name,
int direction)
{
const struct uniphier_aio_chip_spec *chip_spec = aio->chip->chip_spec;
int i;
for (i = 0; i < chip_spec->num_specs; i++) {
const struct uniphier_aio_spec *spec = &chip_spec->specs[i];
if (match_spec(spec, name, direction))
return spec;
}
return NULL;
}
/**
* find_divider - find clock divider by frequency
* @aio: the AIO device pointer
* @pll_id: PLL ID, should be AUD_PLL_XX
* @freq: required frequency
*
* Find suitable clock divider by frequency.
*
* Return: The ID of PLL if successful, otherwise negative error value.
*/
static int find_divider(struct uniphier_aio *aio, int pll_id, unsigned int freq)
{
struct uniphier_aio_pll *pll;
int mul[] = { 1, 1, 1, 2, };
int div[] = { 2, 3, 1, 3, };
int i;
if (!is_valid_pll(aio->chip, pll_id))
return -EINVAL;
pll = &aio->chip->plls[pll_id];
for (i = 0; i < ARRAY_SIZE(mul); i++)
if (pll->freq * mul[i] / div[i] == freq)
return i;
return -ENOTSUPP;
}
static int uniphier_aio_set_sysclk(struct snd_soc_dai *dai, int clk_id,
unsigned int freq, int dir)
{
struct uniphier_aio *aio = uniphier_priv(dai);
struct device *dev = &aio->chip->pdev->dev;
bool pll_auto = false;
int pll_id, div_id;
switch (clk_id) {
case AUD_CLK_IO:
return -ENOTSUPP;
case AUD_CLK_A1:
pll_id = AUD_PLL_A1;
break;
case AUD_CLK_F1:
pll_id = AUD_PLL_F1;
break;
case AUD_CLK_A2:
pll_id = AUD_PLL_A2;
break;
case AUD_CLK_F2:
pll_id = AUD_PLL_F2;
break;
case AUD_CLK_A:
pll_id = AUD_PLL_A1;
pll_auto = true;
break;
case AUD_CLK_F:
pll_id = AUD_PLL_F1;
pll_auto = true;
break;
case AUD_CLK_APLL:
pll_id = AUD_PLL_APLL;
break;
case AUD_CLK_RX0:
pll_id = AUD_PLL_RX0;
break;
case AUD_CLK_USB0:
pll_id = AUD_PLL_USB0;
break;
case AUD_CLK_HSC0:
pll_id = AUD_PLL_HSC0;
break;
default:
dev_err(dev, "Sysclk(%d) is not supported\n", clk_id);
return -EINVAL;
}
if (pll_auto) {
for (pll_id = 0; pll_id < aio->chip->num_plls; pll_id++) {
div_id = find_divider(aio, pll_id, freq);
if (div_id >= 0) {
aio->plldiv = div_id;
break;
}
}
if (pll_id == aio->chip->num_plls) {
dev_err(dev, "Sysclk frequency is not supported(%d)\n",
freq);
return -EINVAL;
}
}
if (dir == SND_SOC_CLOCK_OUT)
aio->pll_out = pll_id;
else
aio->pll_in = pll_id;
return 0;
}
static int uniphier_aio_set_pll(struct snd_soc_dai *dai, int pll_id,
int source, unsigned int freq_in,
unsigned int freq_out)
{
struct uniphier_aio *aio = uniphier_priv(dai);
struct device *dev = &aio->chip->pdev->dev;
int ret;
if (!is_valid_pll(aio->chip, pll_id))
return -EINVAL;
if (!aio->chip->plls[pll_id].enable) {
dev_err(dev, "PLL(%d) is not implemented\n", pll_id);
return -ENOTSUPP;
}
ret = aio_chip_set_pll(aio->chip, pll_id, freq_out);
if (ret < 0)
return ret;
return 0;
}
static int uniphier_aio_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
struct uniphier_aio *aio = uniphier_priv(dai);
struct device *dev = &aio->chip->pdev->dev;
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_LEFT_J:
case SND_SOC_DAIFMT_RIGHT_J:
case SND_SOC_DAIFMT_I2S:
aio->fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
break;
default:
dev_err(dev, "Format is not supported(%d)\n",
fmt & SND_SOC_DAIFMT_FORMAT_MASK);
return -EINVAL;
}
return 0;
}
static int uniphier_aio_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct uniphier_aio *aio = uniphier_priv(dai);
struct uniphier_aio_sub *sub = &aio->sub[substream->stream];
int ret;
sub->substream = substream;
sub->pass_through = 0;
sub->use_mmap = true;
ret = aio_init(sub);
if (ret)
return ret;
return 0;
}
static void uniphier_aio_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct uniphier_aio *aio = uniphier_priv(dai);
struct uniphier_aio_sub *sub = &aio->sub[substream->stream];
sub->substream = NULL;
}
static int uniphier_aio_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct uniphier_aio *aio = uniphier_priv(dai);
struct uniphier_aio_sub *sub = &aio->sub[substream->stream];
struct device *dev = &aio->chip->pdev->dev;
int freq, ret;
switch (params_rate(params)) {
case 48000:
case 32000:
case 24000:
freq = 12288000;
break;
case 44100:
case 22050:
freq = 11289600;
break;
default:
dev_err(dev, "Rate is not supported(%d)\n",
params_rate(params));
return -EINVAL;
}
ret = snd_soc_dai_set_sysclk(dai, AUD_CLK_A,
freq, SND_SOC_CLOCK_OUT);
if (ret)
return ret;
sub->params = *params;
sub->setting = 1;
aio_port_reset(sub);
aio_src_reset(sub);
return 0;
}
static int uniphier_aio_hw_free(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct uniphier_aio *aio = uniphier_priv(dai);
struct uniphier_aio_sub *sub = &aio->sub[substream->stream];
sub->setting = 0;
return 0;
}
static int uniphier_aio_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct uniphier_aio *aio = uniphier_priv(dai);
struct uniphier_aio_sub *sub = &aio->sub[substream->stream];
int ret;
ret = aio_port_set_param(sub, sub->pass_through, &sub->params);
if (ret)
return ret;
ret = aio_src_set_param(sub, &sub->params);
if (ret)
return ret;
aio_port_set_enable(sub, 1);
ret = aio_if_set_param(sub, sub->pass_through);
if (ret)
return ret;
if (sub->swm->type == PORT_TYPE_CONV) {
ret = aio_srcif_set_param(sub);
if (ret)
return ret;
ret = aio_srcch_set_param(sub);
if (ret)
return ret;
aio_srcch_set_enable(sub, 1);
}
return 0;
}
const struct snd_soc_dai_ops uniphier_aio_i2s_ops = {
.set_sysclk = uniphier_aio_set_sysclk,
.set_pll = uniphier_aio_set_pll,
.set_fmt = uniphier_aio_set_fmt,
.startup = uniphier_aio_startup,
.shutdown = uniphier_aio_shutdown,
.hw_params = uniphier_aio_hw_params,
.hw_free = uniphier_aio_hw_free,
.prepare = uniphier_aio_prepare,
};
EXPORT_SYMBOL_GPL(uniphier_aio_i2s_ops);
const struct snd_soc_dai_ops uniphier_aio_spdif_ops = {
.set_sysclk = uniphier_aio_set_sysclk,
.set_pll = uniphier_aio_set_pll,
.startup = uniphier_aio_startup,
.shutdown = uniphier_aio_shutdown,
.hw_params = uniphier_aio_hw_params,
.hw_free = uniphier_aio_hw_free,
.prepare = uniphier_aio_prepare,
};
EXPORT_SYMBOL_GPL(uniphier_aio_spdif_ops);
int uniphier_aio_dai_probe(struct snd_soc_dai *dai)
{
struct uniphier_aio *aio = uniphier_priv(dai);
int i;
for (i = 0; i < ARRAY_SIZE(aio->sub); i++) {
struct uniphier_aio_sub *sub = &aio->sub[i];
const struct uniphier_aio_spec *spec;
spec = find_spec(aio, dai->name, i);
if (!spec)
continue;
sub->swm = &spec->swm;
sub->spec = spec;
}
aio_iecout_set_enable(aio->chip, true);
aio_chip_init(aio->chip);
aio->chip->active = 1;
return 0;
}
EXPORT_SYMBOL_GPL(uniphier_aio_dai_probe);
int uniphier_aio_dai_remove(struct snd_soc_dai *dai)
{
struct uniphier_aio *aio = uniphier_priv(dai);
aio->chip->active = 0;
return 0;
}
EXPORT_SYMBOL_GPL(uniphier_aio_dai_remove);
int uniphier_aio_dai_suspend(struct snd_soc_dai *dai)
{
struct uniphier_aio *aio = uniphier_priv(dai);
reset_control_assert(aio->chip->rst);
clk_disable_unprepare(aio->chip->clk);
return 0;
}
EXPORT_SYMBOL_GPL(uniphier_aio_dai_suspend);
int uniphier_aio_dai_resume(struct snd_soc_dai *dai)
{
struct uniphier_aio *aio = uniphier_priv(dai);
int ret, i;
if (!aio->chip->active)
return 0;
ret = clk_prepare_enable(aio->chip->clk);
if (ret)
return ret;
ret = reset_control_deassert(aio->chip->rst);
if (ret)
goto err_out_clock;
aio_iecout_set_enable(aio->chip, true);
aio_chip_init(aio->chip);
for (i = 0; i < ARRAY_SIZE(aio->sub); i++) {
struct uniphier_aio_sub *sub = &aio->sub[i];
if (!sub->spec || !sub->substream)
continue;
ret = aio_init(sub);
if (ret)
goto err_out_clock;
if (!sub->setting)
continue;
aio_port_reset(sub);
aio_src_reset(sub);
}
return 0;
err_out_clock:
clk_disable_unprepare(aio->chip->clk);
return ret;
}
EXPORT_SYMBOL_GPL(uniphier_aio_dai_resume);
static const struct snd_soc_component_driver uniphier_aio_component = {
.name = "uniphier-aio",
};
int uniphier_aio_probe(struct platform_device *pdev)
{
struct uniphier_aio_chip *chip;
struct device *dev = &pdev->dev;
int ret, i, j;
chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
if (!chip)
return -ENOMEM;
chip->chip_spec = of_device_get_match_data(dev);
if (!chip->chip_spec)
return -EINVAL;
chip->regmap_sg = syscon_regmap_lookup_by_phandle(dev->of_node,
"socionext,syscon");
if (IS_ERR(chip->regmap_sg)) {
if (PTR_ERR(chip->regmap_sg) == -EPROBE_DEFER)
return -EPROBE_DEFER;
chip->regmap_sg = NULL;
}
chip->clk = devm_clk_get(dev, "aio");
if (IS_ERR(chip->clk))
return PTR_ERR(chip->clk);
chip->rst = devm_reset_control_get_shared(dev, "aio");
if (IS_ERR(chip->rst))
return PTR_ERR(chip->rst);
chip->num_aios = chip->chip_spec->num_dais;
chip->aios = devm_kzalloc(dev,
sizeof(struct uniphier_aio) * chip->num_aios,
GFP_KERNEL);
if (!chip->aios)
return -ENOMEM;
chip->num_plls = chip->chip_spec->num_plls;
chip->plls = devm_kzalloc(dev, sizeof(struct uniphier_aio_pll) *
chip->num_plls, GFP_KERNEL);
if (!chip->plls)
return -ENOMEM;
memcpy(chip->plls, chip->chip_spec->plls,
sizeof(struct uniphier_aio_pll) * chip->num_plls);
for (i = 0; i < chip->num_aios; i++) {
struct uniphier_aio *aio = &chip->aios[i];
aio->chip = chip;
aio->fmt = SND_SOC_DAIFMT_I2S;
for (j = 0; j < ARRAY_SIZE(aio->sub); j++) {
struct uniphier_aio_sub *sub = &aio->sub[j];
sub->aio = aio;
spin_lock_init(&sub->lock);
}
}
chip->pdev = pdev;
platform_set_drvdata(pdev, chip);
ret = clk_prepare_enable(chip->clk);
if (ret)
return ret;
ret = reset_control_deassert(chip->rst);
if (ret)
goto err_out_clock;
ret = devm_snd_soc_register_component(dev, &uniphier_aio_component,
chip->chip_spec->dais,
chip->chip_spec->num_dais);
if (ret) {
dev_err(dev, "Register component failed.\n");
goto err_out_reset;
}
ret = uniphier_aiodma_soc_register_platform(pdev);
if (ret) {
dev_err(dev, "Register platform failed.\n");
goto err_out_reset;
}
return 0;
err_out_reset:
reset_control_assert(chip->rst);
err_out_clock:
clk_disable_unprepare(chip->clk);
return ret;
}
EXPORT_SYMBOL_GPL(uniphier_aio_probe);
int uniphier_aio_remove(struct platform_device *pdev)
{
struct uniphier_aio_chip *chip = platform_get_drvdata(pdev);
reset_control_assert(chip->rst);
clk_disable_unprepare(chip->clk);
return 0;
}
EXPORT_SYMBOL_GPL(uniphier_aio_remove);
MODULE_AUTHOR("Katsuhiro Suzuki <suzuki.katsuhiro@socionext.com>");
MODULE_DESCRIPTION("UniPhier AIO CPU DAI driver.");
MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,319 @@
// SPDX-License-Identifier: GPL-2.0
//
// Socionext UniPhier AIO DMA driver.
//
// Copyright (c) 2016-2018 Socionext Inc.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; version 2
// of the License.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, see <http://www.gnu.org/licenses/>.
#include <linux/dma-mapping.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/soc.h>
#include "aio.h"
static struct snd_pcm_hardware uniphier_aiodma_hw = {
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED,
.period_bytes_min = 256,
.period_bytes_max = 4096,
.periods_min = 4,
.periods_max = 1024,
.buffer_bytes_max = 128 * 1024,
};
static void aiodma_pcm_irq(struct uniphier_aio_sub *sub)
{
struct snd_pcm_runtime *runtime = sub->substream->runtime;
int bytes = runtime->period_size *
runtime->channels * samples_to_bytes(runtime, 1);
int ret;
spin_lock(&sub->lock);
ret = aiodma_rb_set_threshold(sub, runtime->dma_bytes,
sub->threshold + bytes);
if (!ret)
sub->threshold += bytes;
aiodma_rb_sync(sub, runtime->dma_addr, runtime->dma_bytes, bytes);
aiodma_rb_clear_irq(sub);
spin_unlock(&sub->lock);
snd_pcm_period_elapsed(sub->substream);
}
static void aiodma_compr_irq(struct uniphier_aio_sub *sub)
{
struct snd_compr_runtime *runtime = sub->cstream->runtime;
int bytes = runtime->fragment_size;
int ret;
spin_lock(&sub->lock);
ret = aiodma_rb_set_threshold(sub, sub->compr_bytes,
sub->threshold + bytes);
if (!ret)
sub->threshold += bytes;
aiodma_rb_sync(sub, sub->compr_addr, sub->compr_bytes, bytes);
aiodma_rb_clear_irq(sub);
spin_unlock(&sub->lock);
snd_compr_fragment_elapsed(sub->cstream);
}
static irqreturn_t aiodma_irq(int irq, void *p)
{
struct platform_device *pdev = p;
struct uniphier_aio_chip *chip = platform_get_drvdata(pdev);
irqreturn_t ret = IRQ_NONE;
int i, j;
for (i = 0; i < chip->num_aios; i++) {
struct uniphier_aio *aio = &chip->aios[i];
for (j = 0; j < ARRAY_SIZE(aio->sub); j++) {
struct uniphier_aio_sub *sub = &aio->sub[j];
/* Skip channel that does not trigger */
if (!sub->running || !aiodma_rb_is_irq(sub))
continue;
if (sub->substream)
aiodma_pcm_irq(sub);
if (sub->cstream)
aiodma_compr_irq(sub);
ret = IRQ_HANDLED;
}
}
return ret;
}
static int uniphier_aiodma_open(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
snd_soc_set_runtime_hwparams(substream, &uniphier_aiodma_hw);
return snd_pcm_hw_constraint_step(runtime, 0,
SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 256);
}
static int uniphier_aiodma_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
substream->runtime->dma_bytes = params_buffer_bytes(params);
return 0;
}
static int uniphier_aiodma_hw_free(struct snd_pcm_substream *substream)
{
snd_pcm_set_runtime_buffer(substream, NULL);
substream->runtime->dma_bytes = 0;
return 0;
}
static int uniphier_aiodma_prepare(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
struct uniphier_aio *aio = uniphier_priv(rtd->cpu_dai);
struct uniphier_aio_sub *sub = &aio->sub[substream->stream];
int bytes = runtime->period_size *
runtime->channels * samples_to_bytes(runtime, 1);
unsigned long flags;
int ret;
ret = aiodma_ch_set_param(sub);
if (ret)
return ret;
spin_lock_irqsave(&sub->lock, flags);
ret = aiodma_rb_set_buffer(sub, runtime->dma_addr,
runtime->dma_addr + runtime->dma_bytes,
bytes);
spin_unlock_irqrestore(&sub->lock, flags);
if (ret)
return ret;
return 0;
}
static int uniphier_aiodma_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
struct uniphier_aio *aio = uniphier_priv(rtd->cpu_dai);
struct uniphier_aio_sub *sub = &aio->sub[substream->stream];
struct device *dev = &aio->chip->pdev->dev;
int bytes = runtime->period_size *
runtime->channels * samples_to_bytes(runtime, 1);
unsigned long flags;
spin_lock_irqsave(&sub->lock, flags);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
aiodma_rb_sync(sub, runtime->dma_addr, runtime->dma_bytes,
bytes);
aiodma_ch_set_enable(sub, 1);
sub->running = 1;
break;
case SNDRV_PCM_TRIGGER_STOP:
sub->running = 0;
aiodma_ch_set_enable(sub, 0);
break;
default:
dev_warn(dev, "Unknown trigger(%d) ignored\n", cmd);
break;
}
spin_unlock_irqrestore(&sub->lock, flags);
return 0;
}
static snd_pcm_uframes_t uniphier_aiodma_pointer(
struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
struct uniphier_aio *aio = uniphier_priv(rtd->cpu_dai);
struct uniphier_aio_sub *sub = &aio->sub[substream->stream];
int bytes = runtime->period_size *
runtime->channels * samples_to_bytes(runtime, 1);
unsigned long flags;
snd_pcm_uframes_t pos;
spin_lock_irqsave(&sub->lock, flags);
aiodma_rb_sync(sub, runtime->dma_addr, runtime->dma_bytes, bytes);
if (sub->swm->dir == PORT_DIR_OUTPUT)
pos = bytes_to_frames(runtime, sub->rd_offs);
else
pos = bytes_to_frames(runtime, sub->wr_offs);
spin_unlock_irqrestore(&sub->lock, flags);
return pos;
}
static int uniphier_aiodma_mmap(struct snd_pcm_substream *substream,
struct vm_area_struct *vma)
{
vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
return remap_pfn_range(vma, vma->vm_start,
substream->dma_buffer.addr >> PAGE_SHIFT,
vma->vm_end - vma->vm_start, vma->vm_page_prot);
}
static const struct snd_pcm_ops uniphier_aiodma_ops = {
.open = uniphier_aiodma_open,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = uniphier_aiodma_hw_params,
.hw_free = uniphier_aiodma_hw_free,
.prepare = uniphier_aiodma_prepare,
.trigger = uniphier_aiodma_trigger,
.pointer = uniphier_aiodma_pointer,
.mmap = uniphier_aiodma_mmap,
};
static int uniphier_aiodma_new(struct snd_soc_pcm_runtime *rtd)
{
struct device *dev = rtd->card->snd_card->dev;
struct snd_pcm *pcm = rtd->pcm;
int ret;
ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(33));
if (ret)
return ret;
return snd_pcm_lib_preallocate_pages_for_all(pcm,
SNDRV_DMA_TYPE_DEV, dev,
uniphier_aiodma_hw.buffer_bytes_max,
uniphier_aiodma_hw.buffer_bytes_max);
}
static void uniphier_aiodma_free(struct snd_pcm *pcm)
{
snd_pcm_lib_preallocate_free_for_all(pcm);
}
static const struct snd_soc_component_driver uniphier_soc_platform = {
.pcm_new = uniphier_aiodma_new,
.pcm_free = uniphier_aiodma_free,
.ops = &uniphier_aiodma_ops,
.compr_ops = &uniphier_aio_compr_ops,
};
static const struct regmap_config aiodma_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.max_register = 0x7fffc,
.cache_type = REGCACHE_NONE,
};
/**
* uniphier_aiodma_soc_register_platform - register the AIO DMA
* @pdev: the platform device
*
* Register and setup the DMA of AIO to transfer the sound data to device.
* This function need to call once at driver startup and need NOT to call
* unregister function.
*
* Return: Zero if successful, otherwise a negative value on error.
*/
int uniphier_aiodma_soc_register_platform(struct platform_device *pdev)
{
struct uniphier_aio_chip *chip = platform_get_drvdata(pdev);
struct device *dev = &pdev->dev;
struct resource *res;
void __iomem *preg;
int irq, ret;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
preg = devm_ioremap_resource(dev, res);
if (IS_ERR(preg))
return PTR_ERR(preg);
chip->regmap = devm_regmap_init_mmio(dev, preg,
&aiodma_regmap_config);
if (IS_ERR(chip->regmap))
return PTR_ERR(chip->regmap);
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(dev, "Could not get irq.\n");
return irq;
}
ret = devm_request_irq(dev, irq, aiodma_irq,
IRQF_SHARED, dev_name(dev), pdev);
if (ret)
return ret;
return devm_snd_soc_register_component(dev, &uniphier_soc_platform,
NULL, 0);
}
EXPORT_SYMBOL_GPL(uniphier_aiodma_soc_register_platform);

View File

@ -0,0 +1,431 @@
// SPDX-License-Identifier: GPL-2.0
//
// Socionext UniPhier AIO ALSA driver for LD11/LD20.
//
// Copyright (c) 2016-2018 Socionext Inc.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; version 2
// of the License.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, see <http://www.gnu.org/licenses/>.
#include <linux/module.h>
#include "aio.h"
static const struct uniphier_aio_spec uniphier_aio_ld11[] = {
/* for HDMI PCM In, Pin:AI1Dx */
{
.name = AUD_NAME_PCMIN1,
.gname = AUD_GNAME_HDMI,
.swm = {
.type = PORT_TYPE_I2S,
.dir = PORT_DIR_INPUT,
.rb = { 21, 14, },
.ch = { 21, 14, },
.iif = { 5, 3, },
.iport = { 0, AUD_HW_PCMIN1, },
},
},
/* for SIF In, Pin:AI2Dx */
{
.name = AUD_NAME_PCMIN2,
.swm = {
.type = PORT_TYPE_I2S,
.dir = PORT_DIR_INPUT,
.rb = { 22, 15, },
.ch = { 22, 15, },
.iif = { 6, 4, },
.iport = { 1, AUD_HW_PCMIN2, },
},
},
/* for Line In, Pin:AI3Dx */
{
.name = AUD_NAME_PCMIN3,
.gname = AUD_GNAME_LINE,
.swm = {
.type = PORT_TYPE_EVE,
.dir = PORT_DIR_INPUT,
.rb = { 23, 16, },
.ch = { 23, 16, },
.iif = { 7, 5, },
.iport = { 2, AUD_HW_PCMIN3, },
},
},
/* for S/PDIF In, Pin:AI1IEC */
{
.name = AUD_NAME_IECIN1,
.gname = AUD_GNAME_IEC,
.swm = {
.type = PORT_TYPE_SPDIF,
.dir = PORT_DIR_INPUT,
.rb = { 26, 17, },
.ch = { 26, 17, },
.iif = { 10, 6, },
.iport = { 3, AUD_HW_IECIN1, },
},
},
/* for Speaker, Pin:AO1Dx */
{
.name = AUD_NAME_HPCMOUT1,
.swm = {
.type = PORT_TYPE_I2S,
.dir = PORT_DIR_OUTPUT,
.rb = { 0, 0, },
.ch = { 0, 0, },
.oif = { 0, 0, },
.oport = { 0, AUD_HW_HPCMOUT1, },
},
},
/* for HDMI PCM, Pin:AO2Dx */
{
.name = AUD_NAME_PCMOUT1,
.gname = AUD_GNAME_HDMI,
.swm = {
.type = PORT_TYPE_I2S,
.dir = PORT_DIR_OUTPUT,
.rb = { 0, 0, },
.ch = { 0, 0, },
.oif = { 0, 0, },
.oport = { 3, AUD_HW_PCMOUT1, },
},
},
/* for Line Out, Pin:LO2_x */
{
.name = AUD_NAME_PCMOUT2,
.gname = AUD_GNAME_LINE,
.swm = {
.type = PORT_TYPE_EVE,
.dir = PORT_DIR_OUTPUT,
.rb = { 2, 2, },
.ch = { 2, 2, },
.oif = { 2, 2, },
.oport = { 1, AUD_HW_PCMOUT2, },
},
},
/* for Headphone, Pin:HP1_x */
{
.name = AUD_NAME_PCMOUT3,
.swm = {
.type = PORT_TYPE_EVE,
.dir = PORT_DIR_OUTPUT,
.rb = { 3, 3, },
.ch = { 3, 3, },
.oif = { 3, 3, },
.oport = { 2, AUD_HW_PCMOUT3, },
},
},
/* for HW Sampling Rate Converter */
{
.name = AUD_NAME_EPCMOUT2,
.swm = {
.type = PORT_TYPE_CONV,
.dir = PORT_DIR_OUTPUT,
.rb = { 7, 5, },
.ch = { 7, 5, },
.oif = { 7, 5, },
.oport = { 6, AUD_HW_EPCMOUT2, },
.och = { 17, 12, },
.iif = { 1, 1, },
},
},
/* for HW Sampling Rate Converter 2 */
{
.name = AUD_NAME_EPCMOUT3,
.swm = {
.type = PORT_TYPE_CONV,
.dir = PORT_DIR_OUTPUT,
.rb = { 8, 6, },
.ch = { 8, 6, },
.oif = { 8, 6, },
.oport = { 7, AUD_HW_EPCMOUT3, },
.och = { 18, 13, },
.iif = { 2, 2, },
},
},
/* for S/PDIF Out, Pin:AO1IEC */
{
.name = AUD_NAME_HIECOUT1,
.gname = AUD_GNAME_IEC,
.swm = {
.type = PORT_TYPE_SPDIF,
.dir = PORT_DIR_OUTPUT,
.rb = { 1, 1, },
.ch = { 1, 1, },
.oif = { 1, 1, },
.oport = { 12, AUD_HW_HIECOUT1, },
},
},
/* for S/PDIF Out, Pin:AO1IEC, Compress */
{
.name = AUD_NAME_HIECCOMPOUT1,
.gname = AUD_GNAME_IEC,
.swm = {
.type = PORT_TYPE_SPDIF,
.dir = PORT_DIR_OUTPUT,
.rb = { 1, 1, },
.ch = { 1, 1, },
.oif = { 1, 1, },
.oport = { 12, AUD_HW_HIECOUT1, },
},
},
};
static const struct uniphier_aio_pll uniphier_aio_pll_ld11[] = {
[AUD_PLL_A1] = { .enable = true, },
[AUD_PLL_F1] = { .enable = true, },
[AUD_PLL_A2] = { .enable = true, },
[AUD_PLL_F2] = { .enable = true, },
[AUD_PLL_APLL] = { .enable = true, },
[AUD_PLL_RX0] = { .enable = true, },
[AUD_PLL_USB0] = { .enable = true, },
[AUD_PLL_HSC0] = { .enable = true, },
};
static int uniphier_aio_ld11_probe(struct snd_soc_dai *dai)
{
int ret;
ret = uniphier_aio_dai_probe(dai);
if (ret < 0)
return ret;
ret = snd_soc_dai_set_pll(dai, AUD_PLL_A1, 0, 0, 36864000);
if (ret < 0)
return ret;
ret = snd_soc_dai_set_pll(dai, AUD_PLL_F1, 0, 0, 36864000);
if (ret < 0)
return ret;
ret = snd_soc_dai_set_pll(dai, AUD_PLL_A2, 0, 0, 33868800);
if (ret < 0)
return ret;
ret = snd_soc_dai_set_pll(dai, AUD_PLL_F2, 0, 0, 33868800);
if (ret < 0)
return ret;
return 0;
}
static struct snd_soc_dai_driver uniphier_aio_dai_ld11[] = {
{
.name = AUD_GNAME_HDMI,
.probe = uniphier_aio_ld11_probe,
.remove = uniphier_aio_dai_remove,
.suspend = uniphier_aio_dai_suspend,
.resume = uniphier_aio_dai_resume,
.playback = {
.stream_name = AUD_NAME_PCMOUT1,
.formats = SNDRV_PCM_FMTBIT_S32_LE,
.rates = SNDRV_PCM_RATE_48000,
.channels_min = 2,
.channels_max = 2,
},
.capture = {
.stream_name = AUD_NAME_PCMIN1,
.formats = SNDRV_PCM_FMTBIT_S32_LE,
.rates = SNDRV_PCM_RATE_48000 |
SNDRV_PCM_RATE_44100 |
SNDRV_PCM_RATE_32000,
.channels_min = 2,
.channels_max = 2,
},
.ops = &uniphier_aio_i2s_ops,
},
{
.name = AUD_NAME_PCMIN2,
.probe = uniphier_aio_ld11_probe,
.remove = uniphier_aio_dai_remove,
.suspend = uniphier_aio_dai_suspend,
.resume = uniphier_aio_dai_resume,
.capture = {
.stream_name = AUD_NAME_PCMIN2,
.formats = SNDRV_PCM_FMTBIT_S32_LE,
.rates = SNDRV_PCM_RATE_48000,
.channels_min = 2,
.channels_max = 2,
},
.ops = &uniphier_aio_i2s_ops,
},
{
.name = AUD_GNAME_LINE,
.probe = uniphier_aio_ld11_probe,
.remove = uniphier_aio_dai_remove,
.suspend = uniphier_aio_dai_suspend,
.resume = uniphier_aio_dai_resume,
.playback = {
.stream_name = AUD_NAME_PCMOUT2,
.formats = SNDRV_PCM_FMTBIT_S32_LE,
.rates = SNDRV_PCM_RATE_48000,
.channels_min = 2,
.channels_max = 2,
},
.capture = {
.stream_name = AUD_NAME_PCMIN3,
.formats = SNDRV_PCM_FMTBIT_S32_LE,
.rates = SNDRV_PCM_RATE_48000,
.channels_min = 2,
.channels_max = 2,
},
.ops = &uniphier_aio_i2s_ops,
},
{
.name = AUD_NAME_HPCMOUT1,
.probe = uniphier_aio_ld11_probe,
.remove = uniphier_aio_dai_remove,
.suspend = uniphier_aio_dai_suspend,
.resume = uniphier_aio_dai_resume,
.playback = {
.stream_name = AUD_NAME_HPCMOUT1,
.formats = SNDRV_PCM_FMTBIT_S32_LE,
.rates = SNDRV_PCM_RATE_48000,
.channels_min = 2,
.channels_max = 2,
},
.ops = &uniphier_aio_i2s_ops,
},
{
.name = AUD_NAME_PCMOUT3,
.probe = uniphier_aio_ld11_probe,
.remove = uniphier_aio_dai_remove,
.suspend = uniphier_aio_dai_suspend,
.resume = uniphier_aio_dai_resume,
.playback = {
.stream_name = AUD_NAME_PCMOUT3,
.formats = SNDRV_PCM_FMTBIT_S32_LE,
.rates = SNDRV_PCM_RATE_48000,
.channels_min = 2,
.channels_max = 2,
},
.ops = &uniphier_aio_i2s_ops,
},
{
.name = AUD_NAME_HIECOUT1,
.probe = uniphier_aio_ld11_probe,
.remove = uniphier_aio_dai_remove,
.suspend = uniphier_aio_dai_suspend,
.resume = uniphier_aio_dai_resume,
.playback = {
.stream_name = AUD_NAME_HIECOUT1,
.formats = SNDRV_PCM_FMTBIT_S32_LE,
.rates = SNDRV_PCM_RATE_48000,
.channels_min = 2,
.channels_max = 2,
},
.ops = &uniphier_aio_spdif_ops,
},
{
.name = AUD_NAME_EPCMOUT2,
.probe = uniphier_aio_ld11_probe,
.remove = uniphier_aio_dai_remove,
.suspend = uniphier_aio_dai_suspend,
.resume = uniphier_aio_dai_resume,
.playback = {
.stream_name = AUD_NAME_EPCMOUT2,
.formats = SNDRV_PCM_FMTBIT_S32_LE,
.rates = SNDRV_PCM_RATE_48000 |
SNDRV_PCM_RATE_44100 |
SNDRV_PCM_RATE_32000,
.channels_min = 2,
.channels_max = 2,
},
.ops = &uniphier_aio_i2s_ops,
},
{
.name = AUD_NAME_EPCMOUT3,
.probe = uniphier_aio_ld11_probe,
.remove = uniphier_aio_dai_remove,
.suspend = uniphier_aio_dai_suspend,
.resume = uniphier_aio_dai_resume,
.playback = {
.stream_name = AUD_NAME_EPCMOUT3,
.formats = SNDRV_PCM_FMTBIT_S32_LE,
.rates = SNDRV_PCM_RATE_48000 |
SNDRV_PCM_RATE_44100 |
SNDRV_PCM_RATE_32000,
.channels_min = 2,
.channels_max = 2,
},
.ops = &uniphier_aio_i2s_ops,
},
{
.name = AUD_NAME_HIECCOMPOUT1,
.probe = uniphier_aio_ld11_probe,
.remove = uniphier_aio_dai_remove,
.suspend = uniphier_aio_dai_suspend,
.resume = uniphier_aio_dai_resume,
.compress_new = snd_soc_new_compress,
.playback = {
.stream_name = AUD_NAME_HIECCOMPOUT1,
.channels_min = 1,
.channels_max = 1,
},
.ops = &uniphier_aio_spdif_ops,
},
};
static const struct uniphier_aio_chip_spec uniphier_aio_ld11_spec = {
.specs = uniphier_aio_ld11,
.num_specs = ARRAY_SIZE(uniphier_aio_ld11),
.dais = uniphier_aio_dai_ld11,
.num_dais = ARRAY_SIZE(uniphier_aio_dai_ld11),
.plls = uniphier_aio_pll_ld11,
.num_plls = ARRAY_SIZE(uniphier_aio_pll_ld11),
.addr_ext = 0,
};
static const struct uniphier_aio_chip_spec uniphier_aio_ld20_spec = {
.specs = uniphier_aio_ld11,
.num_specs = ARRAY_SIZE(uniphier_aio_ld11),
.dais = uniphier_aio_dai_ld11,
.num_dais = ARRAY_SIZE(uniphier_aio_dai_ld11),
.plls = uniphier_aio_pll_ld11,
.num_plls = ARRAY_SIZE(uniphier_aio_pll_ld11),
.addr_ext = 1,
};
static const struct of_device_id uniphier_aio_of_match[] = {
{
.compatible = "socionext,uniphier-ld11-aio",
.data = &uniphier_aio_ld11_spec,
},
{
.compatible = "socionext,uniphier-ld20-aio",
.data = &uniphier_aio_ld20_spec,
},
{},
};
MODULE_DEVICE_TABLE(of, uniphier_aio_of_match);
static struct platform_driver uniphier_aio_driver = {
.driver = {
.name = "snd-uniphier-aio-ld11",
.of_match_table = of_match_ptr(uniphier_aio_of_match),
},
.probe = uniphier_aio_probe,
.remove = uniphier_aio_remove,
};
module_platform_driver(uniphier_aio_driver);
MODULE_AUTHOR("Katsuhiro Suzuki <suzuki.katsuhiro@socionext.com>");
MODULE_DESCRIPTION("UniPhier LD11/LD20 AIO driver.");
MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,320 @@
// SPDX-License-Identifier: GPL-2.0
//
// Socionext UniPhier AIO ALSA driver for PXs2.
//
// Copyright (c) 2018 Socionext Inc.
#include <linux/module.h>
#include "aio.h"
static const struct uniphier_aio_spec uniphier_aio_pxs2[] = {
/* for Line PCM In, Pin:AI1Dx */
{
.name = AUD_NAME_PCMIN1,
.gname = AUD_GNAME_LINE,
.swm = {
.type = PORT_TYPE_I2S,
.dir = PORT_DIR_INPUT,
.rb = { 16, 11, },
.ch = { 16, 11, },
.iif = { 0, 0, },
.iport = { 0, AUD_HW_PCMIN1, },
},
},
/* for Speaker/Headphone/Mic PCM In, Pin:AI2Dx */
{
.name = AUD_NAME_PCMIN2,
.gname = AUD_GNAME_AUX,
.swm = {
.type = PORT_TYPE_I2S,
.dir = PORT_DIR_INPUT,
.rb = { 17, 12, },
.ch = { 17, 12, },
.iif = { 1, 1, },
.iport = { 1, AUD_HW_PCMIN2, },
},
},
/* for HDMI PCM Out, Pin:AO1Dx (inner) */
{
.name = AUD_NAME_HPCMOUT1,
.gname = AUD_GNAME_HDMI,
.swm = {
.type = PORT_TYPE_I2S,
.dir = PORT_DIR_OUTPUT,
.rb = { 0, 0, },
.ch = { 0, 0, },
.oif = { 0, 0, },
.oport = { 3, AUD_HW_HPCMOUT1, },
},
},
/* for Line PCM Out, Pin:AO2Dx */
{
.name = AUD_NAME_PCMOUT1,
.gname = AUD_GNAME_LINE,
.swm = {
.type = PORT_TYPE_I2S,
.dir = PORT_DIR_OUTPUT,
.rb = { 1, 1, },
.ch = { 1, 1, },
.oif = { 1, 1, },
.oport = { 0, AUD_HW_PCMOUT1, },
},
},
/* for Speaker/Headphone/Mic PCM Out, Pin:AO3Dx */
{
.name = AUD_NAME_PCMOUT2,
.gname = AUD_GNAME_AUX,
.swm = {
.type = PORT_TYPE_I2S,
.dir = PORT_DIR_OUTPUT,
.rb = { 2, 2, },
.ch = { 2, 2, },
.oif = { 2, 2, },
.oport = { 1, AUD_HW_PCMOUT2, },
},
},
/* for HDMI Out, Pin:AO1IEC */
{
.name = AUD_NAME_HIECOUT1,
.swm = {
.type = PORT_TYPE_SPDIF,
.dir = PORT_DIR_OUTPUT,
.rb = { 6, 4, },
.ch = { 6, 4, },
.oif = { 6, 4, },
.oport = { 12, AUD_HW_HIECOUT1, },
},
},
/* for HDMI Out, Pin:AO1IEC, Compress */
{
.name = AUD_NAME_HIECCOMPOUT1,
.swm = {
.type = PORT_TYPE_SPDIF,
.dir = PORT_DIR_OUTPUT,
.rb = { 6, 4, },
.ch = { 6, 4, },
.oif = { 6, 4, },
.oport = { 12, AUD_HW_HIECOUT1, },
},
},
/* for S/PDIF Out, Pin:AO2IEC */
{
.name = AUD_NAME_IECOUT1,
.swm = {
.type = PORT_TYPE_SPDIF,
.dir = PORT_DIR_OUTPUT,
.rb = { 7, 5, },
.ch = { 7, 5, },
.oif = { 7, 5, },
.oport = { 13, AUD_HW_IECOUT1, },
},
},
/* for S/PDIF Out, Pin:AO2IEC */
{
.name = AUD_NAME_IECCOMPOUT1,
.swm = {
.type = PORT_TYPE_SPDIF,
.dir = PORT_DIR_OUTPUT,
.rb = { 7, 5, },
.ch = { 7, 5, },
.oif = { 7, 5, },
.oport = { 13, AUD_HW_IECOUT1, },
},
},
};
static const struct uniphier_aio_pll uniphier_aio_pll_pxs2[] = {
[AUD_PLL_A1] = { .enable = true, },
[AUD_PLL_F1] = { .enable = true, },
[AUD_PLL_A2] = { .enable = true, },
[AUD_PLL_F2] = { .enable = true, },
[AUD_PLL_APLL] = { .enable = true, },
[AUD_PLL_HSC0] = { .enable = true, },
};
static int uniphier_aio_pxs2_probe(struct snd_soc_dai *dai)
{
int ret;
ret = uniphier_aio_dai_probe(dai);
if (ret < 0)
return ret;
ret = snd_soc_dai_set_pll(dai, AUD_PLL_A1, 0, 0, 36864000);
if (ret < 0)
return ret;
ret = snd_soc_dai_set_pll(dai, AUD_PLL_F1, 0, 0, 36864000);
if (ret < 0)
return ret;
ret = snd_soc_dai_set_pll(dai, AUD_PLL_A2, 0, 0, 33868800);
if (ret < 0)
return ret;
ret = snd_soc_dai_set_pll(dai, AUD_PLL_F2, 0, 0, 33868800);
if (ret < 0)
return ret;
return 0;
}
static struct snd_soc_dai_driver uniphier_aio_dai_pxs2[] = {
{
.name = AUD_GNAME_HDMI,
.probe = uniphier_aio_pxs2_probe,
.remove = uniphier_aio_dai_remove,
.suspend = uniphier_aio_dai_suspend,
.resume = uniphier_aio_dai_resume,
.playback = {
.stream_name = AUD_NAME_HPCMOUT1,
.formats = SNDRV_PCM_FMTBIT_S32_LE,
.rates = SNDRV_PCM_RATE_48000,
.channels_min = 2,
.channels_max = 2,
},
.ops = &uniphier_aio_i2s_ops,
},
{
.name = AUD_GNAME_LINE,
.probe = uniphier_aio_pxs2_probe,
.remove = uniphier_aio_dai_remove,
.suspend = uniphier_aio_dai_suspend,
.resume = uniphier_aio_dai_resume,
.playback = {
.stream_name = AUD_NAME_PCMOUT1,
.formats = SNDRV_PCM_FMTBIT_S32_LE,
.rates = SNDRV_PCM_RATE_48000,
.channels_min = 2,
.channels_max = 2,
},
.capture = {
.stream_name = AUD_NAME_PCMIN1,
.formats = SNDRV_PCM_FMTBIT_S32_LE,
.rates = SNDRV_PCM_RATE_48000,
.channels_min = 2,
.channels_max = 2,
},
.ops = &uniphier_aio_i2s_ops,
},
{
.name = AUD_GNAME_AUX,
.probe = uniphier_aio_pxs2_probe,
.remove = uniphier_aio_dai_remove,
.suspend = uniphier_aio_dai_suspend,
.resume = uniphier_aio_dai_resume,
.playback = {
.stream_name = AUD_NAME_PCMOUT2,
.formats = SNDRV_PCM_FMTBIT_S32_LE,
.rates = SNDRV_PCM_RATE_48000,
.channels_min = 2,
.channels_max = 2,
},
.capture = {
.stream_name = AUD_NAME_PCMIN2,
.formats = SNDRV_PCM_FMTBIT_S32_LE,
.rates = SNDRV_PCM_RATE_48000,
.channels_min = 2,
.channels_max = 2,
},
.ops = &uniphier_aio_i2s_ops,
},
{
.name = AUD_NAME_HIECOUT1,
.probe = uniphier_aio_pxs2_probe,
.remove = uniphier_aio_dai_remove,
.suspend = uniphier_aio_dai_suspend,
.resume = uniphier_aio_dai_resume,
.playback = {
.stream_name = AUD_NAME_HIECOUT1,
.formats = SNDRV_PCM_FMTBIT_S32_LE,
.rates = SNDRV_PCM_RATE_48000,
.channels_min = 2,
.channels_max = 2,
},
.ops = &uniphier_aio_spdif_ops,
},
{
.name = AUD_NAME_IECOUT1,
.probe = uniphier_aio_pxs2_probe,
.remove = uniphier_aio_dai_remove,
.suspend = uniphier_aio_dai_suspend,
.resume = uniphier_aio_dai_resume,
.playback = {
.stream_name = AUD_NAME_IECOUT1,
.formats = SNDRV_PCM_FMTBIT_S32_LE,
.rates = SNDRV_PCM_RATE_48000,
.channels_min = 2,
.channels_max = 2,
},
.ops = &uniphier_aio_spdif_ops,
},
{
.name = AUD_NAME_HIECCOMPOUT1,
.probe = uniphier_aio_pxs2_probe,
.remove = uniphier_aio_dai_remove,
.suspend = uniphier_aio_dai_suspend,
.resume = uniphier_aio_dai_resume,
.compress_new = snd_soc_new_compress,
.playback = {
.stream_name = AUD_NAME_HIECCOMPOUT1,
.channels_min = 1,
.channels_max = 1,
},
.ops = &uniphier_aio_spdif_ops,
},
{
.name = AUD_NAME_IECCOMPOUT1,
.probe = uniphier_aio_pxs2_probe,
.remove = uniphier_aio_dai_remove,
.suspend = uniphier_aio_dai_suspend,
.resume = uniphier_aio_dai_resume,
.compress_new = snd_soc_new_compress,
.playback = {
.stream_name = AUD_NAME_IECCOMPOUT1,
.channels_min = 1,
.channels_max = 1,
},
.ops = &uniphier_aio_spdif_ops,
},
};
static const struct uniphier_aio_chip_spec uniphier_aio_pxs2_spec = {
.specs = uniphier_aio_pxs2,
.num_specs = ARRAY_SIZE(uniphier_aio_pxs2),
.dais = uniphier_aio_dai_pxs2,
.num_dais = ARRAY_SIZE(uniphier_aio_dai_pxs2),
.plls = uniphier_aio_pll_pxs2,
.num_plls = ARRAY_SIZE(uniphier_aio_pll_pxs2),
.addr_ext = 0,
};
static const struct of_device_id uniphier_aio_of_match[] = {
{
.compatible = "socionext,uniphier-pxs2-aio",
.data = &uniphier_aio_pxs2_spec,
},
{},
};
MODULE_DEVICE_TABLE(of, uniphier_aio_of_match);
static struct platform_driver uniphier_aio_driver = {
.driver = {
.name = "snd-uniphier-aio-pxs2",
.of_match_table = of_match_ptr(uniphier_aio_of_match),
},
.probe = uniphier_aio_probe,
.remove = uniphier_aio_remove,
};
module_platform_driver(uniphier_aio_driver);
MODULE_AUTHOR("Katsuhiro Suzuki <suzuki.katsuhiro@socionext.com>");
MODULE_DESCRIPTION("UniPhier PXs2 AIO driver.");
MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,465 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Socionext UniPhier AIO ALSA driver.
*
* Copyright (c) 2016-2018 Socionext Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; version 2
* of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SND_UNIPHIER_AIO_REG_H__
#define SND_UNIPHIER_AIO_REG_H__
#include <linux/bitops.h>
/* soc-glue */
#define SG_AOUTEN 0x1c04
/* SW view */
#define A2CHNMAPCTR0(n) (0x00000 + 0x40 * (n))
#define A2RBNMAPCTR0(n) (0x01000 + 0x40 * (n))
#define A2IPORTNMAPCTR0(n) (0x02000 + 0x40 * (n))
#define A2IPORTNMAPCTR1(n) (0x02004 + 0x40 * (n))
#define A2IIFNMAPCTR0(n) (0x03000 + 0x40 * (n))
#define A2OPORTNMAPCTR0(n) (0x04000 + 0x40 * (n))
#define A2OPORTNMAPCTR1(n) (0x04004 + 0x40 * (n))
#define A2OPORTNMAPCTR2(n) (0x04008 + 0x40 * (n))
#define A2OIFNMAPCTR0(n) (0x05000 + 0x40 * (n))
#define A2ATNMAPCTR0(n) (0x06000 + 0x40 * (n))
#define MAPCTR0_EN 0x80000000
/* CTL */
#define A2APLLCTR0 0x07000
#define A2APLLCTR0_APLLXPOW_MASK GENMASK(3, 0)
#define A2APLLCTR0_APLLXPOW_PWOFF (0x0 << 0)
#define A2APLLCTR0_APLLXPOW_PWON (0xf << 0)
#define A2APLLCTR1 0x07004
#define A2APLLCTR1_APLLX_MASK 0x00010101
#define A2APLLCTR1_APLLX_36MHZ 0x00000000
#define A2APLLCTR1_APLLX_33MHZ 0x00000001
#define A2EXMCLKSEL0 0x07030
#define A2EXMCLKSEL0_EXMCLK_MASK GENMASK(2, 0)
#define A2EXMCLKSEL0_EXMCLK_OUTPUT (0x0 << 0)
#define A2EXMCLKSEL0_EXMCLK_INPUT (0x7 << 0)
#define A2SSIFSW 0x07050
#define A2CH22_2CTR 0x07054
#define A2AIOINPUTSEL 0x070e0
#define A2AIOINPUTSEL_RXSEL_PCMI1_MASK GENMASK(2, 0)
#define A2AIOINPUTSEL_RXSEL_PCMI1_HDMIRX1 (0x2 << 0)
#define A2AIOINPUTSEL_RXSEL_PCMI2_MASK GENMASK(6, 4)
#define A2AIOINPUTSEL_RXSEL_PCMI2_SIF (0x7 << 4)
#define A2AIOINPUTSEL_RXSEL_PCMI3_MASK GENMASK(10, 8)
#define A2AIOINPUTSEL_RXSEL_PCMI3_EVEA (0x1 << 8)
#define A2AIOINPUTSEL_RXSEL_IECI1_MASK GENMASK(14, 12)
#define A2AIOINPUTSEL_RXSEL_IECI1_HDMIRX1 (0x2 << 12)
#define A2AIOINPUTSEL_RXSEL_MASK (A2AIOINPUTSEL_RXSEL_PCMI1_MASK | \
A2AIOINPUTSEL_RXSEL_PCMI2_MASK | \
A2AIOINPUTSEL_RXSEL_PCMI3_MASK | \
A2AIOINPUTSEL_RXSEL_IECI1_HDMIRX1)
/* INTC */
#define INTCHIM(m) (0x9028 + 0x80 * (m))
#define INTRBIM(m) (0x9030 + 0x80 * (m))
#define INTCHID(m) (0xa028 + 0x80 * (m))
#define INTRBID(m) (0xa030 + 0x80 * (m))
/* AIN(PCMINN) */
#define IPORTMXCTR1(n) (0x22000 + 0x400 * (n))
#define IPORTMXCTR1_LRSEL_MASK GENMASK(11, 10)
#define IPORTMXCTR1_LRSEL_RIGHT (0x0 << 10)
#define IPORTMXCTR1_LRSEL_LEFT (0x1 << 10)
#define IPORTMXCTR1_LRSEL_I2S (0x2 << 10)
#define IPORTMXCTR1_OUTBITSEL_MASK (0x800003U << 8)
#define IPORTMXCTR1_OUTBITSEL_32 (0x800000U << 8)
#define IPORTMXCTR1_OUTBITSEL_24 (0x000000U << 8)
#define IPORTMXCTR1_OUTBITSEL_20 (0x000001U << 8)
#define IPORTMXCTR1_OUTBITSEL_16 (0x000002U << 8)
#define IPORTMXCTR1_CHSEL_MASK GENMASK(6, 4)
#define IPORTMXCTR1_CHSEL_ALL (0x0 << 4)
#define IPORTMXCTR1_CHSEL_D0_D2 (0x1 << 4)
#define IPORTMXCTR1_CHSEL_D0 (0x2 << 4)
#define IPORTMXCTR1_CHSEL_D1 (0x3 << 4)
#define IPORTMXCTR1_CHSEL_D2 (0x4 << 4)
#define IPORTMXCTR1_CHSEL_DMIX (0x5 << 4)
#define IPORTMXCTR1_FSSEL_MASK GENMASK(3, 0)
#define IPORTMXCTR1_FSSEL_48 (0x0 << 0)
#define IPORTMXCTR1_FSSEL_96 (0x1 << 0)
#define IPORTMXCTR1_FSSEL_192 (0x2 << 0)
#define IPORTMXCTR1_FSSEL_32 (0x3 << 0)
#define IPORTMXCTR1_FSSEL_44_1 (0x4 << 0)
#define IPORTMXCTR1_FSSEL_88_2 (0x5 << 0)
#define IPORTMXCTR1_FSSEL_176_4 (0x6 << 0)
#define IPORTMXCTR1_FSSEL_16 (0x8 << 0)
#define IPORTMXCTR1_FSSEL_22_05 (0x9 << 0)
#define IPORTMXCTR1_FSSEL_24 (0xa << 0)
#define IPORTMXCTR1_FSSEL_8 (0xb << 0)
#define IPORTMXCTR1_FSSEL_11_025 (0xc << 0)
#define IPORTMXCTR1_FSSEL_12 (0xd << 0)
#define IPORTMXCTR2(n) (0x22004 + 0x400 * (n))
#define IPORTMXCTR2_ACLKSEL_MASK GENMASK(19, 16)
#define IPORTMXCTR2_ACLKSEL_A1 (0x0 << 16)
#define IPORTMXCTR2_ACLKSEL_F1 (0x1 << 16)
#define IPORTMXCTR2_ACLKSEL_A2 (0x2 << 16)
#define IPORTMXCTR2_ACLKSEL_F2 (0x3 << 16)
#define IPORTMXCTR2_ACLKSEL_A2PLL (0x4 << 16)
#define IPORTMXCTR2_ACLKSEL_RX1 (0x5 << 16)
#define IPORTMXCTR2_ACLKSEL_RX2 (0x6 << 16)
#define IPORTMXCTR2_MSSEL_MASK BIT(15)
#define IPORTMXCTR2_MSSEL_SLAVE (0x0 << 15)
#define IPORTMXCTR2_MSSEL_MASTER (0x1 << 15)
#define IPORTMXCTR2_EXTLSIFSSEL_MASK BIT(14)
#define IPORTMXCTR2_EXTLSIFSSEL_36 (0x0 << 14)
#define IPORTMXCTR2_EXTLSIFSSEL_24 (0x1 << 14)
#define IPORTMXCTR2_DACCKSEL_MASK GENMASK(9, 8)
#define IPORTMXCTR2_DACCKSEL_1_2 (0x0 << 8)
#define IPORTMXCTR2_DACCKSEL_1_3 (0x1 << 8)
#define IPORTMXCTR2_DACCKSEL_1_1 (0x2 << 8)
#define IPORTMXCTR2_DACCKSEL_2_3 (0x3 << 8)
#define IPORTMXCTR2_REQEN_MASK BIT(0)
#define IPORTMXCTR2_REQEN_DISABLE (0x0 << 0)
#define IPORTMXCTR2_REQEN_ENABLE (0x1 << 0)
#define IPORTMXCNTCTR(n) (0x22010 + 0x400 * (n))
#define IPORTMXCOUNTER(n) (0x22014 + 0x400 * (n))
#define IPORTMXCNTMONI(n) (0x22018 + 0x400 * (n))
#define IPORTMXACLKSEL0EX(n) (0x22020 + 0x400 * (n))
#define IPORTMXACLKSEL0EX_ACLKSEL0EX_MASK GENMASK(3, 0)
#define IPORTMXACLKSEL0EX_ACLKSEL0EX_INTERNAL (0x0 << 0)
#define IPORTMXACLKSEL0EX_ACLKSEL0EX_EXTERNAL (0xf << 0)
#define IPORTMXEXNOE(n) (0x22070 + 0x400 * (n))
#define IPORTMXEXNOE_PCMINOE_MASK BIT(0)
#define IPORTMXEXNOE_PCMINOE_OUTPUT (0x0 << 0)
#define IPORTMXEXNOE_PCMINOE_INPUT (0x1 << 0)
#define IPORTMXMASK(n) (0x22078 + 0x400 * (n))
#define IPORTMXMASK_IUXCKMSK_MASK GENMASK(18, 16)
#define IPORTMXMASK_IUXCKMSK_ON (0x0 << 16)
#define IPORTMXMASK_IUXCKMSK_OFF (0x7 << 16)
#define IPORTMXMASK_XCKMSK_MASK GENMASK(2, 0)
#define IPORTMXMASK_XCKMSK_ON (0x0 << 0)
#define IPORTMXMASK_XCKMSK_OFF (0x7 << 0)
#define IPORTMXRSTCTR(n) (0x2207c + 0x400 * (n))
#define IPORTMXRSTCTR_RSTPI_MASK BIT(7)
#define IPORTMXRSTCTR_RSTPI_RELEASE (0x0 << 7)
#define IPORTMXRSTCTR_RSTPI_RESET (0x1 << 7)
/* AIN(PBinMX) */
#define PBINMXCTR(n) (0x20200 + 0x40 * (n))
#define PBINMXCTR_NCONNECT_MASK BIT(15)
#define PBINMXCTR_NCONNECT_CONNECT (0x0 << 15)
#define PBINMXCTR_NCONNECT_DISCONNECT (0x1 << 15)
#define PBINMXCTR_INOUTSEL_MASK BIT(14)
#define PBINMXCTR_INOUTSEL_IN (0x0 << 14)
#define PBINMXCTR_INOUTSEL_OUT (0x1 << 14)
#define PBINMXCTR_PBINSEL_SHIFT (8)
#define PBINMXCTR_ENDIAN_MASK GENMASK(5, 4)
#define PBINMXCTR_ENDIAN_3210 (0x0 << 4)
#define PBINMXCTR_ENDIAN_0123 (0x1 << 4)
#define PBINMXCTR_ENDIAN_1032 (0x2 << 4)
#define PBINMXCTR_ENDIAN_2301 (0x3 << 4)
#define PBINMXCTR_MEMFMT_MASK GENMASK(3, 0)
#define PBINMXCTR_MEMFMT_D0 (0x0 << 0)
#define PBINMXCTR_MEMFMT_5_1CH_DMIX (0x1 << 0)
#define PBINMXCTR_MEMFMT_6CH (0x2 << 0)
#define PBINMXCTR_MEMFMT_4CH (0x3 << 0)
#define PBINMXCTR_MEMFMT_DMIX (0x4 << 0)
#define PBINMXCTR_MEMFMT_1CH (0x5 << 0)
#define PBINMXCTR_MEMFMT_16LR (0x6 << 0)
#define PBINMXCTR_MEMFMT_7_1CH (0x7 << 0)
#define PBINMXCTR_MEMFMT_7_1CH_DMIX (0x8 << 0)
#define PBINMXCTR_MEMFMT_STREAM (0xf << 0)
#define PBINMXPAUSECTR0(n) (0x20204 + 0x40 * (n))
#define PBINMXPAUSECTR1(n) (0x20208 + 0x40 * (n))
/* AOUT */
#define AOUTENCTR0 0x40040
#define AOUTENCTR1 0x40044
#define AOUTENCTR2 0x40048
#define AOUTRSTCTR0 0x40060
#define AOUTRSTCTR1 0x40064
#define AOUTRSTCTR2 0x40068
#define AOUTSRCRSTCTR0 0x400c0
#define AOUTSRCRSTCTR1 0x400c4
#define AOUTSRCRSTCTR2 0x400c8
/* AOUT(PCMOUTN) */
#define OPORTMXCTR1(n) (0x42000 + 0x400 * (n))
#define OPORTMXCTR1_I2SLRSEL_MASK (0x11 << 10)
#define OPORTMXCTR1_I2SLRSEL_RIGHT (0x00 << 10)
#define OPORTMXCTR1_I2SLRSEL_LEFT (0x01 << 10)
#define OPORTMXCTR1_I2SLRSEL_I2S (0x11 << 10)
#define OPORTMXCTR1_OUTBITSEL_MASK (0x800003U << 8)
#define OPORTMXCTR1_OUTBITSEL_32 (0x800000U << 8)
#define OPORTMXCTR1_OUTBITSEL_24 (0x000000U << 8)
#define OPORTMXCTR1_OUTBITSEL_20 (0x000001U << 8)
#define OPORTMXCTR1_OUTBITSEL_16 (0x000002U << 8)
#define OPORTMXCTR1_FSSEL_MASK GENMASK(3, 0)
#define OPORTMXCTR1_FSSEL_48 (0x0 << 0)
#define OPORTMXCTR1_FSSEL_96 (0x1 << 0)
#define OPORTMXCTR1_FSSEL_192 (0x2 << 0)
#define OPORTMXCTR1_FSSEL_32 (0x3 << 0)
#define OPORTMXCTR1_FSSEL_44_1 (0x4 << 0)
#define OPORTMXCTR1_FSSEL_88_2 (0x5 << 0)
#define OPORTMXCTR1_FSSEL_176_4 (0x6 << 0)
#define OPORTMXCTR1_FSSEL_16 (0x8 << 0)
#define OPORTMXCTR1_FSSEL_22_05 (0x9 << 0)
#define OPORTMXCTR1_FSSEL_24 (0xa << 0)
#define OPORTMXCTR1_FSSEL_8 (0xb << 0)
#define OPORTMXCTR1_FSSEL_11_025 (0xc << 0)
#define OPORTMXCTR1_FSSEL_12 (0xd << 0)
#define OPORTMXCTR2(n) (0x42004 + 0x400 * (n))
#define OPORTMXCTR2_ACLKSEL_MASK GENMASK(19, 16)
#define OPORTMXCTR2_ACLKSEL_A1 (0x0 << 16)
#define OPORTMXCTR2_ACLKSEL_F1 (0x1 << 16)
#define OPORTMXCTR2_ACLKSEL_A2 (0x2 << 16)
#define OPORTMXCTR2_ACLKSEL_F2 (0x3 << 16)
#define OPORTMXCTR2_ACLKSEL_A2PLL (0x4 << 16)
#define OPORTMXCTR2_ACLKSEL_RX1 (0x5 << 16)
#define OPORTMXCTR2_ACLKSEL_RX2 (0x6 << 16)
#define OPORTMXCTR2_MSSEL_MASK BIT(15)
#define OPORTMXCTR2_MSSEL_SLAVE (0x0 << 15)
#define OPORTMXCTR2_MSSEL_MASTER (0x1 << 15)
#define OPORTMXCTR2_EXTLSIFSSEL_MASK BIT(14)
#define OPORTMXCTR2_EXTLSIFSSEL_36 (0x0 << 14)
#define OPORTMXCTR2_EXTLSIFSSEL_24 (0x1 << 14)
#define OPORTMXCTR2_DACCKSEL_MASK GENMASK(9, 8)
#define OPORTMXCTR2_DACCKSEL_1_2 (0x0 << 8)
#define OPORTMXCTR2_DACCKSEL_1_3 (0x1 << 8)
#define OPORTMXCTR2_DACCKSEL_1_1 (0x2 << 8)
#define OPORTMXCTR2_DACCKSEL_2_3 (0x3 << 8)
#define OPORTMXCTR3(n) (0x42008 + 0x400 * (n))
#define OPORTMXCTR3_IECTHUR_MASK BIT(19)
#define OPORTMXCTR3_IECTHUR_IECOUT (0x0 << 19)
#define OPORTMXCTR3_IECTHUR_IECIN (0x1 << 19)
#define OPORTMXCTR3_SRCSEL_MASK GENMASK(18, 16)
#define OPORTMXCTR3_SRCSEL_PCM (0x0 << 16)
#define OPORTMXCTR3_SRCSEL_STREAM (0x1 << 16)
#define OPORTMXCTR3_SRCSEL_CDDTS (0x2 << 16)
#define OPORTMXCTR3_VALID_MASK BIT(12)
#define OPORTMXCTR3_VALID_PCM (0x0 << 12)
#define OPORTMXCTR3_VALID_STREAM (0x1 << 12)
#define OPORTMXCTR3_PMSEL_MASK BIT(3)
#define OPORTMXCTR3_PMSEL_MUTE (0x0 << 3)
#define OPORTMXCTR3_PMSEL_PAUSE (0x1 << 3)
#define OPORTMXCTR3_PMSW_MASK BIT(2)
#define OPORTMXCTR3_PMSW_MUTE_OFF (0x0 << 2)
#define OPORTMXCTR3_PMSW_MUTE_ON (0x1 << 2)
#define OPORTMXSRC1CTR(n) (0x4200c + 0x400 * (n))
#define OPORTMXSRC1CTR_FSIIPNUM_SHIFT (24)
#define OPORTMXSRC1CTR_THMODE_MASK BIT(23)
#define OPORTMXSRC1CTR_THMODE_SRC (0x0 << 23)
#define OPORTMXSRC1CTR_THMODE_BYPASS (0x1 << 23)
#define OPORTMXSRC1CTR_LOCK_MASK BIT(16)
#define OPORTMXSRC1CTR_LOCK_UNLOCK (0x0 << 16)
#define OPORTMXSRC1CTR_LOCK_LOCK (0x1 << 16)
#define OPORTMXSRC1CTR_SRCPATH_MASK BIT(15)
#define OPORTMXSRC1CTR_SRCPATH_BYPASS (0x0 << 15)
#define OPORTMXSRC1CTR_SRCPATH_CALC (0x1 << 15)
#define OPORTMXSRC1CTR_SYNC_MASK BIT(14)
#define OPORTMXSRC1CTR_SYNC_ASYNC (0x0 << 14)
#define OPORTMXSRC1CTR_SYNC_SYNC (0x1 << 14)
#define OPORTMXSRC1CTR_FSOCK_MASK GENMASK(11, 10)
#define OPORTMXSRC1CTR_FSOCK_44_1 (0x0 << 10)
#define OPORTMXSRC1CTR_FSOCK_48 (0x1 << 10)
#define OPORTMXSRC1CTR_FSOCK_32 (0x2 << 10)
#define OPORTMXSRC1CTR_FSICK_MASK GENMASK(9, 8)
#define OPORTMXSRC1CTR_FSICK_44_1 (0x0 << 8)
#define OPORTMXSRC1CTR_FSICK_48 (0x1 << 8)
#define OPORTMXSRC1CTR_FSICK_32 (0x2 << 8)
#define OPORTMXSRC1CTR_FSIIPSEL_MASK GENMASK(5, 4)
#define OPORTMXSRC1CTR_FSIIPSEL_INNER (0x0 << 4)
#define OPORTMXSRC1CTR_FSIIPSEL_OUTER (0x1 << 4)
#define OPORTMXSRC1CTR_FSISEL_MASK GENMASK(3, 0)
#define OPORTMXSRC1CTR_FSISEL_ACLK (0x0 << 0)
#define OPORTMXSRC1CTR_FSISEL_DD (0x1 << 0)
#define OPORTMXDSDMUTEDAT(n) (0x42020 + 0x400 * (n))
#define OPORTMXDXDFREQMODE(n) (0x42024 + 0x400 * (n))
#define OPORTMXDSDSEL(n) (0x42028 + 0x400 * (n))
#define OPORTMXDSDPORT(n) (0x4202c + 0x400 * (n))
#define OPORTMXACLKSEL0EX(n) (0x42030 + 0x400 * (n))
#define OPORTMXPATH(n) (0x42040 + 0x400 * (n))
#define OPORTMXSYNC(n) (0x42044 + 0x400 * (n))
#define OPORTMXREPET(n) (0x42050 + 0x400 * (n))
#define OPORTMXREPET_STRLENGTH_AC3 SBF_(IEC61937_FRM_STR_AC3, 16)
#define OPORTMXREPET_STRLENGTH_MPA SBF_(IEC61937_FRM_STR_MPA, 16)
#define OPORTMXREPET_STRLENGTH_MP3 SBF_(IEC61937_FRM_STR_MP3, 16)
#define OPORTMXREPET_STRLENGTH_DTS1 SBF_(IEC61937_FRM_STR_DTS1, 16)
#define OPORTMXREPET_STRLENGTH_DTS2 SBF_(IEC61937_FRM_STR_DTS2, 16)
#define OPORTMXREPET_STRLENGTH_DTS3 SBF_(IEC61937_FRM_STR_DTS3, 16)
#define OPORTMXREPET_STRLENGTH_AAC SBF_(IEC61937_FRM_STR_AAC, 16)
#define OPORTMXREPET_PMLENGTH_AC3 SBF_(IEC61937_FRM_PAU_AC3, 0)
#define OPORTMXREPET_PMLENGTH_MPA SBF_(IEC61937_FRM_PAU_MPA, 0)
#define OPORTMXREPET_PMLENGTH_MP3 SBF_(IEC61937_FRM_PAU_MP3, 0)
#define OPORTMXREPET_PMLENGTH_DTS1 SBF_(IEC61937_FRM_PAU_DTS1, 0)
#define OPORTMXREPET_PMLENGTH_DTS2 SBF_(IEC61937_FRM_PAU_DTS2, 0)
#define OPORTMXREPET_PMLENGTH_DTS3 SBF_(IEC61937_FRM_PAU_DTS3, 0)
#define OPORTMXREPET_PMLENGTH_AAC SBF_(IEC61937_FRM_PAU_AAC, 0)
#define OPORTMXPAUDAT(n) (0x42054 + 0x400 * (n))
#define OPORTMXPAUDAT_PAUSEPC_CMN (IEC61937_PC_PAUSE << 16)
#define OPORTMXPAUDAT_PAUSEPD_AC3 (IEC61937_FRM_PAU_AC3 * 4)
#define OPORTMXPAUDAT_PAUSEPD_MPA (IEC61937_FRM_PAU_MPA * 4)
#define OPORTMXPAUDAT_PAUSEPD_MP3 (IEC61937_FRM_PAU_MP3 * 4)
#define OPORTMXPAUDAT_PAUSEPD_DTS1 (IEC61937_FRM_PAU_DTS1 * 4)
#define OPORTMXPAUDAT_PAUSEPD_DTS2 (IEC61937_FRM_PAU_DTS2 * 4)
#define OPORTMXPAUDAT_PAUSEPD_DTS3 (IEC61937_FRM_PAU_DTS3 * 4)
#define OPORTMXPAUDAT_PAUSEPD_AAC (IEC61937_FRM_PAU_AAC * 4)
#define OPORTMXRATE_I(n) (0x420e4 + 0x400 * (n))
#define OPORTMXRATE_I_EQU_MASK BIT(31)
#define OPORTMXRATE_I_EQU_NOTEQUAL (0x0 << 31)
#define OPORTMXRATE_I_EQU_EQUAL (0x1 << 31)
#define OPORTMXRATE_I_SRCBPMD_MASK BIT(29)
#define OPORTMXRATE_I_SRCBPMD_BYPASS (0x0 << 29)
#define OPORTMXRATE_I_SRCBPMD_SRC (0x1 << 29)
#define OPORTMXRATE_I_LRCKSTP_MASK BIT(24)
#define OPORTMXRATE_I_LRCKSTP_START (0x0 << 24)
#define OPORTMXRATE_I_LRCKSTP_STOP (0x1 << 24)
#define OPORTMXRATE_I_ACLKSRC_MASK GENMASK(15, 12)
#define OPORTMXRATE_I_ACLKSRC_APLL (0x0 << 12)
#define OPORTMXRATE_I_ACLKSRC_USB (0x1 << 12)
#define OPORTMXRATE_I_ACLKSRC_HSC (0x3 << 12)
/* if OPORTMXRATE_I_ACLKSRC_APLL */
#define OPORTMXRATE_I_ACLKSEL_MASK GENMASK(11, 8)
#define OPORTMXRATE_I_ACLKSEL_APLLA1 (0x0 << 8)
#define OPORTMXRATE_I_ACLKSEL_APLLF1 (0x1 << 8)
#define OPORTMXRATE_I_ACLKSEL_APLLA2 (0x2 << 8)
#define OPORTMXRATE_I_ACLKSEL_APLLF2 (0x3 << 8)
#define OPORTMXRATE_I_ACLKSEL_APLL (0x4 << 8)
#define OPORTMXRATE_I_ACLKSEL_HDMI1 (0x5 << 8)
#define OPORTMXRATE_I_ACLKSEL_HDMI2 (0x6 << 8)
#define OPORTMXRATE_I_ACLKSEL_AI1ADCCK (0xc << 8)
#define OPORTMXRATE_I_ACLKSEL_AI2ADCCK (0xd << 8)
#define OPORTMXRATE_I_ACLKSEL_AI3ADCCK (0xe << 8)
#define OPORTMXRATE_I_MCKSEL_MASK GENMASK(7, 4)
#define OPORTMXRATE_I_MCKSEL_36 (0x0 << 4)
#define OPORTMXRATE_I_MCKSEL_33 (0x1 << 4)
#define OPORTMXRATE_I_MCKSEL_HSC27 (0xb << 4)
#define OPORTMXRATE_I_FSSEL_MASK GENMASK(3, 0)
#define OPORTMXRATE_I_FSSEL_48 (0x0 << 0)
#define OPORTMXRATE_I_FSSEL_96 (0x1 << 0)
#define OPORTMXRATE_I_FSSEL_192 (0x2 << 0)
#define OPORTMXRATE_I_FSSEL_32 (0x3 << 0)
#define OPORTMXRATE_I_FSSEL_44_1 (0x4 << 0)
#define OPORTMXRATE_I_FSSEL_88_2 (0x5 << 0)
#define OPORTMXRATE_I_FSSEL_176_4 (0x6 << 0)
#define OPORTMXRATE_I_FSSEL_16 (0x8 << 0)
#define OPORTMXRATE_I_FSSEL_22_05 (0x9 << 0)
#define OPORTMXRATE_I_FSSEL_24 (0xa << 0)
#define OPORTMXRATE_I_FSSEL_8 (0xb << 0)
#define OPORTMXRATE_I_FSSEL_11_025 (0xc << 0)
#define OPORTMXRATE_I_FSSEL_12 (0xd << 0)
#define OPORTMXEXNOE(n) (0x420f0 + 0x400 * (n))
#define OPORTMXMASK(n) (0x420f8 + 0x400 * (n))
#define OPORTMXMASK_IUDXMSK_MASK GENMASK(28, 24)
#define OPORTMXMASK_IUDXMSK_ON (0x00 << 24)
#define OPORTMXMASK_IUDXMSK_OFF (0x1f << 24)
#define OPORTMXMASK_IUXCKMSK_MASK GENMASK(18, 16)
#define OPORTMXMASK_IUXCKMSK_ON (0x0 << 16)
#define OPORTMXMASK_IUXCKMSK_OFF (0x7 << 16)
#define OPORTMXMASK_DXMSK_MASK GENMASK(12, 8)
#define OPORTMXMASK_DXMSK_ON (0x00 << 8)
#define OPORTMXMASK_DXMSK_OFF (0x1f << 8)
#define OPORTMXMASK_XCKMSK_MASK GENMASK(2, 0)
#define OPORTMXMASK_XCKMSK_ON (0x0 << 0)
#define OPORTMXMASK_XCKMSK_OFF (0x7 << 0)
#define OPORTMXDEBUG(n) (0x420fc + 0x400 * (n))
#define OPORTMXT0RSTCTR(n) (0x4211c + 0x400 * (n))
#define OPORTMXT1RSTCTR(n) (0x4213c + 0x400 * (n))
#define OPORTMXT2RSTCTR(n) (0x4215c + 0x400 * (n))
#define OPORTMXT3RSTCTR(n) (0x4217c + 0x400 * (n))
#define OPORTMXT4RSTCTR(n) (0x4219c + 0x400 * (n))
#define SBF_(frame, shift) (((frame) * 2 - 1) << shift)
/* AOUT(PBoutMX) */
#define PBOUTMXCTR0(n) (0x40200 + 0x40 * (n))
#define PBOUTMXCTR0_ENDIAN_MASK GENMASK(5, 4)
#define PBOUTMXCTR0_ENDIAN_3210 (0x0 << 4)
#define PBOUTMXCTR0_ENDIAN_0123 (0x1 << 4)
#define PBOUTMXCTR0_ENDIAN_1032 (0x2 << 4)
#define PBOUTMXCTR0_ENDIAN_2301 (0x3 << 4)
#define PBOUTMXCTR0_MEMFMT_MASK GENMASK(3, 0)
#define PBOUTMXCTR0_MEMFMT_10CH (0x0 << 0)
#define PBOUTMXCTR0_MEMFMT_8CH (0x1 << 0)
#define PBOUTMXCTR0_MEMFMT_6CH (0x2 << 0)
#define PBOUTMXCTR0_MEMFMT_4CH (0x3 << 0)
#define PBOUTMXCTR0_MEMFMT_2CH (0x4 << 0)
#define PBOUTMXCTR0_MEMFMT_STREAM (0x5 << 0)
#define PBOUTMXCTR0_MEMFMT_1CH (0x6 << 0)
#define PBOUTMXCTR1(n) (0x40204 + 0x40 * (n))
#define PBOUTMXINTCTR(n) (0x40208 + 0x40 * (n))
/* A2D(subsystem) */
#define CDA2D_STRT0 0x10000
#define CDA2D_STRT0_STOP_MASK BIT(31)
#define CDA2D_STRT0_STOP_START (0x0 << 31)
#define CDA2D_STRT0_STOP_STOP (0x1 << 31)
#define CDA2D_STAT0 0x10020
#define CDA2D_TEST 0x100a0
#define CDA2D_TEST_DDR_MODE_MASK GENMASK(3, 2)
#define CDA2D_TEST_DDR_MODE_EXTON0 (0x0 << 2)
#define CDA2D_TEST_DDR_MODE_EXTOFF1 (0x3 << 2)
#define CDA2D_STRTADRSLOAD 0x100b0
#define CDA2D_CHMXCTRL1(n) (0x12000 + 0x80 * (n))
#define CDA2D_CHMXCTRL1_INDSIZE_MASK BIT(0)
#define CDA2D_CHMXCTRL1_INDSIZE_FINITE (0x0 << 0)
#define CDA2D_CHMXCTRL1_INDSIZE_INFINITE (0x1 << 0)
#define CDA2D_CHMXCTRL2(n) (0x12004 + 0x80 * (n))
#define CDA2D_CHMXSRCAMODE(n) (0x12020 + 0x80 * (n))
#define CDA2D_CHMXDSTAMODE(n) (0x12024 + 0x80 * (n))
#define CDA2D_CHMXAMODE_ENDIAN_MASK GENMASK(17, 16)
#define CDA2D_CHMXAMODE_ENDIAN_3210 (0x0 << 16)
#define CDA2D_CHMXAMODE_ENDIAN_0123 (0x1 << 16)
#define CDA2D_CHMXAMODE_ENDIAN_1032 (0x2 << 16)
#define CDA2D_CHMXAMODE_ENDIAN_2301 (0x3 << 16)
#define CDA2D_CHMXAMODE_RSSEL_SHIFT (8)
#define CDA2D_CHMXAMODE_AUPDT_MASK GENMASK(5, 4)
#define CDA2D_CHMXAMODE_AUPDT_INC (0x0 << 4)
#define CDA2D_CHMXAMODE_AUPDT_FIX (0x2 << 4)
#define CDA2D_CHMXAMODE_TYPE_MASK GENMASK(3, 2)
#define CDA2D_CHMXAMODE_TYPE_NORMAL (0x0 << 2)
#define CDA2D_CHMXAMODE_TYPE_RING (0x1 << 2)
#define CDA2D_CHMXSRCSTRTADRS(n) (0x12030 + 0x80 * (n))
#define CDA2D_CHMXSRCSTRTADRSU(n) (0x12034 + 0x80 * (n))
#define CDA2D_CHMXDSTSTRTADRS(n) (0x12038 + 0x80 * (n))
#define CDA2D_CHMXDSTSTRTADRSU(n) (0x1203c + 0x80 * (n))
/* A2D(ring buffer) */
#define CDA2D_RBFLUSH0 0x10040
#define CDA2D_RBADRSLOAD 0x100b4
#define CDA2D_RDPTRLOAD 0x100b8
#define CDA2D_RDPTRLOAD_LSFLAG_LOAD (0x0 << 31)
#define CDA2D_RDPTRLOAD_LSFLAG_STORE (0x1 << 31)
#define CDA2D_WRPTRLOAD 0x100bc
#define CDA2D_WRPTRLOAD_LSFLAG_LOAD (0x0 << 31)
#define CDA2D_WRPTRLOAD_LSFLAG_STORE (0x1 << 31)
#define CDA2D_RBMXBGNADRS(n) (0x14000 + 0x80 * (n))
#define CDA2D_RBMXBGNADRSU(n) (0x14004 + 0x80 * (n))
#define CDA2D_RBMXENDADRS(n) (0x14008 + 0x80 * (n))
#define CDA2D_RBMXENDADRSU(n) (0x1400c + 0x80 * (n))
#define CDA2D_RBMXBTH(n) (0x14038 + 0x80 * (n))
#define CDA2D_RBMXRTH(n) (0x1403c + 0x80 * (n))
#define CDA2D_RBMXRDPTR(n) (0x14020 + 0x80 * (n))
#define CDA2D_RBMXRDPTRU(n) (0x14024 + 0x80 * (n))
#define CDA2D_RBMXWRPTR(n) (0x14028 + 0x80 * (n))
#define CDA2D_RBMXWRPTRU(n) (0x1402c + 0x80 * (n))
#define CDA2D_RBMXPTRU_PTRU_MASK GENMASK(1, 0)
#define CDA2D_RBMXCNFG(n) (0x14030 + 0x80 * (n))
#define CDA2D_RBMXIR(n) (0x14014 + 0x80 * (n))
#define CDA2D_RBMXIE(n) (0x14018 + 0x80 * (n))
#define CDA2D_RBMXID(n) (0x1401c + 0x80 * (n))
#define CDA2D_RBMXIX_SPACE BIT(3)
#define CDA2D_RBMXIX_REMAIN BIT(4)
#endif /* SND_UNIPHIER_AIO_REG_H__ */

View File

@ -0,0 +1,359 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Socionext UniPhier AIO ALSA driver.
*
* Copyright (c) 2016-2018 Socionext Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; version 2
* of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SND_UNIPHIER_AIO_H__
#define SND_UNIPHIER_AIO_H__
#include <linux/spinlock.h>
#include <linux/types.h>
#include <sound/pcm.h>
#include <sound/soc.h>
#include <sound/soc-dai.h>
struct platform_device;
enum ID_PORT_TYPE {
PORT_TYPE_UNKNOWN,
PORT_TYPE_I2S,
PORT_TYPE_SPDIF,
PORT_TYPE_EVE,
PORT_TYPE_CONV,
};
enum ID_PORT_DIR {
PORT_DIR_OUTPUT,
PORT_DIR_INPUT,
};
enum IEC61937_PC {
IEC61937_PC_AC3 = 0x0001,
IEC61937_PC_PAUSE = 0x0003,
IEC61937_PC_MPA = 0x0004,
IEC61937_PC_MP3 = 0x0005,
IEC61937_PC_DTS1 = 0x000b,
IEC61937_PC_DTS2 = 0x000c,
IEC61937_PC_DTS3 = 0x000d,
IEC61937_PC_AAC = 0x0007,
};
/* IEC61937 Repetition period of data-burst in IEC60958 frames */
#define IEC61937_FRM_STR_AC3 1536
#define IEC61937_FRM_STR_MPA 1152
#define IEC61937_FRM_STR_MP3 1152
#define IEC61937_FRM_STR_DTS1 512
#define IEC61937_FRM_STR_DTS2 1024
#define IEC61937_FRM_STR_DTS3 2048
#define IEC61937_FRM_STR_AAC 1024
/* IEC61937 Repetition period of Pause data-burst in IEC60958 frames */
#define IEC61937_FRM_PAU_AC3 3
#define IEC61937_FRM_PAU_MPA 32
#define IEC61937_FRM_PAU_MP3 32
#define IEC61937_FRM_PAU_DTS1 3
#define IEC61937_FRM_PAU_DTS2 3
#define IEC61937_FRM_PAU_DTS3 3
#define IEC61937_FRM_PAU_AAC 32
/* IEC61937 Pa and Pb */
#define IEC61937_HEADER_SIGN 0x1f4e72f8
#define AUD_HW_PCMIN1 0
#define AUD_HW_PCMIN2 1
#define AUD_HW_PCMIN3 2
#define AUD_HW_IECIN1 3
#define AUD_HW_DIECIN1 4
#define AUD_NAME_PCMIN1 "aio-pcmin1"
#define AUD_NAME_PCMIN2 "aio-pcmin2"
#define AUD_NAME_PCMIN3 "aio-pcmin3"
#define AUD_NAME_IECIN1 "aio-iecin1"
#define AUD_NAME_DIECIN1 "aio-diecin1"
#define AUD_HW_HPCMOUT1 0
#define AUD_HW_PCMOUT1 1
#define AUD_HW_PCMOUT2 2
#define AUD_HW_PCMOUT3 3
#define AUD_HW_EPCMOUT1 4
#define AUD_HW_EPCMOUT2 5
#define AUD_HW_EPCMOUT3 6
#define AUD_HW_EPCMOUT6 9
#define AUD_HW_HIECOUT1 10
#define AUD_HW_IECOUT1 11
#define AUD_HW_CMASTER 31
#define AUD_NAME_HPCMOUT1 "aio-hpcmout1"
#define AUD_NAME_PCMOUT1 "aio-pcmout1"
#define AUD_NAME_PCMOUT2 "aio-pcmout2"
#define AUD_NAME_PCMOUT3 "aio-pcmout3"
#define AUD_NAME_EPCMOUT1 "aio-epcmout1"
#define AUD_NAME_EPCMOUT2 "aio-epcmout2"
#define AUD_NAME_EPCMOUT3 "aio-epcmout3"
#define AUD_NAME_EPCMOUT6 "aio-epcmout6"
#define AUD_NAME_HIECOUT1 "aio-hiecout1"
#define AUD_NAME_IECOUT1 "aio-iecout1"
#define AUD_NAME_CMASTER "aio-cmaster"
#define AUD_NAME_HIECCOMPOUT1 "aio-hieccompout1"
#define AUD_NAME_IECCOMPOUT1 "aio-ieccompout1"
#define AUD_GNAME_HDMI "aio-hdmi"
#define AUD_GNAME_LINE "aio-line"
#define AUD_GNAME_AUX "aio-aux"
#define AUD_GNAME_IEC "aio-iec"
#define AUD_CLK_IO 0
#define AUD_CLK_A1 1
#define AUD_CLK_F1 2
#define AUD_CLK_A2 3
#define AUD_CLK_F2 4
#define AUD_CLK_A 5
#define AUD_CLK_F 6
#define AUD_CLK_APLL 7
#define AUD_CLK_RX0 8
#define AUD_CLK_USB0 9
#define AUD_CLK_HSC0 10
#define AUD_PLL_A1 0
#define AUD_PLL_F1 1
#define AUD_PLL_A2 2
#define AUD_PLL_F2 3
#define AUD_PLL_APLL 4
#define AUD_PLL_RX0 5
#define AUD_PLL_USB0 6
#define AUD_PLL_HSC0 7
#define AUD_PLLDIV_1_2 0
#define AUD_PLLDIV_1_3 1
#define AUD_PLLDIV_1_1 2
#define AUD_PLLDIV_2_3 3
#define AUD_RING_SIZE (128 * 1024)
#define AUD_MIN_FRAGMENT 4
#define AUD_MAX_FRAGMENT 8
#define AUD_MIN_FRAGMENT_SIZE (4 * 1024)
#define AUD_MAX_FRAGMENT_SIZE (16 * 1024)
/*
* This is a selector for virtual register map of AIO.
*
* map: Specify the index of virtual register map.
* hw : Specify the ID of real register map, selector uses this value.
* A meaning of this value depends specification of SoC.
*/
struct uniphier_aio_selector {
int map;
int hw;
};
/**
* 'SoftWare MAPping' setting of UniPhier AIO registers.
*
* We have to setup 'virtual' register maps to access 'real' registers of AIO.
* This feature is legacy and meaningless but AIO needs this to work.
*
* Each hardware blocks have own virtual register maps as following:
*
* Address Virtual Real
* ------- --------- ---------------
* 0x12000 DMAC map0 --> [selector] --> DMAC hardware 3
* 0x12080 DMAC map1 --> [selector] --> DMAC hardware 1
* ...
* 0x42000 Port map0 --> [selector] --> Port hardware 1
* 0x42400 Port map1 --> [selector] --> Port hardware 2
* ...
*
* ch : Input or output channel of DMAC
* rb : Ring buffer
* iport: PCM input port
* iif : Input interface
* oport: PCM output port
* oif : Output interface
* och : Output channel of DMAC for sampling rate converter
*
* These are examples for sound data paths:
*
* For caputure device:
* (outer of AIO) -> iport -> iif -> ch -> rb -> (CPU)
* For playback device:
* (CPU) -> rb -> ch -> oif -> oport -> (outer of AIO)
* For sampling rate converter device:
* (CPU) -> rb -> ch -> oif -> (HW SRC) -> iif -> och -> orb -> (CPU)
*/
struct uniphier_aio_swmap {
int type;
int dir;
struct uniphier_aio_selector ch;
struct uniphier_aio_selector rb;
struct uniphier_aio_selector iport;
struct uniphier_aio_selector iif;
struct uniphier_aio_selector oport;
struct uniphier_aio_selector oif;
struct uniphier_aio_selector och;
};
struct uniphier_aio_spec {
const char *name;
const char *gname;
struct uniphier_aio_swmap swm;
};
struct uniphier_aio_pll {
bool enable;
unsigned int freq;
};
struct uniphier_aio_chip_spec {
const struct uniphier_aio_spec *specs;
int num_specs;
const struct uniphier_aio_pll *plls;
int num_plls;
struct snd_soc_dai_driver *dais;
int num_dais;
/* DMA access mode, this is workaround for DMA hungup */
int addr_ext;
};
struct uniphier_aio_sub {
struct uniphier_aio *aio;
/* Guard sub->rd_offs and wr_offs from IRQ handler. */
spinlock_t lock;
const struct uniphier_aio_swmap *swm;
const struct uniphier_aio_spec *spec;
/* For PCM audio */
struct snd_pcm_substream *substream;
struct snd_pcm_hw_params params;
/* For compress audio */
struct snd_compr_stream *cstream;
struct snd_compr_params cparams;
unsigned char *compr_area;
dma_addr_t compr_addr;
size_t compr_bytes;
int pass_through;
enum IEC61937_PC iec_pc;
bool iec_header;
/* Both PCM and compress audio */
bool use_mmap;
int setting;
int running;
u64 rd_offs;
u64 wr_offs;
u32 threshold;
u64 rd_org;
u64 wr_org;
u64 rd_total;
u64 wr_total;
};
struct uniphier_aio {
struct uniphier_aio_chip *chip;
struct uniphier_aio_sub sub[2];
unsigned int fmt;
/* Set one of AUD_CLK_X */
int clk_in;
int clk_out;
/* Set one of AUD_PLL_X */
int pll_in;
int pll_out;
/* Set one of AUD_PLLDIV_X */
int plldiv;
};
struct uniphier_aio_chip {
struct platform_device *pdev;
const struct uniphier_aio_chip_spec *chip_spec;
struct uniphier_aio *aios;
int num_aios;
struct uniphier_aio_pll *plls;
int num_plls;
struct clk *clk;
struct reset_control *rst;
struct regmap *regmap;
struct regmap *regmap_sg;
int active;
};
static inline struct uniphier_aio *uniphier_priv(struct snd_soc_dai *dai)
{
struct uniphier_aio_chip *chip = snd_soc_dai_get_drvdata(dai);
return &chip->aios[dai->id];
}
int uniphier_aiodma_soc_register_platform(struct platform_device *pdev);
extern const struct snd_compr_ops uniphier_aio_compr_ops;
int uniphier_aio_dai_probe(struct snd_soc_dai *dai);
int uniphier_aio_dai_remove(struct snd_soc_dai *dai);
int uniphier_aio_dai_suspend(struct snd_soc_dai *dai);
int uniphier_aio_dai_resume(struct snd_soc_dai *dai);
int uniphier_aio_probe(struct platform_device *pdev);
int uniphier_aio_remove(struct platform_device *pdev);
extern const struct snd_soc_dai_ops uniphier_aio_i2s_ops;
extern const struct snd_soc_dai_ops uniphier_aio_spdif_ops;
u64 aio_rb_cnt(struct uniphier_aio_sub *sub);
u64 aio_rbt_cnt_to_end(struct uniphier_aio_sub *sub);
u64 aio_rb_space(struct uniphier_aio_sub *sub);
u64 aio_rb_space_to_end(struct uniphier_aio_sub *sub);
void aio_iecout_set_enable(struct uniphier_aio_chip *chip, bool enable);
int aio_chip_set_pll(struct uniphier_aio_chip *chip, int pll_id,
unsigned int freq);
void aio_chip_init(struct uniphier_aio_chip *chip);
int aio_init(struct uniphier_aio_sub *sub);
void aio_port_reset(struct uniphier_aio_sub *sub);
int aio_port_set_rate(struct uniphier_aio_sub *sub, int rate);
int aio_port_set_fmt(struct uniphier_aio_sub *sub);
int aio_port_set_clk(struct uniphier_aio_sub *sub);
int aio_port_set_param(struct uniphier_aio_sub *sub, int pass_through,
const struct snd_pcm_hw_params *params);
void aio_port_set_enable(struct uniphier_aio_sub *sub, int enable);
int aio_if_set_param(struct uniphier_aio_sub *sub, int pass_through);
int aio_oport_set_stream_type(struct uniphier_aio_sub *sub,
enum IEC61937_PC pc);
void aio_src_reset(struct uniphier_aio_sub *sub);
int aio_src_set_param(struct uniphier_aio_sub *sub,
const struct snd_pcm_hw_params *params);
int aio_srcif_set_param(struct uniphier_aio_sub *sub);
int aio_srcch_set_param(struct uniphier_aio_sub *sub);
void aio_srcch_set_enable(struct uniphier_aio_sub *sub, int enable);
int aiodma_ch_set_param(struct uniphier_aio_sub *sub);
void aiodma_ch_set_enable(struct uniphier_aio_sub *sub, int enable);
int aiodma_rb_set_threshold(struct uniphier_aio_sub *sub, u64 size, u32 th);
int aiodma_rb_set_buffer(struct uniphier_aio_sub *sub, u64 start, u64 end,
int period);
void aiodma_rb_sync(struct uniphier_aio_sub *sub, u64 start, u64 size,
int period);
bool aiodma_rb_is_irq(struct uniphier_aio_sub *sub);
void aiodma_rb_clear_irq(struct uniphier_aio_sub *sub);
#endif /* SND_UNIPHIER_AIO_H__ */

View File

@ -1,22 +1,8 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Socionext UniPhier EVEA ADC/DAC codec driver.
*
* Copyright (c) 2016-2017 Socionext Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; version 2
* of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
//
// Socionext UniPhier EVEA ADC/DAC codec driver.
//
// Copyright (c) 2016-2017 Socionext Inc.
#include <linux/clk.h>
#include <linux/module.h>
@ -32,6 +18,8 @@
#define AADCPOW(n) (0x0078 + 0x04 * (n))
#define AADCPOW_AADC_POWD BIT(0)
#define ALINSW1 0x0088
#define ALINSW1_SEL1_SHIFT 3
#define AHPOUTPOW 0x0098
#define AHPOUTPOW_HP_ON BIT(4)
#define ALINEPOW 0x009c
@ -220,8 +208,8 @@ static void evea_update_switch_all(struct evea_priv *evea)
static int evea_get_switch_lin(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct evea_priv *evea = snd_soc_codec_get_drvdata(codec);
struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
struct evea_priv *evea = snd_soc_component_get_drvdata(component);
ucontrol->value.integer.value[0] = evea->switch_lin;
@ -231,8 +219,8 @@ static int evea_get_switch_lin(struct snd_kcontrol *kcontrol,
static int evea_set_switch_lin(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct evea_priv *evea = snd_soc_codec_get_drvdata(codec);
struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
struct evea_priv *evea = snd_soc_component_get_drvdata(component);
if (evea->switch_lin == ucontrol->value.integer.value[0])
return 0;
@ -245,8 +233,8 @@ static int evea_set_switch_lin(struct snd_kcontrol *kcontrol,
static int evea_get_switch_lo(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct evea_priv *evea = snd_soc_codec_get_drvdata(codec);
struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
struct evea_priv *evea = snd_soc_component_get_drvdata(component);
ucontrol->value.integer.value[0] = evea->switch_lo;
@ -256,8 +244,8 @@ static int evea_get_switch_lo(struct snd_kcontrol *kcontrol,
static int evea_set_switch_lo(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct evea_priv *evea = snd_soc_codec_get_drvdata(codec);
struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
struct evea_priv *evea = snd_soc_component_get_drvdata(component);
if (evea->switch_lo == ucontrol->value.integer.value[0])
return 0;
@ -270,8 +258,8 @@ static int evea_set_switch_lo(struct snd_kcontrol *kcontrol,
static int evea_get_switch_hp(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct evea_priv *evea = snd_soc_codec_get_drvdata(codec);
struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
struct evea_priv *evea = snd_soc_component_get_drvdata(component);
ucontrol->value.integer.value[0] = evea->switch_hp;
@ -281,8 +269,8 @@ static int evea_get_switch_hp(struct snd_kcontrol *kcontrol,
static int evea_set_switch_hp(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct evea_priv *evea = snd_soc_codec_get_drvdata(codec);
struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
struct evea_priv *evea = snd_soc_component_get_drvdata(component);
if (evea->switch_hp == ucontrol->value.integer.value[0])
return 0;
@ -292,7 +280,16 @@ static int evea_set_switch_hp(struct snd_kcontrol *kcontrol,
return evea_update_switch_hp(evea);
}
static const struct snd_kcontrol_new eva_controls[] = {
static const char * const linsw1_sel1_text[] = {
"LIN1", "LIN2", "LIN3"
};
static SOC_ENUM_SINGLE_DECL(linsw1_sel1_enum,
ALINSW1, ALINSW1_SEL1_SHIFT,
linsw1_sel1_text);
static const struct snd_kcontrol_new evea_controls[] = {
SOC_ENUM("Line Capture Source", linsw1_sel1_enum),
SOC_SINGLE_BOOL_EXT("Line Capture Switch", 0,
evea_get_switch_lin, evea_set_switch_lin),
SOC_SINGLE_BOOL_EXT("Line Playback Switch", 0,
@ -301,9 +298,9 @@ static const struct snd_kcontrol_new eva_controls[] = {
evea_get_switch_hp, evea_set_switch_hp),
};
static int evea_codec_probe(struct snd_soc_codec *codec)
static int evea_codec_probe(struct snd_soc_component *component)
{
struct evea_priv *evea = snd_soc_codec_get_drvdata(codec);
struct evea_priv *evea = snd_soc_component_get_drvdata(component);
evea->switch_lin = 1;
evea->switch_lo = 1;
@ -315,9 +312,9 @@ static int evea_codec_probe(struct snd_soc_codec *codec)
return 0;
}
static int evea_codec_suspend(struct snd_soc_codec *codec)
static int evea_codec_suspend(struct snd_soc_component *component)
{
struct evea_priv *evea = snd_soc_codec_get_drvdata(codec);
struct evea_priv *evea = snd_soc_component_get_drvdata(component);
evea_set_power_state_off(evea);
@ -331,9 +328,9 @@ static int evea_codec_suspend(struct snd_soc_codec *codec)
return 0;
}
static int evea_codec_resume(struct snd_soc_codec *codec)
static int evea_codec_resume(struct snd_soc_component *component)
{
struct evea_priv *evea = snd_soc_codec_get_drvdata(codec);
struct evea_priv *evea = snd_soc_component_get_drvdata(component);
int ret;
ret = clk_prepare_enable(evea->clk);
@ -376,19 +373,20 @@ err_out_clock:
return ret;
}
static struct snd_soc_codec_driver soc_codec_evea = {
.probe = evea_codec_probe,
.suspend = evea_codec_suspend,
.resume = evea_codec_resume,
.component_driver = {
.dapm_widgets = evea_widgets,
.num_dapm_widgets = ARRAY_SIZE(evea_widgets),
.dapm_routes = evea_routes,
.num_dapm_routes = ARRAY_SIZE(evea_routes),
.controls = eva_controls,
.num_controls = ARRAY_SIZE(eva_controls),
},
static struct snd_soc_component_driver soc_codec_evea = {
.probe = evea_codec_probe,
.suspend = evea_codec_suspend,
.resume = evea_codec_resume,
.dapm_widgets = evea_widgets,
.num_dapm_widgets = ARRAY_SIZE(evea_widgets),
.dapm_routes = evea_routes,
.num_dapm_routes = ARRAY_SIZE(evea_routes),
.controls = evea_controls,
.num_controls = ARRAY_SIZE(evea_controls),
.idle_bias_on = 1,
.use_pmdown_time = 1,
.endianness = 1,
.non_legacy_dai_naming = 1,
};
static struct snd_soc_dai_driver soc_dai_evea[] = {
@ -505,7 +503,7 @@ static int evea_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, evea);
ret = snd_soc_register_codec(&pdev->dev, &soc_codec_evea,
ret = devm_snd_soc_register_component(&pdev->dev, &soc_codec_evea,
soc_dai_evea, ARRAY_SIZE(soc_dai_evea));
if (ret)
goto err_out_reset_adamv;
@ -534,8 +532,6 @@ static int evea_remove(struct platform_device *pdev)
{
struct evea_priv *evea = platform_get_drvdata(pdev);
snd_soc_unregister_codec(&pdev->dev);
reset_control_assert(evea->rst_adamv);
reset_control_assert(evea->rst_exiv);
reset_control_assert(evea->rst);