mirror of
https://github.com/torvalds/linux.git
synced 2024-11-27 22:51:35 +00:00
Merge remote-tracking branches 'asoc/topic/rcar', 'asoc/topic/rockchip', 'asoc/topic/rt286' and 'asoc/topic/rt5631' into asoc-next
This commit is contained in:
commit
6e6d57d07a
@ -1,8 +1,12 @@
|
||||
Renesas R-Car sound
|
||||
|
||||
Required properties:
|
||||
- compatible : "renesas,rcar_sound-gen1" if generation1
|
||||
- compatible : "renesas,rcar_sound-<soctype>", fallbacks
|
||||
"renesas,rcar_sound-gen1" if generation1, and
|
||||
"renesas,rcar_sound-gen2" if generation2
|
||||
Examples with soctypes are:
|
||||
- "renesas,rcar_sound-r8a7790" (R-Car H2)
|
||||
- "renesas,rcar_sound-r8a7791" (R-Car M2-W)
|
||||
- reg : Should contain the register physical address.
|
||||
required register is
|
||||
SRU/ADG/SSI if generation1
|
||||
@ -35,9 +39,9 @@ DAI subnode properties:
|
||||
|
||||
Example:
|
||||
|
||||
rcar_sound: rcar_sound@0xffd90000 {
|
||||
rcar_sound: rcar_sound@ec500000 {
|
||||
#sound-dai-cells = <1>;
|
||||
compatible = "renesas,rcar_sound-gen2";
|
||||
compatible = "renesas,rcar_sound-r8a7791", "renesas,rcar_sound-gen2";
|
||||
reg = <0 0xec500000 0 0x1000>, /* SCU */
|
||||
<0 0xec5a0000 0 0x100>, /* ADG */
|
||||
<0 0xec540000 0 0x1000>, /* SSIU */
|
||||
|
48
Documentation/devicetree/bindings/sound/rt5631.txt
Normal file
48
Documentation/devicetree/bindings/sound/rt5631.txt
Normal file
@ -0,0 +1,48 @@
|
||||
ALC5631/RT5631 audio CODEC
|
||||
|
||||
This device supports I2C only.
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : "realtek,alc5631" or "realtek,rt5631"
|
||||
|
||||
- reg : the I2C address of the device.
|
||||
|
||||
Pins on the device (for linking into audio routes):
|
||||
|
||||
* SPK_OUT_R_P
|
||||
* SPK_OUT_R_N
|
||||
* SPK_OUT_L_P
|
||||
* SPK_OUT_L_N
|
||||
* HP_OUT_L
|
||||
* HP_OUT_R
|
||||
* AUX_OUT2_LP
|
||||
* AUX_OUT2_RN
|
||||
* AUX_OUT1_LP
|
||||
* AUX_OUT1_RN
|
||||
* AUX_IN_L_JD
|
||||
* AUX_IN_R_JD
|
||||
* MONO_IN_P
|
||||
* MONO_IN_N
|
||||
* MIC1_P
|
||||
* MIC1_N
|
||||
* MIC2_P
|
||||
* MIC2_N
|
||||
* MONO_OUT_P
|
||||
* MONO_OUT_N
|
||||
* MICBIAS1
|
||||
* MICBIAS2
|
||||
|
||||
Example:
|
||||
|
||||
alc5631: alc5631@1a {
|
||||
compatible = "realtek,alc5631";
|
||||
reg = <0x1a>;
|
||||
};
|
||||
|
||||
or
|
||||
|
||||
rt5631: rt5631@1a {
|
||||
compatible = "realtek,rt5631";
|
||||
reg = <0x1a>;
|
||||
};
|
@ -36,14 +36,14 @@
|
||||
#define RSND_SSI_CLK_PIN_SHARE (1 << 31)
|
||||
#define RSND_SSI_NO_BUSIF (1 << 30) /* SSI+DMA without BUSIF */
|
||||
|
||||
#define RSND_SSI(_dma_id, _pio_irq, _flags) \
|
||||
{ .dma_id = _dma_id, .pio_irq = _pio_irq, .flags = _flags }
|
||||
#define RSND_SSI(_dma_id, _irq, _flags) \
|
||||
{ .dma_id = _dma_id, .irq = _irq, .flags = _flags }
|
||||
#define RSND_SSI_UNUSED \
|
||||
{ .dma_id = -1, .pio_irq = -1, .flags = 0 }
|
||||
{ .dma_id = -1, .irq = -1, .flags = 0 }
|
||||
|
||||
struct rsnd_ssi_platform_info {
|
||||
int dma_id;
|
||||
int pio_irq;
|
||||
int irq;
|
||||
u32 flags;
|
||||
};
|
||||
|
||||
|
@ -501,7 +501,8 @@ config SND_SOC_RT286
|
||||
depends on I2C
|
||||
|
||||
config SND_SOC_RT5631
|
||||
tristate
|
||||
tristate "Realtek ALC5631/RT5631 CODEC"
|
||||
depends on I2C
|
||||
|
||||
config SND_SOC_RT5640
|
||||
tristate
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
@ -36,11 +37,13 @@
|
||||
|
||||
struct rt286_priv {
|
||||
struct regmap *regmap;
|
||||
struct snd_soc_codec *codec;
|
||||
struct rt286_platform_data pdata;
|
||||
struct i2c_client *i2c;
|
||||
struct snd_soc_jack *jack;
|
||||
struct delayed_work jack_detect_work;
|
||||
int sys_clk;
|
||||
int clk_id;
|
||||
struct reg_default *index_cache;
|
||||
};
|
||||
|
||||
@ -188,7 +191,7 @@ static int rt286_hw_write(void *context, unsigned int reg, unsigned int value)
|
||||
u8 data[4];
|
||||
int ret, i;
|
||||
|
||||
/*handle index registers*/
|
||||
/* handle index registers */
|
||||
if (reg <= 0xff) {
|
||||
rt286_hw_write(client, RT286_COEF_INDEX, reg);
|
||||
for (i = 0; i < INDEX_CACHE_SIZE; i++) {
|
||||
@ -231,7 +234,7 @@ static int rt286_hw_read(void *context, unsigned int reg, unsigned int *value)
|
||||
__be32 be_reg;
|
||||
unsigned int index, vid, buf = 0x0;
|
||||
|
||||
/*handle index registers*/
|
||||
/* handle index registers */
|
||||
if (reg <= 0xff) {
|
||||
rt286_hw_write(client, RT286_COEF_INDEX, reg);
|
||||
reg = RT286_PROC_COEF;
|
||||
@ -298,7 +301,6 @@ static int rt286_support_power_controls[] = {
|
||||
static int rt286_jack_detect(struct rt286_priv *rt286, bool *hp, bool *mic)
|
||||
{
|
||||
unsigned int val, buf;
|
||||
int i;
|
||||
|
||||
*hp = false;
|
||||
*mic = false;
|
||||
@ -309,67 +311,44 @@ static int rt286_jack_detect(struct rt286_priv *rt286, bool *hp, bool *mic)
|
||||
if (*hp) {
|
||||
/* power on HV,VERF */
|
||||
regmap_update_bits(rt286->regmap,
|
||||
RT286_POWER_CTRL1, 0x1001, 0x0);
|
||||
RT286_DC_GAIN, 0x200, 0x200);
|
||||
|
||||
snd_soc_dapm_force_enable_pin(&rt286->codec->dapm,
|
||||
"HV");
|
||||
snd_soc_dapm_force_enable_pin(&rt286->codec->dapm,
|
||||
"VREF");
|
||||
/* power LDO1 */
|
||||
regmap_update_bits(rt286->regmap,
|
||||
RT286_POWER_CTRL2, 0x4, 0x4);
|
||||
snd_soc_dapm_force_enable_pin(&rt286->codec->dapm,
|
||||
"LDO1");
|
||||
snd_soc_dapm_sync(&rt286->codec->dapm);
|
||||
|
||||
regmap_write(rt286->regmap, RT286_SET_MIC1, 0x24);
|
||||
msleep(50);
|
||||
|
||||
regmap_update_bits(rt286->regmap,
|
||||
RT286_CBJ_CTRL1, 0xfcc0, 0xd400);
|
||||
msleep(300);
|
||||
regmap_read(rt286->regmap, RT286_CBJ_CTRL2, &val);
|
||||
|
||||
msleep(200);
|
||||
i = 40;
|
||||
while (((val & 0x0800) == 0) && (i > 0)) {
|
||||
if (0x0070 == (val & 0x0070)) {
|
||||
*mic = true;
|
||||
} else {
|
||||
regmap_update_bits(rt286->regmap,
|
||||
RT286_CBJ_CTRL1, 0xfcc0, 0xe400);
|
||||
msleep(300);
|
||||
regmap_read(rt286->regmap,
|
||||
RT286_CBJ_CTRL2, &val);
|
||||
i--;
|
||||
msleep(20);
|
||||
if (0x0070 == (val & 0x0070))
|
||||
*mic = true;
|
||||
else
|
||||
*mic = false;
|
||||
}
|
||||
|
||||
if (0x0400 == (val & 0x0700)) {
|
||||
*mic = false;
|
||||
|
||||
regmap_write(rt286->regmap,
|
||||
RT286_SET_MIC1, 0x20);
|
||||
/* power off HV,VERF */
|
||||
regmap_update_bits(rt286->regmap,
|
||||
RT286_POWER_CTRL1, 0x1001, 0x1001);
|
||||
regmap_update_bits(rt286->regmap,
|
||||
RT286_A_BIAS_CTRL3, 0xc000, 0x0000);
|
||||
regmap_update_bits(rt286->regmap,
|
||||
RT286_CBJ_CTRL1, 0x0030, 0x0000);
|
||||
regmap_update_bits(rt286->regmap,
|
||||
RT286_A_BIAS_CTRL2, 0xc000, 0x0000);
|
||||
} else if ((0x0200 == (val & 0x0700)) ||
|
||||
(0x0100 == (val & 0x0700))) {
|
||||
*mic = true;
|
||||
regmap_update_bits(rt286->regmap,
|
||||
RT286_A_BIAS_CTRL3, 0xc000, 0x8000);
|
||||
regmap_update_bits(rt286->regmap,
|
||||
RT286_CBJ_CTRL1, 0x0030, 0x0020);
|
||||
regmap_update_bits(rt286->regmap,
|
||||
RT286_A_BIAS_CTRL2, 0xc000, 0x8000);
|
||||
} else {
|
||||
*mic = false;
|
||||
}
|
||||
|
||||
regmap_update_bits(rt286->regmap,
|
||||
RT286_MISC_CTRL1,
|
||||
0x0060, 0x0000);
|
||||
RT286_DC_GAIN, 0x200, 0x0);
|
||||
|
||||
} else {
|
||||
regmap_update_bits(rt286->regmap,
|
||||
RT286_MISC_CTRL1,
|
||||
0x0060, 0x0020);
|
||||
regmap_update_bits(rt286->regmap,
|
||||
RT286_A_BIAS_CTRL3,
|
||||
0xc000, 0x8000);
|
||||
regmap_update_bits(rt286->regmap,
|
||||
RT286_CBJ_CTRL1,
|
||||
0x0030, 0x0020);
|
||||
regmap_update_bits(rt286->regmap,
|
||||
RT286_A_BIAS_CTRL2,
|
||||
0xc000, 0x8000);
|
||||
|
||||
*mic = false;
|
||||
regmap_write(rt286->regmap, RT286_SET_MIC1, 0x20);
|
||||
}
|
||||
} else {
|
||||
regmap_read(rt286->regmap, RT286_GET_HP_SENSE, &buf);
|
||||
@ -378,6 +357,12 @@ static int rt286_jack_detect(struct rt286_priv *rt286, bool *hp, bool *mic)
|
||||
*mic = buf & 0x80000000;
|
||||
}
|
||||
|
||||
snd_soc_dapm_disable_pin(&rt286->codec->dapm, "HV");
|
||||
snd_soc_dapm_disable_pin(&rt286->codec->dapm, "VREF");
|
||||
if (!*hp)
|
||||
snd_soc_dapm_disable_pin(&rt286->codec->dapm, "LDO1");
|
||||
snd_soc_dapm_sync(&rt286->codec->dapm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -415,6 +400,17 @@ int rt286_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rt286_mic_detect);
|
||||
|
||||
static int is_mclk_mode(struct snd_soc_dapm_widget *source,
|
||||
struct snd_soc_dapm_widget *sink)
|
||||
{
|
||||
struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(source->codec);
|
||||
|
||||
if (rt286->clk_id == RT286_SCLK_S_MCLK)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -6350, 50, 0);
|
||||
static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, 0, 1000, 0);
|
||||
|
||||
@ -568,7 +564,84 @@ static int rt286_adc_event(struct snd_soc_dapm_widget *w,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rt286_vref_event(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *kcontrol, int event)
|
||||
{
|
||||
struct snd_soc_codec *codec = w->codec;
|
||||
|
||||
switch (event) {
|
||||
case SND_SOC_DAPM_PRE_PMU:
|
||||
snd_soc_update_bits(codec,
|
||||
RT286_CBJ_CTRL1, 0x0400, 0x0000);
|
||||
mdelay(50);
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rt286_ldo2_event(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *kcontrol, int event)
|
||||
{
|
||||
struct snd_soc_codec *codec = w->codec;
|
||||
|
||||
switch (event) {
|
||||
case SND_SOC_DAPM_POST_PMU:
|
||||
snd_soc_update_bits(codec, RT286_POWER_CTRL2, 0x38, 0x08);
|
||||
break;
|
||||
case SND_SOC_DAPM_PRE_PMD:
|
||||
snd_soc_update_bits(codec, RT286_POWER_CTRL2, 0x38, 0x30);
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rt286_mic1_event(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *kcontrol, int event)
|
||||
{
|
||||
struct snd_soc_codec *codec = w->codec;
|
||||
|
||||
switch (event) {
|
||||
case SND_SOC_DAPM_PRE_PMU:
|
||||
snd_soc_update_bits(codec,
|
||||
RT286_A_BIAS_CTRL3, 0xc000, 0x8000);
|
||||
snd_soc_update_bits(codec,
|
||||
RT286_A_BIAS_CTRL2, 0xc000, 0x8000);
|
||||
break;
|
||||
case SND_SOC_DAPM_POST_PMD:
|
||||
snd_soc_update_bits(codec,
|
||||
RT286_A_BIAS_CTRL3, 0xc000, 0x0000);
|
||||
snd_soc_update_bits(codec,
|
||||
RT286_A_BIAS_CTRL2, 0xc000, 0x0000);
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_dapm_widget rt286_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_SUPPLY_S("HV", 1, RT286_POWER_CTRL1,
|
||||
12, 1, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY("VREF", RT286_POWER_CTRL1,
|
||||
0, 1, rt286_vref_event, SND_SOC_DAPM_PRE_PMU),
|
||||
SND_SOC_DAPM_SUPPLY_S("LDO1", 1, RT286_POWER_CTRL2,
|
||||
2, 0, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY_S("LDO2", 2, RT286_POWER_CTRL1,
|
||||
13, 1, rt286_ldo2_event, SND_SOC_DAPM_PRE_PMD |
|
||||
SND_SOC_DAPM_POST_PMU),
|
||||
SND_SOC_DAPM_SUPPLY("MCLK MODE", RT286_PLL_CTRL1,
|
||||
5, 0, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY("MIC1 Input Buffer", SND_SOC_NOPM,
|
||||
0, 0, rt286_mic1_event, SND_SOC_DAPM_PRE_PMU |
|
||||
SND_SOC_DAPM_POST_PMD),
|
||||
|
||||
/* Input Lines */
|
||||
SND_SOC_DAPM_INPUT("DMIC1 Pin"),
|
||||
SND_SOC_DAPM_INPUT("DMIC2 Pin"),
|
||||
@ -642,6 +715,25 @@ static const struct snd_soc_dapm_widget rt286_dapm_widgets[] = {
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route rt286_dapm_routes[] = {
|
||||
{"ADC 0", NULL, "MCLK MODE", is_mclk_mode},
|
||||
{"ADC 1", NULL, "MCLK MODE", is_mclk_mode},
|
||||
{"Front", NULL, "MCLK MODE", is_mclk_mode},
|
||||
{"Surround", NULL, "MCLK MODE", is_mclk_mode},
|
||||
|
||||
{"HP Power", NULL, "LDO1"},
|
||||
{"HP Power", NULL, "LDO2"},
|
||||
|
||||
{"MIC1", NULL, "LDO1"},
|
||||
{"MIC1", NULL, "LDO2"},
|
||||
{"MIC1", NULL, "HV"},
|
||||
{"MIC1", NULL, "VREF"},
|
||||
{"MIC1", NULL, "MIC1 Input Buffer"},
|
||||
|
||||
{"SPO", NULL, "LDO1"},
|
||||
{"SPO", NULL, "LDO2"},
|
||||
{"SPO", NULL, "HV"},
|
||||
{"SPO", NULL, "VREF"},
|
||||
|
||||
{"DMIC1", NULL, "DMIC1 Pin"},
|
||||
{"DMIC2", NULL, "DMIC2 Pin"},
|
||||
{"DMIC1", NULL, "DMIC Receiver"},
|
||||
@ -880,6 +972,7 @@ static int rt286_set_dai_sysclk(struct snd_soc_dai *dai,
|
||||
}
|
||||
|
||||
rt286->sys_clk = freq;
|
||||
rt286->clk_id = clk_id;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -915,13 +1008,18 @@ static int rt286_set_bias_level(struct snd_soc_codec *codec,
|
||||
|
||||
case SND_SOC_BIAS_ON:
|
||||
mdelay(10);
|
||||
snd_soc_update_bits(codec,
|
||||
RT286_CBJ_CTRL1, 0x0400, 0x0400);
|
||||
snd_soc_update_bits(codec,
|
||||
RT286_DC_GAIN, 0x200, 0x0);
|
||||
|
||||
break;
|
||||
|
||||
case SND_SOC_BIAS_STANDBY:
|
||||
snd_soc_write(codec,
|
||||
RT286_SET_AUDIO_POWER, AC_PWRST_D3);
|
||||
snd_soc_update_bits(codec,
|
||||
RT286_DC_GAIN, 0x200, 0x0);
|
||||
RT286_CBJ_CTRL1, 0x0400, 0x0000);
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -962,6 +1060,7 @@ static int rt286_probe(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
rt286->codec = codec;
|
||||
codec->dapm.bias_level = SND_SOC_BIAS_OFF;
|
||||
|
||||
if (rt286->i2c->irq) {
|
||||
@ -1107,6 +1206,16 @@ static const struct acpi_device_id rt286_acpi_match[] = {
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, rt286_acpi_match);
|
||||
|
||||
static struct dmi_system_id force_combo_jack_table[] = {
|
||||
{
|
||||
.ident = "Intel Wilson Beach",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_BOARD_NAME, "Wilson Beach SDS")
|
||||
}
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
||||
static int rt286_i2c_probe(struct i2c_client *i2c,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
@ -1142,6 +1251,9 @@ static int rt286_i2c_probe(struct i2c_client *i2c,
|
||||
if (pdata)
|
||||
rt286->pdata = *pdata;
|
||||
|
||||
if (dmi_check_system(force_combo_jack_table))
|
||||
rt286->pdata.cbj_en = true;
|
||||
|
||||
regmap_write(rt286->regmap, RT286_SET_AUDIO_POWER, AC_PWRST_D3);
|
||||
|
||||
for (i = 0; i < RT286_POWER_REG_LEN; i++)
|
||||
@ -1152,7 +1264,6 @@ static int rt286_i2c_probe(struct i2c_client *i2c,
|
||||
if (!rt286->pdata.cbj_en) {
|
||||
regmap_write(rt286->regmap, RT286_CBJ_CTRL2, 0x0000);
|
||||
regmap_write(rt286->regmap, RT286_MIC1_DET_CTRL, 0x0816);
|
||||
regmap_write(rt286->regmap, RT286_MISC_CTRL1, 0x0000);
|
||||
regmap_update_bits(rt286->regmap,
|
||||
RT286_CBJ_CTRL1, 0xf000, 0xb000);
|
||||
} else {
|
||||
@ -1169,10 +1280,12 @@ static int rt286_i2c_probe(struct i2c_client *i2c,
|
||||
|
||||
mdelay(10);
|
||||
|
||||
/*Power down LDO2*/
|
||||
regmap_update_bits(rt286->regmap, RT286_POWER_CTRL2, 0x8, 0x0);
|
||||
regmap_write(rt286->regmap, RT286_MISC_CTRL1, 0x0000);
|
||||
/* Power down LDO, VREF */
|
||||
regmap_update_bits(rt286->regmap, RT286_POWER_CTRL2, 0xc, 0x0);
|
||||
regmap_update_bits(rt286->regmap, RT286_POWER_CTRL1, 0x1001, 0x1001);
|
||||
|
||||
/*Set depop parameter*/
|
||||
/* Set depop parameter */
|
||||
regmap_update_bits(rt286->regmap, RT286_DEPOP_CTRL2, 0x403a, 0x401a);
|
||||
regmap_update_bits(rt286->regmap, RT286_DEPOP_CTRL3, 0xf777, 0x4737);
|
||||
regmap_update_bits(rt286->regmap, RT286_DEPOP_CTRL4, 0x00ff, 0x003f);
|
||||
|
@ -1612,29 +1612,6 @@ static int rt5631_probe(struct snd_soc_codec *codec)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rt5631_remove(struct snd_soc_codec *codec)
|
||||
{
|
||||
rt5631_set_bias_level(codec, SND_SOC_BIAS_OFF);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int rt5631_suspend(struct snd_soc_codec *codec)
|
||||
{
|
||||
rt5631_set_bias_level(codec, SND_SOC_BIAS_OFF);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rt5631_resume(struct snd_soc_codec *codec)
|
||||
{
|
||||
rt5631_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define rt5631_suspend NULL
|
||||
#define rt5631_resume NULL
|
||||
#endif
|
||||
|
||||
#define RT5631_STEREO_RATES SNDRV_PCM_RATE_8000_96000
|
||||
#define RT5631_FORMAT (SNDRV_PCM_FMTBIT_S16_LE | \
|
||||
SNDRV_PCM_FMTBIT_S20_3LE | \
|
||||
@ -1672,10 +1649,8 @@ static struct snd_soc_dai_driver rt5631_dai[] = {
|
||||
|
||||
static struct snd_soc_codec_driver soc_codec_dev_rt5631 = {
|
||||
.probe = rt5631_probe,
|
||||
.remove = rt5631_remove,
|
||||
.suspend = rt5631_suspend,
|
||||
.resume = rt5631_resume,
|
||||
.set_bias_level = rt5631_set_bias_level,
|
||||
.suspend_bias_off = true,
|
||||
.controls = rt5631_snd_controls,
|
||||
.num_controls = ARRAY_SIZE(rt5631_snd_controls),
|
||||
.dapm_widgets = rt5631_dapm_widgets,
|
||||
@ -1686,10 +1661,20 @@ static struct snd_soc_codec_driver soc_codec_dev_rt5631 = {
|
||||
|
||||
static const struct i2c_device_id rt5631_i2c_id[] = {
|
||||
{ "rt5631", 0 },
|
||||
{ "alc5631", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, rt5631_i2c_id);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static struct of_device_id rt5631_i2c_dt_ids[] = {
|
||||
{ .compatible = "realtek,rt5631"},
|
||||
{ .compatible = "realtek,alc5631"},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, rt5631_i2c_dt_ids);
|
||||
#endif
|
||||
|
||||
static const struct regmap_config rt5631_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 16,
|
||||
@ -1734,6 +1719,7 @@ static struct i2c_driver rt5631_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "rt5631",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_match_ptr(rt5631_i2c_dt_ids),
|
||||
},
|
||||
.probe = rt5631_i2c_probe,
|
||||
.remove = rt5631_i2c_remove,
|
||||
|
@ -1,11 +1,16 @@
|
||||
config SND_SOC_ROCKCHIP
|
||||
tristate "ASoC support for Rockchip"
|
||||
depends on COMPILE_TEST || ARCH_ROCKCHIP
|
||||
select SND_SOC_GENERIC_DMAENGINE_PCM
|
||||
help
|
||||
Say Y or M if you want to add support for codecs attached to
|
||||
the Rockchip SoCs' Audio interfaces. You will also need to
|
||||
select the audio interfaces to support below.
|
||||
|
||||
config SND_SOC_ROCKCHIP_I2S
|
||||
tristate
|
||||
tristate "Rockchip I2S Device Driver"
|
||||
depends on CLKDEV_LOOKUP && SND_SOC_ROCKCHIP
|
||||
select SND_SOC_GENERIC_DMAENGINE_PCM
|
||||
help
|
||||
Say Y or M if you want to add support for I2S driver for
|
||||
Rockchip I2S device. The device supports upto maximum of
|
||||
8 channels each for play and record.
|
||||
|
@ -430,7 +430,7 @@ int rsnd_adg_probe(struct platform_device *pdev,
|
||||
adg->clk[CLKI] = devm_clk_get(dev, "clk_i");
|
||||
|
||||
for_each_rsnd_clk(clk, adg, i)
|
||||
dev_dbg(dev, "clk %d : %p\n", i, clk);
|
||||
dev_dbg(dev, "clk %d : %p : %ld\n", i, clk, clk_get_rate(clk));
|
||||
|
||||
rsnd_adg_ssi_clk_init(priv, adg);
|
||||
|
||||
|
@ -349,7 +349,7 @@ int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma,
|
||||
dma_name);
|
||||
if (!dma->chan) {
|
||||
dev_err(dev, "can't get dma channel\n");
|
||||
return -EIO;
|
||||
goto rsnd_dma_channel_err;
|
||||
}
|
||||
|
||||
ret = dmaengine_slave_config(dma->chan, &cfg);
|
||||
@ -363,8 +363,15 @@ int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma,
|
||||
|
||||
rsnd_dma_init_err:
|
||||
rsnd_dma_quit(priv, dma);
|
||||
rsnd_dma_channel_err:
|
||||
|
||||
return ret;
|
||||
/*
|
||||
* DMA failed. try to PIO mode
|
||||
* see
|
||||
* rsnd_ssi_fallback()
|
||||
* rsnd_rdai_continuance_probe()
|
||||
*/
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
void rsnd_dma_quit(struct rsnd_priv *priv,
|
||||
@ -409,9 +416,16 @@ u32 rsnd_get_adinr(struct rsnd_mod *mod)
|
||||
({ \
|
||||
struct rsnd_priv *priv = rsnd_mod_to_priv(mod); \
|
||||
struct device *dev = rsnd_priv_to_dev(priv); \
|
||||
dev_dbg(dev, "%s [%d] %s\n", \
|
||||
rsnd_mod_name(mod), rsnd_mod_id(mod), #func); \
|
||||
(mod)->ops->func(mod, rdai); \
|
||||
u32 mask = 1 << __rsnd_mod_shift_##func; \
|
||||
u32 call = __rsnd_mod_call_##func << __rsnd_mod_shift_##func; \
|
||||
int ret = 0; \
|
||||
if ((mod->status & mask) == call) { \
|
||||
dev_dbg(dev, "%s[%d] %s\n", \
|
||||
rsnd_mod_name(mod), rsnd_mod_id(mod), #func); \
|
||||
ret = (mod)->ops->func(mod, rdai); \
|
||||
mod->status = (mod->status & ~mask) | (~call & mask); \
|
||||
} \
|
||||
ret; \
|
||||
})
|
||||
|
||||
#define rsnd_mod_call(mod, func, rdai...) \
|
||||
@ -456,6 +470,13 @@ static int rsnd_dai_connect(struct rsnd_mod *mod,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rsnd_dai_disconnect(struct rsnd_mod *mod,
|
||||
struct rsnd_dai_stream *io)
|
||||
{
|
||||
mod->io = NULL;
|
||||
io->mod[mod->type] = NULL;
|
||||
}
|
||||
|
||||
int rsnd_dai_id(struct rsnd_priv *priv, struct rsnd_dai *rdai)
|
||||
{
|
||||
int id = rdai - priv->rdai;
|
||||
@ -686,6 +707,20 @@ static const struct snd_soc_dai_ops rsnd_soc_dai_ops = {
|
||||
ret; \
|
||||
})
|
||||
|
||||
#define rsnd_path_break(priv, io, type) \
|
||||
{ \
|
||||
struct rsnd_mod *mod; \
|
||||
int id = -1; \
|
||||
\
|
||||
if (rsnd_is_enable_path(io, type)) { \
|
||||
id = rsnd_info_id(priv, io, type); \
|
||||
if (id >= 0) { \
|
||||
mod = rsnd_##type##_mod_get(priv, id); \
|
||||
rsnd_dai_disconnect(mod, io); \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
|
||||
static int rsnd_path_init(struct rsnd_priv *priv,
|
||||
struct rsnd_dai *rdai,
|
||||
struct rsnd_dai_stream *io)
|
||||
@ -933,6 +968,150 @@ static struct snd_pcm_ops rsnd_pcm_ops = {
|
||||
.pointer = rsnd_pointer,
|
||||
};
|
||||
|
||||
/*
|
||||
* snd_kcontrol
|
||||
*/
|
||||
#define kcontrol_to_cfg(kctrl) ((struct rsnd_kctrl_cfg *)kctrl->private_value)
|
||||
static int rsnd_kctrl_info(struct snd_kcontrol *kctrl,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
struct rsnd_kctrl_cfg *cfg = kcontrol_to_cfg(kctrl);
|
||||
|
||||
if (cfg->texts) {
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
|
||||
uinfo->count = cfg->size;
|
||||
uinfo->value.enumerated.items = cfg->max;
|
||||
if (uinfo->value.enumerated.item >= cfg->max)
|
||||
uinfo->value.enumerated.item = cfg->max - 1;
|
||||
strlcpy(uinfo->value.enumerated.name,
|
||||
cfg->texts[uinfo->value.enumerated.item],
|
||||
sizeof(uinfo->value.enumerated.name));
|
||||
} else {
|
||||
uinfo->count = cfg->size;
|
||||
uinfo->value.integer.min = 0;
|
||||
uinfo->value.integer.max = cfg->max;
|
||||
uinfo->type = (cfg->max == 1) ?
|
||||
SNDRV_CTL_ELEM_TYPE_BOOLEAN :
|
||||
SNDRV_CTL_ELEM_TYPE_INTEGER;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rsnd_kctrl_get(struct snd_kcontrol *kctrl,
|
||||
struct snd_ctl_elem_value *uc)
|
||||
{
|
||||
struct rsnd_kctrl_cfg *cfg = kcontrol_to_cfg(kctrl);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < cfg->size; i++)
|
||||
if (cfg->texts)
|
||||
uc->value.enumerated.item[i] = cfg->val[i];
|
||||
else
|
||||
uc->value.integer.value[i] = cfg->val[i];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rsnd_kctrl_put(struct snd_kcontrol *kctrl,
|
||||
struct snd_ctl_elem_value *uc)
|
||||
{
|
||||
struct rsnd_mod *mod = snd_kcontrol_chip(kctrl);
|
||||
struct rsnd_kctrl_cfg *cfg = kcontrol_to_cfg(kctrl);
|
||||
int i, change = 0;
|
||||
|
||||
for (i = 0; i < cfg->size; i++) {
|
||||
if (cfg->texts) {
|
||||
change |= (uc->value.enumerated.item[i] != cfg->val[i]);
|
||||
cfg->val[i] = uc->value.enumerated.item[i];
|
||||
} else {
|
||||
change |= (uc->value.integer.value[i] != cfg->val[i]);
|
||||
cfg->val[i] = uc->value.integer.value[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (change)
|
||||
cfg->update(mod);
|
||||
|
||||
return change;
|
||||
}
|
||||
|
||||
static int __rsnd_kctrl_new(struct rsnd_mod *mod,
|
||||
struct rsnd_dai *rdai,
|
||||
struct snd_soc_pcm_runtime *rtd,
|
||||
const unsigned char *name,
|
||||
struct rsnd_kctrl_cfg *cfg,
|
||||
void (*update)(struct rsnd_mod *mod))
|
||||
{
|
||||
struct snd_card *card = rtd->card->snd_card;
|
||||
struct snd_kcontrol *kctrl;
|
||||
struct snd_kcontrol_new knew = {
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = name,
|
||||
.info = rsnd_kctrl_info,
|
||||
.get = rsnd_kctrl_get,
|
||||
.put = rsnd_kctrl_put,
|
||||
.private_value = (unsigned long)cfg,
|
||||
};
|
||||
int ret;
|
||||
|
||||
kctrl = snd_ctl_new1(&knew, mod);
|
||||
if (!kctrl)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = snd_ctl_add(card, kctrl);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
cfg->update = update;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rsnd_kctrl_new_m(struct rsnd_mod *mod,
|
||||
struct rsnd_dai *rdai,
|
||||
struct snd_soc_pcm_runtime *rtd,
|
||||
const unsigned char *name,
|
||||
void (*update)(struct rsnd_mod *mod),
|
||||
struct rsnd_kctrl_cfg_m *_cfg,
|
||||
u32 max)
|
||||
{
|
||||
_cfg->cfg.max = max;
|
||||
_cfg->cfg.size = RSND_DVC_CHANNELS;
|
||||
_cfg->cfg.val = _cfg->val;
|
||||
return __rsnd_kctrl_new(mod, rdai, rtd, name, &_cfg->cfg, update);
|
||||
}
|
||||
|
||||
int rsnd_kctrl_new_s(struct rsnd_mod *mod,
|
||||
struct rsnd_dai *rdai,
|
||||
struct snd_soc_pcm_runtime *rtd,
|
||||
const unsigned char *name,
|
||||
void (*update)(struct rsnd_mod *mod),
|
||||
struct rsnd_kctrl_cfg_s *_cfg,
|
||||
u32 max)
|
||||
{
|
||||
_cfg->cfg.max = max;
|
||||
_cfg->cfg.size = 1;
|
||||
_cfg->cfg.val = &_cfg->val;
|
||||
return __rsnd_kctrl_new(mod, rdai, rtd, name, &_cfg->cfg, update);
|
||||
}
|
||||
|
||||
int rsnd_kctrl_new_e(struct rsnd_mod *mod,
|
||||
struct rsnd_dai *rdai,
|
||||
struct snd_soc_pcm_runtime *rtd,
|
||||
const unsigned char *name,
|
||||
struct rsnd_kctrl_cfg_s *_cfg,
|
||||
void (*update)(struct rsnd_mod *mod),
|
||||
const char * const *texts,
|
||||
u32 max)
|
||||
{
|
||||
_cfg->cfg.max = max;
|
||||
_cfg->cfg.size = 1;
|
||||
_cfg->cfg.val = &_cfg->val;
|
||||
_cfg->cfg.texts = texts;
|
||||
return __rsnd_kctrl_new(mod, rdai, rtd, name, &_cfg->cfg, update);
|
||||
}
|
||||
|
||||
/*
|
||||
* snd_soc_platform
|
||||
*/
|
||||
@ -976,6 +1155,49 @@ static const struct snd_soc_component_driver rsnd_soc_component = {
|
||||
.name = "rsnd",
|
||||
};
|
||||
|
||||
static int rsnd_rdai_continuance_probe(struct rsnd_priv *priv,
|
||||
struct rsnd_dai *rdai,
|
||||
int is_play)
|
||||
{
|
||||
struct rsnd_dai_stream *io = is_play ? &rdai->playback : &rdai->capture;
|
||||
int ret;
|
||||
|
||||
ret = rsnd_dai_call(probe, io, rdai);
|
||||
if (ret == -EAGAIN) {
|
||||
/*
|
||||
* Fallback to PIO mode
|
||||
*/
|
||||
|
||||
/*
|
||||
* call "remove" for SSI/SRC/DVC
|
||||
* SSI will be switch to PIO mode if it was DMA mode
|
||||
* see
|
||||
* rsnd_dma_init()
|
||||
* rsnd_ssi_fallback()
|
||||
*/
|
||||
rsnd_dai_call(remove, io, rdai);
|
||||
|
||||
/*
|
||||
* remove SRC/DVC from DAI,
|
||||
*/
|
||||
rsnd_path_break(priv, io, src);
|
||||
rsnd_path_break(priv, io, dvc);
|
||||
|
||||
/*
|
||||
* fallback
|
||||
*/
|
||||
rsnd_dai_call(fallback, io, rdai);
|
||||
|
||||
/*
|
||||
* retry to "probe".
|
||||
* DAI has SSI which is PIO mode only now.
|
||||
*/
|
||||
ret = rsnd_dai_call(probe, io, rdai);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* rsnd probe
|
||||
*/
|
||||
@ -1037,11 +1259,11 @@ static int rsnd_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
for_each_rsnd_dai(rdai, priv, i) {
|
||||
ret = rsnd_dai_call(probe, &rdai->playback, rdai);
|
||||
ret = rsnd_rdai_continuance_probe(priv, rdai, 1);
|
||||
if (ret)
|
||||
goto exit_snd_probe;
|
||||
|
||||
ret = rsnd_dai_call(probe, &rdai->capture, rdai);
|
||||
ret = rsnd_rdai_continuance_probe(priv, rdai, 0);
|
||||
if (ret)
|
||||
goto exit_snd_probe;
|
||||
}
|
||||
|
@ -11,8 +11,6 @@
|
||||
#include "rsnd.h"
|
||||
|
||||
#define RSND_DVC_NAME_SIZE 16
|
||||
#define RSND_DVC_VOLUME_MAX 100
|
||||
#define RSND_DVC_VOLUME_NUM 2
|
||||
|
||||
#define DVC_NAME "dvc"
|
||||
|
||||
@ -20,8 +18,11 @@ struct rsnd_dvc {
|
||||
struct rsnd_dvc_platform_info *info; /* rcar_snd.h */
|
||||
struct rsnd_mod mod;
|
||||
struct clk *clk;
|
||||
u8 volume[RSND_DVC_VOLUME_NUM];
|
||||
u8 mute[RSND_DVC_VOLUME_NUM];
|
||||
struct rsnd_kctrl_cfg_m volume;
|
||||
struct rsnd_kctrl_cfg_m mute;
|
||||
struct rsnd_kctrl_cfg_s ren; /* Ramp Enable */
|
||||
struct rsnd_kctrl_cfg_s rup; /* Ramp Rate Up */
|
||||
struct rsnd_kctrl_cfg_s rdown; /* Ramp Rate Down */
|
||||
};
|
||||
|
||||
#define rsnd_mod_to_dvc(_mod) \
|
||||
@ -33,23 +34,87 @@ struct rsnd_dvc {
|
||||
((pos) = (struct rsnd_dvc *)(priv)->dvc + i); \
|
||||
i++)
|
||||
|
||||
static const char const *dvc_ramp_rate[] = {
|
||||
"128 dB/1 step", /* 00000 */
|
||||
"64 dB/1 step", /* 00001 */
|
||||
"32 dB/1 step", /* 00010 */
|
||||
"16 dB/1 step", /* 00011 */
|
||||
"8 dB/1 step", /* 00100 */
|
||||
"4 dB/1 step", /* 00101 */
|
||||
"2 dB/1 step", /* 00110 */
|
||||
"1 dB/1 step", /* 00111 */
|
||||
"0.5 dB/1 step", /* 01000 */
|
||||
"0.25 dB/1 step", /* 01001 */
|
||||
"0.125 dB/1 step", /* 01010 */
|
||||
"0.125 dB/2 steps", /* 01011 */
|
||||
"0.125 dB/4 steps", /* 01100 */
|
||||
"0.125 dB/8 steps", /* 01101 */
|
||||
"0.125 dB/16 steps", /* 01110 */
|
||||
"0.125 dB/32 steps", /* 01111 */
|
||||
"0.125 dB/64 steps", /* 10000 */
|
||||
"0.125 dB/128 steps", /* 10001 */
|
||||
"0.125 dB/256 steps", /* 10010 */
|
||||
"0.125 dB/512 steps", /* 10011 */
|
||||
"0.125 dB/1024 steps", /* 10100 */
|
||||
"0.125 dB/2048 steps", /* 10101 */
|
||||
"0.125 dB/4096 steps", /* 10110 */
|
||||
"0.125 dB/8192 steps", /* 10111 */
|
||||
};
|
||||
|
||||
static void rsnd_dvc_volume_update(struct rsnd_mod *mod)
|
||||
{
|
||||
struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod);
|
||||
u32 max = (0x00800000 - 1);
|
||||
u32 vol[RSND_DVC_VOLUME_NUM];
|
||||
u32 val[RSND_DVC_CHANNELS];
|
||||
u32 dvucr = 0;
|
||||
u32 mute = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < RSND_DVC_VOLUME_NUM; i++) {
|
||||
vol[i] = max / RSND_DVC_VOLUME_MAX * dvc->volume[i];
|
||||
mute |= (!!dvc->mute[i]) << i;
|
||||
for (i = 0; i < dvc->mute.cfg.size; i++)
|
||||
mute |= (!!dvc->mute.cfg.val[i]) << i;
|
||||
|
||||
/* Disable DVC Register access */
|
||||
rsnd_mod_write(mod, DVC_DVUER, 0);
|
||||
|
||||
/* Enable Ramp */
|
||||
if (dvc->ren.val) {
|
||||
dvucr |= 0x10;
|
||||
|
||||
/* Digital Volume Max */
|
||||
for (i = 0; i < RSND_DVC_CHANNELS; i++)
|
||||
val[i] = dvc->volume.cfg.max;
|
||||
|
||||
rsnd_mod_write(mod, DVC_VRCTR, 0xff);
|
||||
rsnd_mod_write(mod, DVC_VRPDR, dvc->rup.val << 8 |
|
||||
dvc->rdown.val);
|
||||
/*
|
||||
* FIXME !!
|
||||
* use scale-downed Digital Volume
|
||||
* as Volume Ramp
|
||||
* 7F FFFF -> 3FF
|
||||
*/
|
||||
rsnd_mod_write(mod, DVC_VRDBR,
|
||||
0x3ff - (dvc->volume.val[0] >> 13));
|
||||
|
||||
} else {
|
||||
for (i = 0; i < RSND_DVC_CHANNELS; i++)
|
||||
val[i] = dvc->volume.val[i];
|
||||
}
|
||||
|
||||
rsnd_mod_write(mod, DVC_VOL0R, vol[0]);
|
||||
rsnd_mod_write(mod, DVC_VOL1R, vol[1]);
|
||||
/* Enable Digital Volume */
|
||||
dvucr |= 0x100;
|
||||
rsnd_mod_write(mod, DVC_VOL0R, val[0]);
|
||||
rsnd_mod_write(mod, DVC_VOL1R, val[1]);
|
||||
|
||||
rsnd_mod_write(mod, DVC_ZCMCR, mute);
|
||||
/* Enable Mute */
|
||||
if (mute) {
|
||||
dvucr |= 0x1;
|
||||
rsnd_mod_write(mod, DVC_ZCMCR, mute);
|
||||
}
|
||||
|
||||
rsnd_mod_write(mod, DVC_DVUCR, dvucr);
|
||||
|
||||
/* Enable DVC Register access */
|
||||
rsnd_mod_write(mod, DVC_DVUER, 1);
|
||||
}
|
||||
|
||||
static int rsnd_dvc_probe_gen2(struct rsnd_mod *mod,
|
||||
@ -58,7 +123,8 @@ static int rsnd_dvc_probe_gen2(struct rsnd_mod *mod,
|
||||
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
|
||||
struct device *dev = rsnd_priv_to_dev(priv);
|
||||
|
||||
dev_dbg(dev, "%s (Gen2) is probed\n", rsnd_mod_name(mod));
|
||||
dev_dbg(dev, "%s[%d] (Gen2) is probed\n",
|
||||
rsnd_mod_name(mod), rsnd_mod_id(mod));
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -102,16 +168,11 @@ static int rsnd_dvc_init(struct rsnd_mod *dvc_mod,
|
||||
|
||||
rsnd_mod_write(dvc_mod, DVC_ADINR, rsnd_get_adinr(dvc_mod));
|
||||
|
||||
/* enable Volume / Mute */
|
||||
rsnd_mod_write(dvc_mod, DVC_DVUCR, 0x101);
|
||||
|
||||
/* ch0/ch1 Volume */
|
||||
rsnd_dvc_volume_update(dvc_mod);
|
||||
|
||||
rsnd_mod_write(dvc_mod, DVC_DVUIR, 0);
|
||||
|
||||
rsnd_mod_write(dvc_mod, DVC_DVUER, 1);
|
||||
|
||||
rsnd_adg_set_cmd_timsel_gen2(rdai, dvc_mod, io);
|
||||
|
||||
return 0;
|
||||
@ -143,86 +204,6 @@ static int rsnd_dvc_stop(struct rsnd_mod *mod,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rsnd_dvc_volume_info(struct snd_kcontrol *kctrl,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
struct rsnd_mod *mod = snd_kcontrol_chip(kctrl);
|
||||
struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod);
|
||||
u8 *val = (u8 *)kctrl->private_value;
|
||||
|
||||
uinfo->count = RSND_DVC_VOLUME_NUM;
|
||||
uinfo->value.integer.min = 0;
|
||||
|
||||
if (val == dvc->volume) {
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
||||
uinfo->value.integer.max = RSND_DVC_VOLUME_MAX;
|
||||
} else {
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
|
||||
uinfo->value.integer.max = 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rsnd_dvc_volume_get(struct snd_kcontrol *kctrl,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
u8 *val = (u8 *)kctrl->private_value;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < RSND_DVC_VOLUME_NUM; i++)
|
||||
ucontrol->value.integer.value[i] = val[i];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rsnd_dvc_volume_put(struct snd_kcontrol *kctrl,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct rsnd_mod *mod = snd_kcontrol_chip(kctrl);
|
||||
u8 *val = (u8 *)kctrl->private_value;
|
||||
int i, change = 0;
|
||||
|
||||
for (i = 0; i < RSND_DVC_VOLUME_NUM; i++) {
|
||||
change |= (ucontrol->value.integer.value[i] != val[i]);
|
||||
val[i] = ucontrol->value.integer.value[i];
|
||||
}
|
||||
|
||||
if (change)
|
||||
rsnd_dvc_volume_update(mod);
|
||||
|
||||
return change;
|
||||
}
|
||||
|
||||
static int __rsnd_dvc_pcm_new(struct rsnd_mod *mod,
|
||||
struct rsnd_dai *rdai,
|
||||
struct snd_soc_pcm_runtime *rtd,
|
||||
const unsigned char *name,
|
||||
u8 *private)
|
||||
{
|
||||
struct snd_card *card = rtd->card->snd_card;
|
||||
struct snd_kcontrol *kctrl;
|
||||
struct snd_kcontrol_new knew = {
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = name,
|
||||
.info = rsnd_dvc_volume_info,
|
||||
.get = rsnd_dvc_volume_get,
|
||||
.put = rsnd_dvc_volume_put,
|
||||
.private_value = (unsigned long)private,
|
||||
};
|
||||
int ret;
|
||||
|
||||
kctrl = snd_ctl_new1(&knew, mod);
|
||||
if (!kctrl)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = snd_ctl_add(card, kctrl);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rsnd_dvc_pcm_new(struct rsnd_mod *mod,
|
||||
struct rsnd_dai *rdai,
|
||||
struct snd_soc_pcm_runtime *rtd)
|
||||
@ -232,18 +213,48 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod,
|
||||
int ret;
|
||||
|
||||
/* Volume */
|
||||
ret = __rsnd_dvc_pcm_new(mod, rdai, rtd,
|
||||
ret = rsnd_kctrl_new_m(mod, rdai, rtd,
|
||||
rsnd_dai_is_play(rdai, io) ?
|
||||
"DVC Out Playback Volume" : "DVC In Capture Volume",
|
||||
dvc->volume);
|
||||
rsnd_dvc_volume_update,
|
||||
&dvc->volume, 0x00800000 - 1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Mute */
|
||||
ret = __rsnd_dvc_pcm_new(mod, rdai, rtd,
|
||||
ret = rsnd_kctrl_new_m(mod, rdai, rtd,
|
||||
rsnd_dai_is_play(rdai, io) ?
|
||||
"DVC Out Mute Switch" : "DVC In Mute Switch",
|
||||
dvc->mute);
|
||||
rsnd_dvc_volume_update,
|
||||
&dvc->mute, 1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Ramp */
|
||||
ret = rsnd_kctrl_new_s(mod, rdai, rtd,
|
||||
rsnd_dai_is_play(rdai, io) ?
|
||||
"DVC Out Ramp Switch" : "DVC In Ramp Switch",
|
||||
rsnd_dvc_volume_update,
|
||||
&dvc->ren, 1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = rsnd_kctrl_new_e(mod, rdai, rtd,
|
||||
rsnd_dai_is_play(rdai, io) ?
|
||||
"DVC Out Ramp Up Rate" : "DVC In Ramp Up Rate",
|
||||
&dvc->rup,
|
||||
rsnd_dvc_volume_update,
|
||||
dvc_ramp_rate, ARRAY_SIZE(dvc_ramp_rate));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = rsnd_kctrl_new_e(mod, rdai, rtd,
|
||||
rsnd_dai_is_play(rdai, io) ?
|
||||
"DVC Out Ramp Down Rate" : "DVC In Ramp Down Rate",
|
||||
&dvc->rdown,
|
||||
rsnd_dvc_volume_update,
|
||||
dvc_ramp_rate, ARRAY_SIZE(dvc_ramp_rate));
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
@ -8,6 +8,17 @@
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
/*
|
||||
* #define DEBUG
|
||||
*
|
||||
* you can also add below in
|
||||
* ${LINUX}/drivers/base/regmap/regmap.c
|
||||
* for regmap debug
|
||||
*
|
||||
* #define LOG_DEVICE "xxxx.rcar_sound"
|
||||
*/
|
||||
|
||||
#include "rsnd.h"
|
||||
|
||||
struct rsnd_gen {
|
||||
@ -67,9 +78,10 @@ u32 rsnd_read(struct rsnd_priv *priv,
|
||||
if (!rsnd_is_accessible_reg(priv, gen, reg))
|
||||
return 0;
|
||||
|
||||
regmap_fields_read(gen->regs[reg], rsnd_mod_id(mod), &val);
|
||||
dev_dbg(dev, "r %s[%d] - %4d : %08x\n",
|
||||
rsnd_mod_name(mod), rsnd_mod_id(mod), reg, val);
|
||||
|
||||
dev_dbg(dev, "r %s - 0x%04d : %08x\n", rsnd_mod_name(mod), reg, val);
|
||||
regmap_fields_read(gen->regs[reg], rsnd_mod_id(mod), &val);
|
||||
|
||||
return val;
|
||||
}
|
||||
@ -84,9 +96,10 @@ void rsnd_write(struct rsnd_priv *priv,
|
||||
if (!rsnd_is_accessible_reg(priv, gen, reg))
|
||||
return;
|
||||
|
||||
regmap_fields_write(gen->regs[reg], rsnd_mod_id(mod), data);
|
||||
dev_dbg(dev, "w %s[%d] - %4d : %08x\n",
|
||||
rsnd_mod_name(mod), rsnd_mod_id(mod), reg, data);
|
||||
|
||||
dev_dbg(dev, "w %s - 0x%04d : %08x\n", rsnd_mod_name(mod), reg, data);
|
||||
regmap_fields_write(gen->regs[reg], rsnd_mod_id(mod), data);
|
||||
}
|
||||
|
||||
void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod,
|
||||
@ -98,11 +111,11 @@ void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod,
|
||||
if (!rsnd_is_accessible_reg(priv, gen, reg))
|
||||
return;
|
||||
|
||||
dev_dbg(dev, "b %s[%d] - %4d : %08x/%08x\n",
|
||||
rsnd_mod_name(mod), rsnd_mod_id(mod), reg, data, mask);
|
||||
|
||||
regmap_fields_update_bits(gen->regs[reg], rsnd_mod_id(mod),
|
||||
mask, data);
|
||||
|
||||
dev_dbg(dev, "b %s - 0x%04d : %08x/%08x\n",
|
||||
rsnd_mod_name(mod), reg, data, mask);
|
||||
}
|
||||
|
||||
#define rsnd_gen_regmap_init(priv, id_size, reg_id, conf) \
|
||||
@ -311,6 +324,9 @@ static int rsnd_gen2_probe(struct platform_device *pdev,
|
||||
RSND_GEN_M_REG(DVC_ADINR, 0xe08, 0x100),
|
||||
RSND_GEN_M_REG(DVC_DVUCR, 0xe10, 0x100),
|
||||
RSND_GEN_M_REG(DVC_ZCMCR, 0xe14, 0x100),
|
||||
RSND_GEN_M_REG(DVC_VRCTR, 0xe18, 0x100),
|
||||
RSND_GEN_M_REG(DVC_VRPDR, 0xe1c, 0x100),
|
||||
RSND_GEN_M_REG(DVC_VRDBR, 0xe20, 0x100),
|
||||
RSND_GEN_M_REG(DVC_VOL0R, 0xe28, 0x100),
|
||||
RSND_GEN_M_REG(DVC_VOL1R, 0xe2c, 0x100),
|
||||
RSND_GEN_M_REG(DVC_DVUER, 0xe48, 0x100),
|
||||
|
@ -91,6 +91,9 @@ enum rsnd_reg {
|
||||
RSND_REG_SHARE20,
|
||||
RSND_REG_SHARE21,
|
||||
RSND_REG_SHARE22,
|
||||
RSND_REG_SHARE23,
|
||||
RSND_REG_SHARE24,
|
||||
RSND_REG_SHARE25,
|
||||
|
||||
RSND_REG_MAX,
|
||||
};
|
||||
@ -129,6 +132,9 @@ enum rsnd_reg {
|
||||
#define RSND_REG_CMD_CTRL RSND_REG_SHARE20
|
||||
#define RSND_REG_CMDOUT_TIMSEL RSND_REG_SHARE21
|
||||
#define RSND_REG_BUSIF_DALIGN RSND_REG_SHARE22
|
||||
#define RSND_REG_DVC_VRCTR RSND_REG_SHARE23
|
||||
#define RSND_REG_DVC_VRPDR RSND_REG_SHARE24
|
||||
#define RSND_REG_DVC_VRDBR RSND_REG_SHARE25
|
||||
|
||||
struct rsnd_of_data;
|
||||
struct rsnd_priv;
|
||||
@ -200,6 +206,8 @@ struct rsnd_mod_ops {
|
||||
int (*pcm_new)(struct rsnd_mod *mod,
|
||||
struct rsnd_dai *rdai,
|
||||
struct snd_soc_pcm_runtime *rtd);
|
||||
int (*fallback)(struct rsnd_mod *mod,
|
||||
struct rsnd_dai *rdai);
|
||||
};
|
||||
|
||||
struct rsnd_dai_stream;
|
||||
@ -210,7 +218,35 @@ struct rsnd_mod {
|
||||
struct rsnd_mod_ops *ops;
|
||||
struct rsnd_dma dma;
|
||||
struct rsnd_dai_stream *io;
|
||||
u32 status;
|
||||
};
|
||||
/*
|
||||
* status
|
||||
*
|
||||
* bit
|
||||
* 0 0: probe 1: remove
|
||||
* 1 0: init 1: quit
|
||||
* 2 0: start 1: stop
|
||||
* 3 0: pcm_new
|
||||
* 4 0: fallback
|
||||
*/
|
||||
#define __rsnd_mod_shift_probe 0
|
||||
#define __rsnd_mod_shift_remove 0
|
||||
#define __rsnd_mod_shift_init 1
|
||||
#define __rsnd_mod_shift_quit 1
|
||||
#define __rsnd_mod_shift_start 2
|
||||
#define __rsnd_mod_shift_stop 2
|
||||
#define __rsnd_mod_shift_pcm_new 3
|
||||
#define __rsnd_mod_shift_fallback 4
|
||||
|
||||
#define __rsnd_mod_call_probe 0
|
||||
#define __rsnd_mod_call_remove 1
|
||||
#define __rsnd_mod_call_init 0
|
||||
#define __rsnd_mod_call_quit 1
|
||||
#define __rsnd_mod_call_start 0
|
||||
#define __rsnd_mod_call_stop 1
|
||||
#define __rsnd_mod_call_pcm_new 0
|
||||
#define __rsnd_mod_call_fallback 0
|
||||
|
||||
#define rsnd_mod_to_priv(mod) ((mod)->priv)
|
||||
#define rsnd_mod_to_dma(mod) (&(mod)->dma)
|
||||
@ -267,7 +303,8 @@ struct rsnd_dai *rsnd_dai_get(struct rsnd_priv *priv, int id);
|
||||
int rsnd_dai_is_play(struct rsnd_dai *rdai, struct rsnd_dai_stream *io);
|
||||
int rsnd_dai_id(struct rsnd_priv *priv, struct rsnd_dai *rdai);
|
||||
#define rsnd_dai_get_platform_info(rdai) ((rdai)->info)
|
||||
#define rsnd_io_to_runtime(io) ((io)->substream->runtime)
|
||||
#define rsnd_io_to_runtime(io) ((io)->substream ? \
|
||||
(io)->substream->runtime : NULL)
|
||||
|
||||
void rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int cnt);
|
||||
int rsnd_dai_pointer_offset(struct rsnd_dai_stream *io, int additional);
|
||||
@ -381,6 +418,51 @@ struct rsnd_priv {
|
||||
is_play; \
|
||||
})
|
||||
|
||||
/*
|
||||
* rsnd_kctrl
|
||||
*/
|
||||
struct rsnd_kctrl_cfg {
|
||||
unsigned int max;
|
||||
unsigned int size;
|
||||
u32 *val;
|
||||
const char * const *texts;
|
||||
void (*update)(struct rsnd_mod *mod);
|
||||
};
|
||||
|
||||
#define RSND_DVC_CHANNELS 2
|
||||
struct rsnd_kctrl_cfg_m {
|
||||
struct rsnd_kctrl_cfg cfg;
|
||||
u32 val[RSND_DVC_CHANNELS];
|
||||
};
|
||||
|
||||
struct rsnd_kctrl_cfg_s {
|
||||
struct rsnd_kctrl_cfg cfg;
|
||||
u32 val;
|
||||
};
|
||||
|
||||
int rsnd_kctrl_new_m(struct rsnd_mod *mod,
|
||||
struct rsnd_dai *rdai,
|
||||
struct snd_soc_pcm_runtime *rtd,
|
||||
const unsigned char *name,
|
||||
void (*update)(struct rsnd_mod *mod),
|
||||
struct rsnd_kctrl_cfg_m *_cfg,
|
||||
u32 max);
|
||||
int rsnd_kctrl_new_s(struct rsnd_mod *mod,
|
||||
struct rsnd_dai *rdai,
|
||||
struct snd_soc_pcm_runtime *rtd,
|
||||
const unsigned char *name,
|
||||
void (*update)(struct rsnd_mod *mod),
|
||||
struct rsnd_kctrl_cfg_s *_cfg,
|
||||
u32 max);
|
||||
int rsnd_kctrl_new_e(struct rsnd_mod *mod,
|
||||
struct rsnd_dai *rdai,
|
||||
struct snd_soc_pcm_runtime *rtd,
|
||||
const unsigned char *name,
|
||||
struct rsnd_kctrl_cfg_s *_cfg,
|
||||
void (*update)(struct rsnd_mod *mod),
|
||||
const char * const *texts,
|
||||
u32 max);
|
||||
|
||||
/*
|
||||
* R-Car SRC
|
||||
*/
|
||||
@ -395,10 +477,11 @@ int rsnd_src_ssiu_start(struct rsnd_mod *ssi_mod,
|
||||
struct rsnd_dai *rdai,
|
||||
int use_busif);
|
||||
int rsnd_src_ssiu_stop(struct rsnd_mod *ssi_mod,
|
||||
struct rsnd_dai *rdai,
|
||||
int use_busif);
|
||||
int rsnd_src_enable_ssi_irq(struct rsnd_mod *ssi_mod,
|
||||
struct rsnd_dai *rdai);
|
||||
int rsnd_src_ssi_irq_enable(struct rsnd_mod *ssi_mod,
|
||||
struct rsnd_dai *rdai);
|
||||
int rsnd_src_ssi_irq_disable(struct rsnd_mod *ssi_mod,
|
||||
struct rsnd_dai *rdai);
|
||||
|
||||
#define rsnd_src_nr(priv) ((priv)->src_nr)
|
||||
|
||||
@ -410,6 +493,7 @@ int rsnd_ssi_probe(struct platform_device *pdev,
|
||||
struct rsnd_priv *priv);
|
||||
struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id);
|
||||
int rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod);
|
||||
int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod);
|
||||
|
||||
/*
|
||||
* R-Car DVC
|
||||
|
@ -175,30 +175,47 @@ int rsnd_src_ssiu_start(struct rsnd_mod *ssi_mod,
|
||||
}
|
||||
|
||||
int rsnd_src_ssiu_stop(struct rsnd_mod *ssi_mod,
|
||||
struct rsnd_dai *rdai,
|
||||
int use_busif)
|
||||
struct rsnd_dai *rdai)
|
||||
{
|
||||
/*
|
||||
* DMA settings for SSIU
|
||||
*/
|
||||
if (use_busif)
|
||||
rsnd_mod_write(ssi_mod, SSI_CTRL, 0);
|
||||
rsnd_mod_write(ssi_mod, SSI_CTRL, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rsnd_src_enable_ssi_irq(struct rsnd_mod *ssi_mod,
|
||||
int rsnd_src_ssi_irq_enable(struct rsnd_mod *ssi_mod,
|
||||
struct rsnd_dai *rdai)
|
||||
{
|
||||
struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod);
|
||||
|
||||
/* enable PIO interrupt if Gen2 */
|
||||
if (rsnd_is_gen2(priv))
|
||||
if (rsnd_is_gen1(priv))
|
||||
return 0;
|
||||
|
||||
/* enable SSI interrupt if Gen2 */
|
||||
if (rsnd_ssi_is_dma_mode(ssi_mod))
|
||||
rsnd_mod_write(ssi_mod, INT_ENABLE, 0x0e000000);
|
||||
else
|
||||
rsnd_mod_write(ssi_mod, INT_ENABLE, 0x0f000000);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rsnd_src_ssi_irq_disable(struct rsnd_mod *ssi_mod,
|
||||
struct rsnd_dai *rdai)
|
||||
{
|
||||
struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod);
|
||||
|
||||
if (rsnd_is_gen1(priv))
|
||||
return 0;
|
||||
|
||||
/* disable SSI interrupt if Gen2 */
|
||||
rsnd_mod_write(ssi_mod, INT_ENABLE, 0x00000000);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv,
|
||||
struct rsnd_dai_stream *io,
|
||||
struct snd_pcm_runtime *runtime)
|
||||
@ -239,12 +256,6 @@ static int rsnd_src_set_convert_rate(struct rsnd_mod *mod,
|
||||
rsnd_mod_write(mod, SRC_SWRSR, 0);
|
||||
rsnd_mod_write(mod, SRC_SWRSR, 1);
|
||||
|
||||
/*
|
||||
* Initialize the operation of the SRC internal circuits
|
||||
* see rsnd_src_start()
|
||||
*/
|
||||
rsnd_mod_write(mod, SRC_SRCIR, 1);
|
||||
|
||||
/* Set channel number and output bit length */
|
||||
rsnd_mod_write(mod, SRC_ADINR, rsnd_get_adinr(mod));
|
||||
|
||||
@ -269,6 +280,12 @@ static int rsnd_src_init(struct rsnd_mod *mod,
|
||||
|
||||
clk_prepare_enable(src->clk);
|
||||
|
||||
/*
|
||||
* Initialize the operation of the SRC internal circuits
|
||||
* see rsnd_src_start()
|
||||
*/
|
||||
rsnd_mod_write(mod, SRC_SRCIR, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -282,32 +299,20 @@ static int rsnd_src_quit(struct rsnd_mod *mod,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rsnd_src_start(struct rsnd_mod *mod,
|
||||
struct rsnd_dai *rdai)
|
||||
static int rsnd_src_start(struct rsnd_mod *mod)
|
||||
{
|
||||
struct rsnd_src *src = rsnd_mod_to_src(mod);
|
||||
|
||||
/*
|
||||
* Cancel the initialization and operate the SRC function
|
||||
* see rsnd_src_set_convert_rate()
|
||||
* see rsnd_src_init()
|
||||
*/
|
||||
rsnd_mod_write(mod, SRC_SRCIR, 0);
|
||||
|
||||
if (rsnd_src_convert_rate(src))
|
||||
rsnd_mod_write(mod, SRC_ROUTE_MODE0, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int rsnd_src_stop(struct rsnd_mod *mod,
|
||||
struct rsnd_dai *rdai)
|
||||
static int rsnd_src_stop(struct rsnd_mod *mod)
|
||||
{
|
||||
struct rsnd_src *src = rsnd_mod_to_src(mod);
|
||||
|
||||
if (rsnd_src_convert_rate(src))
|
||||
rsnd_mod_write(mod, SRC_ROUTE_MODE0, 0);
|
||||
|
||||
/* nothing to do */
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -414,6 +419,7 @@ static int rsnd_src_set_convert_timing_gen1(struct rsnd_mod *mod,
|
||||
static int rsnd_src_set_convert_rate_gen1(struct rsnd_mod *mod,
|
||||
struct rsnd_dai *rdai)
|
||||
{
|
||||
struct rsnd_src *src = rsnd_mod_to_src(mod);
|
||||
int ret;
|
||||
|
||||
ret = rsnd_src_set_convert_rate(mod, rdai);
|
||||
@ -427,6 +433,10 @@ static int rsnd_src_set_convert_rate_gen1(struct rsnd_mod *mod,
|
||||
rsnd_mod_write(mod, SRC_MNFSR,
|
||||
rsnd_mod_read(mod, SRC_IFSVR) / 100 * 98);
|
||||
|
||||
/* Gen1/Gen2 are not compatible */
|
||||
if (rsnd_src_convert_rate(src))
|
||||
rsnd_mod_write(mod, SRC_ROUTE_MODE0, 1);
|
||||
|
||||
/* no SRC_BFSSR settings, since SRC_SRCCR::BUFMD is 0 */
|
||||
|
||||
return 0;
|
||||
@ -438,7 +448,8 @@ static int rsnd_src_probe_gen1(struct rsnd_mod *mod,
|
||||
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
|
||||
struct device *dev = rsnd_priv_to_dev(priv);
|
||||
|
||||
dev_dbg(dev, "%s (Gen1) is probed\n", rsnd_mod_name(mod));
|
||||
dev_dbg(dev, "%s[%d] (Gen1) is probed\n",
|
||||
rsnd_mod_name(mod), rsnd_mod_id(mod));
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -474,7 +485,7 @@ static int rsnd_src_start_gen1(struct rsnd_mod *mod,
|
||||
|
||||
rsnd_mod_bset(mod, SRC_ROUTE_CTRL, (1 << id), (1 << id));
|
||||
|
||||
return rsnd_src_start(mod, rdai);
|
||||
return rsnd_src_start(mod);
|
||||
}
|
||||
|
||||
static int rsnd_src_stop_gen1(struct rsnd_mod *mod,
|
||||
@ -484,7 +495,7 @@ static int rsnd_src_stop_gen1(struct rsnd_mod *mod,
|
||||
|
||||
rsnd_mod_bset(mod, SRC_ROUTE_CTRL, (1 << id), 0);
|
||||
|
||||
return rsnd_src_stop(mod, rdai);
|
||||
return rsnd_src_stop(mod);
|
||||
}
|
||||
|
||||
static struct rsnd_mod_ops rsnd_src_gen1_ops = {
|
||||
@ -507,16 +518,17 @@ static int rsnd_src_set_convert_rate_gen2(struct rsnd_mod *mod,
|
||||
struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
|
||||
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
|
||||
struct rsnd_src *src = rsnd_mod_to_src(mod);
|
||||
u32 convert_rate = rsnd_src_convert_rate(src);
|
||||
uint ratio;
|
||||
int ret;
|
||||
|
||||
/* 6 - 1/6 are very enough ratio for SRC_BSDSR */
|
||||
if (!rsnd_src_convert_rate(src))
|
||||
if (!convert_rate)
|
||||
ratio = 0;
|
||||
else if (rsnd_src_convert_rate(src) > runtime->rate)
|
||||
ratio = 100 * rsnd_src_convert_rate(src) / runtime->rate;
|
||||
else if (convert_rate > runtime->rate)
|
||||
ratio = 100 * convert_rate / runtime->rate;
|
||||
else
|
||||
ratio = 100 * runtime->rate / rsnd_src_convert_rate(src);
|
||||
ratio = 100 * runtime->rate / convert_rate;
|
||||
|
||||
if (ratio > 600) {
|
||||
dev_err(dev, "FSO/FSI ratio error\n");
|
||||
@ -529,6 +541,11 @@ static int rsnd_src_set_convert_rate_gen2(struct rsnd_mod *mod,
|
||||
|
||||
rsnd_mod_write(mod, SRC_SRCCR, 0x00011110);
|
||||
|
||||
if (convert_rate) {
|
||||
/* Gen1/Gen2 are not compatible */
|
||||
rsnd_mod_write(mod, SRC_ROUTE_MODE0, 1);
|
||||
}
|
||||
|
||||
switch (rsnd_mod_id(mod)) {
|
||||
case 5:
|
||||
case 6:
|
||||
@ -578,9 +595,11 @@ static int rsnd_src_probe_gen2(struct rsnd_mod *mod,
|
||||
rsnd_info_is_playback(priv, src),
|
||||
src->info->dma_id);
|
||||
if (ret < 0)
|
||||
dev_err(dev, "SRC DMA failed\n");
|
||||
|
||||
dev_dbg(dev, "%s (Gen2) is probed\n", rsnd_mod_name(mod));
|
||||
dev_err(dev, "%s[%d] (Gen2) failed\n",
|
||||
rsnd_mod_name(mod), rsnd_mod_id(mod));
|
||||
else
|
||||
dev_dbg(dev, "%s[%d] (Gen2) is probed\n",
|
||||
rsnd_mod_name(mod), rsnd_mod_id(mod));
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -624,7 +643,7 @@ static int rsnd_src_start_gen2(struct rsnd_mod *mod,
|
||||
|
||||
rsnd_mod_write(mod, SRC_CTRL, val);
|
||||
|
||||
return rsnd_src_start(mod, rdai);
|
||||
return rsnd_src_start(mod);
|
||||
}
|
||||
|
||||
static int rsnd_src_stop_gen2(struct rsnd_mod *mod,
|
||||
@ -636,7 +655,7 @@ static int rsnd_src_stop_gen2(struct rsnd_mod *mod,
|
||||
|
||||
rsnd_dma_stop(rsnd_mod_to_dma(&src->mod));
|
||||
|
||||
return rsnd_src_stop(mod, rdai);
|
||||
return rsnd_src_stop(mod);
|
||||
}
|
||||
|
||||
static struct rsnd_mod_ops rsnd_src_gen2_ops = {
|
||||
|
@ -68,7 +68,6 @@ struct rsnd_ssi {
|
||||
struct rsnd_dai *rdai;
|
||||
u32 cr_own;
|
||||
u32 cr_clk;
|
||||
u32 cr_etc;
|
||||
int err;
|
||||
unsigned int usrcnt;
|
||||
unsigned int rate;
|
||||
@ -83,7 +82,7 @@ struct rsnd_ssi {
|
||||
#define rsnd_ssi_nr(priv) ((priv)->ssi_nr)
|
||||
#define rsnd_mod_to_ssi(_mod) container_of((_mod), struct rsnd_ssi, mod)
|
||||
#define rsnd_dma_to_ssi(dma) rsnd_mod_to_ssi(rsnd_dma_to_mod(dma))
|
||||
#define rsnd_ssi_pio_available(ssi) ((ssi)->info->pio_irq > 0)
|
||||
#define rsnd_ssi_pio_available(ssi) ((ssi)->info->irq > 0)
|
||||
#define rsnd_ssi_dma_available(ssi) \
|
||||
rsnd_dma_available(rsnd_mod_to_dma(&(ssi)->mod))
|
||||
#define rsnd_ssi_clk_from_parent(ssi) ((ssi)->parent)
|
||||
@ -96,6 +95,9 @@ static int rsnd_ssi_use_busif(struct rsnd_mod *mod)
|
||||
struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
|
||||
int use_busif = 0;
|
||||
|
||||
if (!rsnd_ssi_is_dma_mode(mod))
|
||||
return 0;
|
||||
|
||||
if (!(rsnd_ssi_mode_flags(ssi) & RSND_SSI_NO_BUSIF))
|
||||
use_busif = 1;
|
||||
if (rsnd_io_to_mod_src(io))
|
||||
@ -159,7 +161,8 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi,
|
||||
ssi->cr_clk = FORCE | SWL_32 |
|
||||
SCKD | SWSD | CKDV(j);
|
||||
|
||||
dev_dbg(dev, "ssi%d outputs %u Hz\n",
|
||||
dev_dbg(dev, "%s[%d] outputs %u Hz\n",
|
||||
rsnd_mod_name(&ssi->mod),
|
||||
rsnd_mod_id(&ssi->mod), rate);
|
||||
|
||||
return 0;
|
||||
@ -184,6 +187,7 @@ static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi,
|
||||
{
|
||||
struct rsnd_priv *priv = rsnd_mod_to_priv(&ssi->mod);
|
||||
struct device *dev = rsnd_priv_to_dev(priv);
|
||||
u32 cr_mode;
|
||||
u32 cr;
|
||||
|
||||
if (0 == ssi->usrcnt) {
|
||||
@ -197,16 +201,29 @@ static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi,
|
||||
}
|
||||
}
|
||||
|
||||
cr_mode = rsnd_ssi_is_dma_mode(&ssi->mod) ?
|
||||
DMEN : /* DMA : enable DMA */
|
||||
DIEN; /* PIO : enable Data interrupt */
|
||||
|
||||
|
||||
cr = ssi->cr_own |
|
||||
ssi->cr_clk |
|
||||
ssi->cr_etc |
|
||||
EN;
|
||||
cr_mode |
|
||||
UIEN | OIEN | EN;
|
||||
|
||||
rsnd_mod_write(&ssi->mod, SSICR, cr);
|
||||
|
||||
/* enable WS continue */
|
||||
if (rsnd_dai_is_clk_master(rdai))
|
||||
rsnd_mod_write(&ssi->mod, SSIWSR, CONT);
|
||||
|
||||
/* clear error status */
|
||||
rsnd_mod_write(&ssi->mod, SSISR, 0);
|
||||
|
||||
ssi->usrcnt++;
|
||||
|
||||
dev_dbg(dev, "ssi%d hw started\n", rsnd_mod_id(&ssi->mod));
|
||||
dev_dbg(dev, "%s[%d] hw started\n",
|
||||
rsnd_mod_name(&ssi->mod), rsnd_mod_id(&ssi->mod));
|
||||
}
|
||||
|
||||
static void rsnd_ssi_hw_stop(struct rsnd_ssi *ssi,
|
||||
@ -249,7 +266,8 @@ static void rsnd_ssi_hw_stop(struct rsnd_ssi *ssi,
|
||||
clk_disable_unprepare(ssi->clk);
|
||||
}
|
||||
|
||||
dev_dbg(dev, "ssi%d hw stopped\n", rsnd_mod_id(&ssi->mod));
|
||||
dev_dbg(dev, "%s[%d] hw stopped\n",
|
||||
rsnd_mod_name(&ssi->mod), rsnd_mod_id(&ssi->mod));
|
||||
}
|
||||
|
||||
/*
|
||||
@ -334,25 +352,54 @@ static void rsnd_ssi_record_error(struct rsnd_ssi *ssi, u32 status)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* SSI PIO
|
||||
*/
|
||||
static irqreturn_t rsnd_ssi_pio_interrupt(int irq, void *data)
|
||||
static int rsnd_ssi_start(struct rsnd_mod *mod,
|
||||
struct rsnd_dai *rdai)
|
||||
{
|
||||
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
|
||||
struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
|
||||
|
||||
rsnd_src_ssiu_start(mod, rdai, rsnd_ssi_use_busif(mod));
|
||||
|
||||
rsnd_ssi_hw_start(ssi, rdai, io);
|
||||
|
||||
rsnd_src_ssi_irq_enable(mod, rdai);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rsnd_ssi_stop(struct rsnd_mod *mod,
|
||||
struct rsnd_dai *rdai)
|
||||
{
|
||||
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
|
||||
|
||||
rsnd_src_ssi_irq_disable(mod, rdai);
|
||||
|
||||
rsnd_ssi_record_error(ssi, rsnd_mod_read(mod, SSISR));
|
||||
|
||||
rsnd_ssi_hw_stop(ssi, rdai);
|
||||
|
||||
rsnd_src_ssiu_stop(mod, rdai);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t rsnd_ssi_interrupt(int irq, void *data)
|
||||
{
|
||||
struct rsnd_ssi *ssi = data;
|
||||
struct rsnd_dai *rdai = ssi->rdai;
|
||||
struct rsnd_mod *mod = &ssi->mod;
|
||||
struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
|
||||
u32 status = rsnd_mod_read(mod, SSISR);
|
||||
irqreturn_t ret = IRQ_NONE;
|
||||
|
||||
if (io && (status & DIRQ)) {
|
||||
struct rsnd_dai *rdai = ssi->rdai;
|
||||
if (!io)
|
||||
return IRQ_NONE;
|
||||
|
||||
/* PIO only */
|
||||
if (status & DIRQ) {
|
||||
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
|
||||
u32 *buf = (u32 *)(runtime->dma_area +
|
||||
rsnd_dai_pointer_offset(io, 0));
|
||||
|
||||
rsnd_ssi_record_error(ssi, status);
|
||||
|
||||
/*
|
||||
* 8/16/32 data can be assesse to TDR/RDR register
|
||||
* directly as 32bit data
|
||||
@ -364,73 +411,60 @@ static irqreturn_t rsnd_ssi_pio_interrupt(int irq, void *data)
|
||||
*buf = rsnd_mod_read(mod, SSIRDR);
|
||||
|
||||
rsnd_dai_pointer_update(io, sizeof(*buf));
|
||||
|
||||
ret = IRQ_HANDLED;
|
||||
}
|
||||
|
||||
return ret;
|
||||
/* PIO / DMA */
|
||||
if (status & (UIRQ | OIRQ)) {
|
||||
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
|
||||
struct device *dev = rsnd_priv_to_dev(priv);
|
||||
|
||||
/*
|
||||
* restart SSI
|
||||
*/
|
||||
rsnd_ssi_stop(mod, rdai);
|
||||
rsnd_ssi_start(mod, rdai);
|
||||
|
||||
dev_dbg(dev, "%s[%d] restart\n",
|
||||
rsnd_mod_name(mod), rsnd_mod_id(mod));
|
||||
}
|
||||
|
||||
rsnd_ssi_record_error(ssi, status);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/*
|
||||
* SSI PIO
|
||||
*/
|
||||
static int rsnd_ssi_pio_probe(struct rsnd_mod *mod,
|
||||
struct rsnd_dai *rdai)
|
||||
{
|
||||
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
|
||||
struct device *dev = rsnd_priv_to_dev(priv);
|
||||
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
|
||||
int irq = ssi->info->pio_irq;
|
||||
int ret;
|
||||
|
||||
ret = devm_request_irq(dev, irq,
|
||||
rsnd_ssi_pio_interrupt,
|
||||
ret = devm_request_irq(dev, ssi->info->irq,
|
||||
rsnd_ssi_interrupt,
|
||||
IRQF_SHARED,
|
||||
dev_name(dev), ssi);
|
||||
if (ret)
|
||||
dev_err(dev, "SSI request interrupt failed\n");
|
||||
|
||||
dev_dbg(dev, "%s (PIO) is probed\n", rsnd_mod_name(mod));
|
||||
dev_err(dev, "%s[%d] (PIO) request interrupt failed\n",
|
||||
rsnd_mod_name(mod), rsnd_mod_id(mod));
|
||||
else
|
||||
dev_dbg(dev, "%s[%d] (PIO) is probed\n",
|
||||
rsnd_mod_name(mod), rsnd_mod_id(mod));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rsnd_ssi_pio_start(struct rsnd_mod *mod,
|
||||
struct rsnd_dai *rdai)
|
||||
{
|
||||
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
|
||||
struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
|
||||
|
||||
/* enable PIO IRQ */
|
||||
ssi->cr_etc = UIEN | OIEN | DIEN;
|
||||
|
||||
rsnd_src_ssiu_start(mod, rdai, 0);
|
||||
|
||||
rsnd_src_enable_ssi_irq(mod, rdai);
|
||||
|
||||
rsnd_ssi_hw_start(ssi, rdai, io);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rsnd_ssi_pio_stop(struct rsnd_mod *mod,
|
||||
struct rsnd_dai *rdai)
|
||||
{
|
||||
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
|
||||
|
||||
ssi->cr_etc = 0;
|
||||
|
||||
rsnd_ssi_hw_stop(ssi, rdai);
|
||||
|
||||
rsnd_src_ssiu_stop(mod, rdai, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct rsnd_mod_ops rsnd_ssi_pio_ops = {
|
||||
.name = SSI_NAME,
|
||||
.probe = rsnd_ssi_pio_probe,
|
||||
.init = rsnd_ssi_init,
|
||||
.quit = rsnd_ssi_quit,
|
||||
.start = rsnd_ssi_pio_start,
|
||||
.stop = rsnd_ssi_pio_stop,
|
||||
.start = rsnd_ssi_start,
|
||||
.stop = rsnd_ssi_stop,
|
||||
};
|
||||
|
||||
static int rsnd_ssi_dma_probe(struct rsnd_mod *mod,
|
||||
@ -442,15 +476,28 @@ static int rsnd_ssi_dma_probe(struct rsnd_mod *mod,
|
||||
int dma_id = ssi->info->dma_id;
|
||||
int ret;
|
||||
|
||||
ret = devm_request_irq(dev, ssi->info->irq,
|
||||
rsnd_ssi_interrupt,
|
||||
IRQF_SHARED,
|
||||
dev_name(dev), ssi);
|
||||
if (ret)
|
||||
goto rsnd_ssi_dma_probe_fail;
|
||||
|
||||
ret = rsnd_dma_init(
|
||||
priv, rsnd_mod_to_dma(mod),
|
||||
rsnd_info_is_playback(priv, ssi),
|
||||
dma_id);
|
||||
if (ret)
|
||||
goto rsnd_ssi_dma_probe_fail;
|
||||
|
||||
if (ret < 0)
|
||||
dev_err(dev, "SSI DMA failed\n");
|
||||
dev_dbg(dev, "%s[%d] (DMA) is probed\n",
|
||||
rsnd_mod_name(mod), rsnd_mod_id(mod));
|
||||
|
||||
dev_dbg(dev, "%s (DMA) is probed\n", rsnd_mod_name(mod));
|
||||
return ret;
|
||||
|
||||
rsnd_ssi_dma_probe_fail:
|
||||
dev_err(dev, "%s[%d] (DMA) is failed\n",
|
||||
rsnd_mod_name(mod), rsnd_mod_id(mod));
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -458,49 +505,60 @@ static int rsnd_ssi_dma_probe(struct rsnd_mod *mod,
|
||||
static int rsnd_ssi_dma_remove(struct rsnd_mod *mod,
|
||||
struct rsnd_dai *rdai)
|
||||
{
|
||||
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
|
||||
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
|
||||
struct device *dev = rsnd_priv_to_dev(priv);
|
||||
int irq = ssi->info->irq;
|
||||
|
||||
rsnd_dma_quit(rsnd_mod_to_priv(mod), rsnd_mod_to_dma(mod));
|
||||
|
||||
/* PIO will request IRQ again */
|
||||
devm_free_irq(dev, irq, ssi);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rsnd_ssi_fallback(struct rsnd_mod *mod,
|
||||
struct rsnd_dai *rdai)
|
||||
{
|
||||
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
|
||||
struct device *dev = rsnd_priv_to_dev(priv);
|
||||
|
||||
/*
|
||||
* fallback to PIO
|
||||
*
|
||||
* SSI .probe might be called again.
|
||||
* see
|
||||
* rsnd_rdai_continuance_probe()
|
||||
*/
|
||||
mod->ops = &rsnd_ssi_pio_ops;
|
||||
|
||||
dev_info(dev, "%s[%d] fallback to PIO mode\n",
|
||||
rsnd_mod_name(mod), rsnd_mod_id(mod));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rsnd_ssi_dma_start(struct rsnd_mod *mod,
|
||||
struct rsnd_dai *rdai)
|
||||
{
|
||||
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
|
||||
struct rsnd_dma *dma = rsnd_mod_to_dma(&ssi->mod);
|
||||
struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
|
||||
struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
|
||||
|
||||
/* enable DMA transfer */
|
||||
ssi->cr_etc = DMEN;
|
||||
|
||||
rsnd_src_ssiu_start(mod, rdai, rsnd_ssi_use_busif(mod));
|
||||
rsnd_ssi_start(mod, rdai);
|
||||
|
||||
rsnd_dma_start(dma);
|
||||
|
||||
rsnd_ssi_hw_start(ssi, ssi->rdai, io);
|
||||
|
||||
/* enable WS continue */
|
||||
if (rsnd_dai_is_clk_master(rdai))
|
||||
rsnd_mod_write(&ssi->mod, SSIWSR, CONT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rsnd_ssi_dma_stop(struct rsnd_mod *mod,
|
||||
struct rsnd_dai *rdai)
|
||||
{
|
||||
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
|
||||
struct rsnd_dma *dma = rsnd_mod_to_dma(&ssi->mod);
|
||||
|
||||
ssi->cr_etc = 0;
|
||||
|
||||
rsnd_ssi_record_error(ssi, rsnd_mod_read(mod, SSISR));
|
||||
|
||||
rsnd_ssi_hw_stop(ssi, rdai);
|
||||
struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
|
||||
|
||||
rsnd_dma_stop(dma);
|
||||
|
||||
rsnd_src_ssiu_stop(mod, rdai, 1);
|
||||
rsnd_ssi_stop(mod, rdai);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -519,8 +577,15 @@ static struct rsnd_mod_ops rsnd_ssi_dma_ops = {
|
||||
.quit = rsnd_ssi_quit,
|
||||
.start = rsnd_ssi_dma_start,
|
||||
.stop = rsnd_ssi_dma_stop,
|
||||
.fallback = rsnd_ssi_fallback,
|
||||
};
|
||||
|
||||
int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod)
|
||||
{
|
||||
return mod->ops == &rsnd_ssi_dma_ops;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Non SSI
|
||||
*/
|
||||
@ -614,7 +679,7 @@ static void rsnd_of_parse_ssi(struct platform_device *pdev,
|
||||
/*
|
||||
* irq
|
||||
*/
|
||||
ssi_info->pio_irq = irq_of_parse_and_map(np, 0);
|
||||
ssi_info->irq = irq_of_parse_and_map(np, 0);
|
||||
|
||||
/*
|
||||
* DMA
|
||||
|
Loading…
Reference in New Issue
Block a user