Merge remote-tracking branches 'asoc/topic/rt5645', 'asoc/topic/rt5670', 'asoc/topic/rt5677', 'asoc/topic/samsung' and 'asoc/topic/sgtl5000' into asoc-next

This commit is contained in:
Mark Brown 2014-12-08 13:12:09 +00:00
23 changed files with 2253 additions and 234 deletions

View File

@ -0,0 +1,24 @@
Audio Binding for Arndale boards
Required properties:
- compatible : Can be the following,
"samsung,arndale-rt5631"
- samsung,audio-cpu: The phandle of the Samsung I2S controller
- samsung,audio-codec: The phandle of the audio codec
Optional:
- samsung,model: The name of the sound-card
Arndale Boards has many audio daughter cards, one of them is
rt5631/alc5631. Below example shows audio bindings for rt5631/
alc5631 based codec.
Example:
sound {
compatible = "samsung,arndale-rt5631";
samsung,audio-cpu = <&i2s0>
samsung,audio-codec = <&rt5631>;
};

View File

@ -27,6 +27,21 @@ Optional properties:
Boolean. Indicate MIC1/2 input and LOUT1/2/3 outputs are differential,
rather than single-ended.
- realtek,gpio-config
Array of six 8bit elements that configures GPIO.
0 - floating (reset value)
1 - pull down
2 - pull up
- realtek,jd1-gpio
Configures GPIO Mic Jack detection 1.
Select 0 ~ 3 as OFF, GPIO1, GPIO2 and GPIO3 respectively.
- realtek,jd2-gpio
- realtek,jd3-gpio
Configures GPIO Mic Jack detection 2 and 3.
Select 0 ~ 3 as OFF, GPIO4, GPIO5 and GPIO6 respectively.
Pins on the device (for linking into audio routes):
* IN1P
@ -56,4 +71,6 @@ rt5677 {
realtek,pow-ldo2-gpio =
<&gpio TEGRA_GPIO(V, 3) GPIO_ACTIVE_HIGH>;
realtek,in1-differential = "true";
realtek,gpio-config = /bits/ 8 <0 0 0 0 0 2>; /* pull up GPIO6 */
realtek,jd2-gpio = <3>; /* Enables Jack detection for GPIO6 */
};

View File

@ -6,10 +6,17 @@ Required SoC Specific Properties:
- samsung,s3c6410-i2s: for 8/16/24bit stereo I2S.
- samsung,s5pv210-i2s: for 8/16/24bit multichannel(5.1) I2S with
secondary fifo, s/w reset control and internal mux for root clk src.
- samsung,exynos5420-i2s: for 8/16/24bit multichannel(7.1) I2S with
secondary fifo, s/w reset control, internal mux for root clk src and
TDM support. TDM (Time division multiplexing) is to allow transfer of
multiple channel audio data on single data line.
- samsung,exynos5420-i2s: for 8/16/24bit multichannel(5.1) I2S for
playback, sterio channel capture, secondary fifo using internal
or external dma, s/w reset control, internal mux for root clk src
and 7.1 channel TDM support for playback. TDM (Time division multiplexing)
is to allow transfer of multiple channel audio data on single data line.
- samsung,exynos7-i2s: with all the available features of exynos5 i2s,
exynos7 I2S has 7.1 channel TDM support for capture, secondary fifo
with only external dma and more no.of root clk sampling frequencies.
- samsung,exynos7-i2s1: I2S1 on previous samsung platforms supports
stereo channels. exynos7 i2s1 upgraded to 5.1 multichannel with
slightly modified bit offsets.
- reg: physical base address of the controller and length of memory mapped
region.

View File

@ -7,6 +7,17 @@ Required properties:
- clocks : the clock provider of SYS_MCLK
- micbias-resistor-k-ohms : the bias resistor to be used in kOmhs
The resistor can take values of 2k, 4k or 8k.
If set to 0 it will be off.
If this node is not mentioned or if the value is unknown, then
micbias resistor is set to 4K.
- micbias-voltage-m-volts : the bias voltage to be used in mVolts
The voltage can take values from 1.25V to 3V by 250mV steps
If this node is not mentionned or the value is unknown, then
the value is set to 1.25V.
- VDDA-supply : the regulator provider of VDDA
- VDDIO-supply: the regulator provider of VDDIO
@ -21,6 +32,8 @@ codec: sgtl5000@0a {
compatible = "fsl,sgtl5000";
reg = <0x0a>;
clocks = <&clks 150>;
micbias-resistor-k-ohms = <2>;
micbias-voltage-m-volts = <2250>;
VDDA-supply = <&reg_3p3v>;
VDDIO-supply = <&reg_3p3v>;
};

View File

@ -27,6 +27,7 @@ struct samsung_i2s {
#define QUIRK_NO_MUXPSR (1 << 2)
#define QUIRK_NEED_RSTCLR (1 << 3)
#define QUIRK_SUPPORTS_TDM (1 << 4)
#define QUIRK_SUPPORTS_IDMA (1 << 5)
/* Quirks of the I2S controller */
u32 quirks;
dma_addr_t idma_addr;

View File

@ -23,6 +23,10 @@ struct rt5645_platform_data {
unsigned int hp_det_gpio;
bool gpio_hp_det_active_high;
/* true if codec's jd function is used */
bool en_jd_func;
unsigned int jd_mode;
};
#endif

View File

@ -27,6 +27,16 @@ struct rt5677_platform_data {
bool lout3_diff;
/* DMIC2 clock source selection */
enum rt5677_dmic2_clk dmic2_clk_pin;
/* configures GPIO, 0 - floating, 1 - pulldown, 2 - pullup */
u8 gpio_config[6];
/* jd1 can select 0 ~ 3 as OFF, GPIO1, GPIO2 and GPIO3 respectively */
unsigned int jd1_gpio;
/* jd2 and jd3 can select 0 ~ 3 as
OFF, GPIO4, GPIO5 and GPIO6 respectively */
unsigned int jd2_gpio;
unsigned int jd3_gpio;
};
#endif

View File

@ -86,7 +86,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_RT5645 if I2C
select SND_SOC_RT5651 if I2C
select SND_SOC_RT5670 if I2C
select SND_SOC_RT5677 if I2C
select SND_SOC_RT5677 if I2C && SPI_MASTER
select SND_SOC_SGTL5000 if I2C
select SND_SOC_SI476X if MFD_SI476X_CORE
select SND_SOC_SIRF_AUDIO_CODEC
@ -519,6 +519,10 @@ config SND_SOC_RT5670
config SND_SOC_RT5677
tristate
config SND_SOC_RT5677_SPI
tristate
default SND_SOC_RT5677
#Freescale sgtl5000 codec
config SND_SOC_SGTL5000
tristate "Freescale SGTL5000 CODEC"

View File

@ -82,6 +82,7 @@ snd-soc-rt5645-objs := rt5645.o
snd-soc-rt5651-objs := rt5651.o
snd-soc-rt5670-objs := rt5670.o
snd-soc-rt5677-objs := rt5677.o
snd-soc-rt5677-spi-objs := rt5677-spi.o
snd-soc-sgtl5000-objs := sgtl5000.o
snd-soc-alc5623-objs := alc5623.o
snd-soc-alc5632-objs := alc5632.o
@ -260,6 +261,7 @@ obj-$(CONFIG_SND_SOC_RT5645) += snd-soc-rt5645.o
obj-$(CONFIG_SND_SOC_RT5651) += snd-soc-rt5651.o
obj-$(CONFIG_SND_SOC_RT5670) += snd-soc-rt5670.o
obj-$(CONFIG_SND_SOC_RT5677) += snd-soc-rt5677.o
obj-$(CONFIG_SND_SOC_RT5677_SPI) += snd-soc-rt5677-spi.o
obj-$(CONFIG_SND_SOC_SGTL5000) += snd-soc-sgtl5000.o
obj-$(CONFIG_SND_SOC_SIGMADSP) += snd-soc-sigmadsp.o
obj-$(CONFIG_SND_SOC_SIGMADSP_I2C) += snd-soc-sigmadsp-i2c.o

View File

@ -554,6 +554,53 @@ static int is_sys_clk_from_pll(struct snd_soc_dapm_widget *source,
return 0;
}
static int is_using_asrc(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink)
{
unsigned int reg, shift, val;
switch (source->shift) {
case 0:
reg = RT5645_ASRC_3;
shift = 0;
break;
case 1:
reg = RT5645_ASRC_3;
shift = 4;
break;
case 3:
reg = RT5645_ASRC_2;
shift = 0;
break;
case 8:
reg = RT5645_ASRC_2;
shift = 4;
break;
case 9:
reg = RT5645_ASRC_2;
shift = 8;
break;
case 10:
reg = RT5645_ASRC_2;
shift = 12;
break;
default:
return 0;
}
val = (snd_soc_read(source->codec, reg) >> shift) & 0xf;
switch (val) {
case 1:
case 2:
case 3:
case 4:
return 1;
default:
return 0;
}
}
/* Digital Mixer */
static const struct snd_kcontrol_new rt5645_sto1_adc_l_mix[] = {
SOC_DAPM_SINGLE("ADC1 Switch", RT5645_STO1_ADC_MIXER,
@ -1246,6 +1293,30 @@ static const struct snd_soc_dapm_widget rt5645_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("Mic Det Power", RT5645_PWR_VOL,
RT5645_PWR_MIC_DET_BIT, 0, NULL, 0),
/* ASRC */
SND_SOC_DAPM_SUPPLY_S("I2S1 ASRC", 1, RT5645_ASRC_1,
11, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("I2S2 ASRC", 1, RT5645_ASRC_1,
12, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("DAC STO ASRC", 1, RT5645_ASRC_1,
10, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("DAC MONO L ASRC", 1, RT5645_ASRC_1,
9, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("DAC MONO R ASRC", 1, RT5645_ASRC_1,
8, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("DMIC STO1 ASRC", 1, RT5645_ASRC_1,
7, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("DMIC MONO L ASRC", 1, RT5645_ASRC_1,
5, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("DMIC MONO R ASRC", 1, RT5645_ASRC_1,
4, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("ADC STO1 ASRC", 1, RT5645_ASRC_1,
3, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("ADC MONO L ASRC", 1, RT5645_ASRC_1,
1, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("ADC MONO R ASRC", 1, RT5645_ASRC_1,
0, 0, NULL, 0),
/* Input Side */
/* micbias */
SND_SOC_DAPM_MICBIAS("micbias1", RT5645_PWR_ANLG2,
@ -1504,6 +1575,17 @@ static const struct snd_soc_dapm_widget rt5645_dapm_widgets[] = {
};
static const struct snd_soc_dapm_route rt5645_dapm_routes[] = {
{ "adc stereo1 filter", NULL, "ADC STO1 ASRC", is_using_asrc },
{ "adc stereo2 filter", NULL, "ADC STO2 ASRC", is_using_asrc },
{ "adc mono left filter", NULL, "ADC MONO L ASRC", is_using_asrc },
{ "adc mono right filter", NULL, "ADC MONO R ASRC", is_using_asrc },
{ "dac mono left filter", NULL, "DAC MONO L ASRC", is_using_asrc },
{ "dac mono right filter", NULL, "DAC MONO R ASRC", is_using_asrc },
{ "dac stereo1 filter", NULL, "DAC STO ASRC", is_using_asrc },
{ "I2S1", NULL, "I2S1 ASRC" },
{ "I2S2", NULL, "I2S2 ASRC" },
{ "IN1P", NULL, "LDO2" },
{ "IN2P", NULL, "LDO2" },
@ -1550,12 +1632,15 @@ static const struct snd_soc_dapm_route rt5645_dapm_routes[] = {
{ "Stereo1 DMIC Mux", "DMIC1", "DMIC1" },
{ "Stereo1 DMIC Mux", "DMIC2", "DMIC2" },
{ "Stereo1 DMIC Mux", NULL, "DMIC STO1 ASRC" },
{ "Mono DMIC L Mux", "DMIC1", "DMIC L1" },
{ "Mono DMIC L Mux", "DMIC2", "DMIC L2" },
{ "Mono DMIC L Mux", NULL, "DMIC MONO L ASRC" },
{ "Mono DMIC R Mux", "DMIC1", "DMIC R1" },
{ "Mono DMIC R Mux", "DMIC2", "DMIC R2" },
{ "Mono DMIC R Mux", NULL, "DMIC MONO R ASRC" },
{ "Stereo1 ADC L2 Mux", "DMIC", "Stereo1 DMIC Mux" },
{ "Stereo1 ADC L2 Mux", "DAC MIX", "DAC MIXL" },
@ -2029,8 +2114,11 @@ static int rt5645_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
struct snd_soc_codec *codec = dai->codec;
unsigned int val = 0;
if (rx_mask || tx_mask)
if (rx_mask || tx_mask) {
val |= (1 << 14);
snd_soc_update_bits(codec, RT5645_BASS_BACK,
RT5645_G_BB_BST_MASK, RT5645_G_BB_BST_25DB);
}
switch (slots) {
case 4:
@ -2071,8 +2159,8 @@ static int rt5645_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
switch (level) {
case SND_SOC_BIAS_STANDBY:
if (SND_SOC_BIAS_OFF == codec->dapm.bias_level) {
case SND_SOC_BIAS_PREPARE:
if (SND_SOC_BIAS_STANDBY == codec->dapm.bias_level) {
snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
RT5645_PWR_VREF1 | RT5645_PWR_MB |
RT5645_PWR_BG | RT5645_PWR_VREF2,
@ -2087,15 +2175,24 @@ static int rt5645_set_bias_level(struct snd_soc_codec *codec,
}
break;
case SND_SOC_BIAS_STANDBY:
snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
RT5645_PWR_VREF1 | RT5645_PWR_MB |
RT5645_PWR_BG | RT5645_PWR_VREF2,
RT5645_PWR_VREF1 | RT5645_PWR_MB |
RT5645_PWR_BG | RT5645_PWR_VREF2);
snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
RT5645_PWR_FV1 | RT5645_PWR_FV2,
RT5645_PWR_FV1 | RT5645_PWR_FV2);
break;
case SND_SOC_BIAS_OFF:
snd_soc_write(codec, RT5645_DEPOP_M2, 0x1100);
snd_soc_write(codec, RT5645_GEN_CTRL1, 0x0128);
snd_soc_write(codec, RT5645_PWR_DIG1, 0x0000);
snd_soc_write(codec, RT5645_PWR_DIG2, 0x0000);
snd_soc_write(codec, RT5645_PWR_VOL, 0x0000);
snd_soc_write(codec, RT5645_PWR_MIXER, 0x0000);
snd_soc_write(codec, RT5645_PWR_ANLG1, 0x0000);
snd_soc_write(codec, RT5645_PWR_ANLG2, 0x0000);
snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
RT5645_PWR_VREF1 | RT5645_PWR_MB |
RT5645_PWR_BG | RT5645_PWR_VREF2 |
RT5645_PWR_FV1 | RT5645_PWR_FV2, 0x0);
break;
default:
@ -2106,8 +2203,7 @@ static int rt5645_set_bias_level(struct snd_soc_codec *codec,
return 0;
}
static int rt5645_jack_detect(struct snd_soc_codec *codec,
struct snd_soc_jack *jack)
static int rt5645_jack_detect(struct snd_soc_codec *codec)
{
struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
int gpio_state, jack_type = 0;
@ -2145,34 +2241,44 @@ static int rt5645_jack_detect(struct snd_soc_codec *codec,
snd_soc_dapm_disable_pin(&codec->dapm, "micbias1");
snd_soc_dapm_disable_pin(&codec->dapm, "micbias2");
snd_soc_dapm_disable_pin(&codec->dapm, "LDO2");
if (rt5645->pdata.jd_mode == 0)
snd_soc_dapm_disable_pin(&codec->dapm, "LDO2");
snd_soc_dapm_disable_pin(&codec->dapm, "Mic Det Power");
snd_soc_dapm_sync(&codec->dapm);
}
snd_soc_jack_report(rt5645->jack, jack_type, SND_JACK_HEADSET);
snd_soc_jack_report(rt5645->hp_jack, jack_type, SND_JACK_HEADPHONE);
snd_soc_jack_report(rt5645->mic_jack, jack_type, SND_JACK_MICROPHONE);
return 0;
}
int rt5645_set_jack_detect(struct snd_soc_codec *codec,
struct snd_soc_jack *jack)
struct snd_soc_jack *hp_jack, struct snd_soc_jack *mic_jack)
{
struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
rt5645->jack = jack;
rt5645_jack_detect(codec, rt5645->jack);
rt5645->hp_jack = hp_jack;
rt5645->mic_jack = mic_jack;
rt5645_jack_detect(codec);
return 0;
}
EXPORT_SYMBOL_GPL(rt5645_set_jack_detect);
static void rt5645_jack_detect_work(struct work_struct *work)
{
struct rt5645_priv *rt5645 =
container_of(work, struct rt5645_priv, jack_detect_work.work);
rt5645_jack_detect(rt5645->codec);
}
static irqreturn_t rt5645_irq(int irq, void *data)
{
struct rt5645_priv *rt5645 = data;
rt5645_jack_detect(rt5645->codec, rt5645->jack);
queue_delayed_work(system_power_efficient_wq,
&rt5645->jack_detect_work, msecs_to_jiffies(250));
return IRQ_HANDLED;
}
@ -2187,6 +2293,13 @@ static int rt5645_probe(struct snd_soc_codec *codec)
snd_soc_update_bits(codec, RT5645_CHARGE_PUMP, 0x0300, 0x0200);
/* for JD function */
if (rt5645->pdata.en_jd_func) {
snd_soc_dapm_force_enable_pin(&codec->dapm, "JD Power");
snd_soc_dapm_force_enable_pin(&codec->dapm, "LDO2");
snd_soc_dapm_sync(&codec->dapm);
}
return 0;
}
@ -2420,6 +2533,51 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
}
if (rt5645->pdata.en_jd_func) {
regmap_update_bits(rt5645->regmap, RT5645_GEN_CTRL3,
RT5645_IRQ_CLK_GATE_CTRL | RT5645_MICINDET_MANU,
RT5645_IRQ_CLK_GATE_CTRL | RT5645_MICINDET_MANU);
regmap_update_bits(rt5645->regmap, RT5645_IN1_CTRL1,
RT5645_CBJ_BST1_EN, RT5645_CBJ_BST1_EN);
regmap_update_bits(rt5645->regmap, RT5645_JD_CTRL3,
RT5645_JD_CBJ_EN | RT5645_JD_CBJ_POL,
RT5645_JD_CBJ_EN | RT5645_JD_CBJ_POL);
regmap_update_bits(rt5645->regmap, RT5645_MICBIAS,
RT5645_IRQ_CLK_INT, RT5645_IRQ_CLK_INT);
}
if (rt5645->pdata.jd_mode) {
regmap_update_bits(rt5645->regmap, RT5645_IRQ_CTRL2,
RT5645_IRQ_JD_1_1_EN, RT5645_IRQ_JD_1_1_EN);
regmap_update_bits(rt5645->regmap, RT5645_GEN_CTRL3,
RT5645_JD_PSV_MODE, RT5645_JD_PSV_MODE);
regmap_update_bits(rt5645->regmap, RT5645_HPO_MIXER,
RT5645_IRQ_PSV_MODE, RT5645_IRQ_PSV_MODE);
regmap_update_bits(rt5645->regmap, RT5645_MICBIAS,
RT5645_MIC2_OVCD_EN, RT5645_MIC2_OVCD_EN);
regmap_update_bits(rt5645->regmap, RT5645_GPIO_CTRL1,
RT5645_GP1_PIN_IRQ, RT5645_GP1_PIN_IRQ);
switch (rt5645->pdata.jd_mode) {
case 1:
regmap_update_bits(rt5645->regmap, RT5645_A_JD_CTRL1,
RT5645_JD1_MODE_MASK,
RT5645_JD1_MODE_0);
break;
case 2:
regmap_update_bits(rt5645->regmap, RT5645_A_JD_CTRL1,
RT5645_JD1_MODE_MASK,
RT5645_JD1_MODE_1);
break;
case 3:
regmap_update_bits(rt5645->regmap, RT5645_A_JD_CTRL1,
RT5645_JD1_MODE_MASK,
RT5645_JD1_MODE_2);
break;
default:
break;
}
}
if (rt5645->i2c->irq) {
ret = request_threaded_irq(rt5645->i2c->irq, NULL, rt5645_irq,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING
@ -2438,6 +2596,8 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
dev_err(&i2c->dev, "Fail gpio_direction hp_det_gpio\n");
}
INIT_DELAYED_WORK(&rt5645->jack_detect_work, rt5645_jack_detect_work);
return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5645,
rt5645_dai, ARRAY_SIZE(rt5645_dai));
}
@ -2449,6 +2609,8 @@ static int rt5645_i2c_remove(struct i2c_client *i2c)
if (i2c->irq)
free_irq(i2c->irq, rt5645);
cancel_delayed_work_sync(&rt5645->jack_detect_work);
if (gpio_is_valid(rt5645->pdata.hp_det_gpio))
gpio_free(rt5645->pdata.hp_det_gpio);

View File

@ -594,6 +594,7 @@
#define RT5645_M_DAC1_HM_SFT 14
#define RT5645_M_HPVOL_HM (0x1 << 13)
#define RT5645_M_HPVOL_HM_SFT 13
#define RT5645_IRQ_PSV_MODE (0x1 << 12)
/* SPK Left Mixer Control (0x46) */
#define RT5645_G_RM_L_SM_L_MASK (0x3 << 14)
@ -1348,6 +1349,12 @@
#define RT5645_PWR_CLK25M_SFT 4
#define RT5645_PWR_CLK25M_PD (0x0 << 4)
#define RT5645_PWR_CLK25M_PU (0x1 << 4)
#define RT5645_IRQ_CLK_MCLK (0x0 << 3)
#define RT5645_IRQ_CLK_INT (0x1 << 3)
#define RT5645_JD1_MODE_MASK (0x3 << 0)
#define RT5645_JD1_MODE_0 (0x0 << 0)
#define RT5645_JD1_MODE_1 (0x1 << 0)
#define RT5645_JD1_MODE_2 (0x2 << 0)
/* VAD Control 4 (0x9d) */
#define RT5645_VAD_SEL_MASK (0x3 << 8)
@ -1636,6 +1643,7 @@
#define RT5645_OT_P_SFT 10
#define RT5645_OT_P_NOR (0x0 << 10)
#define RT5645_OT_P_INV (0x1 << 10)
#define RT5645_IRQ_JD_1_1_EN (0x1 << 9)
/* IRQ Control 2 (0xbe) */
#define RT5645_IRQ_MB1_OC_MASK (0x1 << 15)
@ -1853,6 +1861,7 @@
#define RT5645_M_BB_HPF_R_SFT 6
#define RT5645_G_BB_BST_MASK (0x3f)
#define RT5645_G_BB_BST_SFT 0
#define RT5645_G_BB_BST_25DB 0x14
/* MP3 Plus Control 1 (0xd0) */
#define RT5645_M_MP3_L_MASK (0x1 << 15)
@ -2116,6 +2125,10 @@ enum {
#define RT5645_RXDP2_SEL_ADC (0x1 << 3)
#define RT5645_RXDP2_SEL_SFT (3)
/* General Control3 (0xfc) */
#define RT5645_JD_PSV_MODE (0x1 << 12)
#define RT5645_IRQ_CLK_GATE_CTRL (0x1 << 11)
#define RT5645_MICINDET_MANU (0x1 << 7)
/* Vendor ID (0xfd) */
#define RT5645_VER_C 0x2
@ -2167,7 +2180,9 @@ struct rt5645_priv {
struct rt5645_platform_data pdata;
struct regmap *regmap;
struct i2c_client *i2c;
struct snd_soc_jack *jack;
struct snd_soc_jack *hp_jack;
struct snd_soc_jack *mic_jack;
struct delayed_work jack_detect_work;
int sysclk;
int sysclk_src;
@ -2181,6 +2196,6 @@ struct rt5645_priv {
};
int rt5645_set_jack_detect(struct snd_soc_codec *codec,
struct snd_soc_jack *jack);
struct snd_soc_jack *hp_jack, struct snd_soc_jack *mic_jack);
#endif /* __RT5645_H__ */

View File

@ -16,6 +16,7 @@
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/platform_device.h>
#include <linux/acpi.h>
#include <linux/spi/spi.h>
#include <sound/core.h>
#include <sound/pcm.h>
@ -575,6 +576,18 @@ static int is_using_asrc(struct snd_soc_dapm_widget *source,
}
static int can_use_asrc(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink)
{
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
if (rt5670->sysclk > rt5670->lrck[RT5670_AIF1] * 384)
return 1;
return 0;
}
/* Digital Mixer */
static const struct snd_kcontrol_new rt5670_sto1_adc_l_mix[] = {
SOC_DAPM_SINGLE("ADC1 Switch", RT5670_STO1_ADC_MIXER,
@ -1281,6 +1294,14 @@ static const struct snd_soc_dapm_widget rt5670_dapm_widgets[] = {
9, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("DAC MONO R ASRC", 1, RT5670_ASRC_1,
8, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("DMIC STO1 ASRC", 1, RT5670_ASRC_1,
7, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("DMIC STO2 ASRC", 1, RT5670_ASRC_1,
6, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("DMIC MONO L ASRC", 1, RT5670_ASRC_1,
5, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("DMIC MONO R ASRC", 1, RT5670_ASRC_1,
4, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("ADC STO1 ASRC", 1, RT5670_ASRC_1,
3, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("ADC STO2 ASRC", 1, RT5670_ASRC_1,
@ -1595,29 +1616,40 @@ static const struct snd_soc_dapm_widget rt5670_dapm_widgets[] = {
/* PDM */
SND_SOC_DAPM_SUPPLY("PDM1 Power", RT5670_PWR_DIG2,
RT5670_PWR_PDM1_BIT, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("PDM2 Power", RT5670_PWR_DIG2,
RT5670_PWR_PDM2_BIT, 0, NULL, 0),
SND_SOC_DAPM_MUX("PDM1 L Mux", RT5670_PDM_OUT_CTRL,
RT5670_M_PDM1_L_SFT, 1, &rt5670_pdm1_l_mux),
SND_SOC_DAPM_MUX("PDM1 R Mux", RT5670_PDM_OUT_CTRL,
RT5670_M_PDM1_R_SFT, 1, &rt5670_pdm1_r_mux),
SND_SOC_DAPM_MUX("PDM2 L Mux", RT5670_PDM_OUT_CTRL,
RT5670_M_PDM2_L_SFT, 1, &rt5670_pdm2_l_mux),
SND_SOC_DAPM_MUX("PDM2 R Mux", RT5670_PDM_OUT_CTRL,
RT5670_M_PDM2_R_SFT, 1, &rt5670_pdm2_r_mux),
/* Output Lines */
SND_SOC_DAPM_OUTPUT("HPOL"),
SND_SOC_DAPM_OUTPUT("HPOR"),
SND_SOC_DAPM_OUTPUT("LOUTL"),
SND_SOC_DAPM_OUTPUT("LOUTR"),
};
static const struct snd_soc_dapm_widget rt5670_specific_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("PDM2 Power", RT5670_PWR_DIG2,
RT5670_PWR_PDM2_BIT, 0, NULL, 0),
SND_SOC_DAPM_MUX("PDM2 L Mux", RT5670_PDM_OUT_CTRL,
RT5670_M_PDM2_L_SFT, 1, &rt5670_pdm2_l_mux),
SND_SOC_DAPM_MUX("PDM2 R Mux", RT5670_PDM_OUT_CTRL,
RT5670_M_PDM2_R_SFT, 1, &rt5670_pdm2_r_mux),
SND_SOC_DAPM_OUTPUT("PDM1L"),
SND_SOC_DAPM_OUTPUT("PDM1R"),
SND_SOC_DAPM_OUTPUT("PDM2L"),
SND_SOC_DAPM_OUTPUT("PDM2R"),
};
static const struct snd_soc_dapm_widget rt5672_specific_dapm_widgets[] = {
SND_SOC_DAPM_PGA("SPO Amp", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_OUTPUT("SPOLP"),
SND_SOC_DAPM_OUTPUT("SPOLN"),
SND_SOC_DAPM_OUTPUT("SPORP"),
SND_SOC_DAPM_OUTPUT("SPORN"),
};
static const struct snd_soc_dapm_route rt5670_dapm_routes[] = {
{ "ADC Stereo1 Filter", NULL, "ADC STO1 ASRC", is_using_asrc },
{ "ADC Stereo2 Filter", NULL, "ADC STO2 ASRC", is_using_asrc },
@ -1626,9 +1658,13 @@ static const struct snd_soc_dapm_route rt5670_dapm_routes[] = {
{ "DAC Mono Left Filter", NULL, "DAC MONO L ASRC", is_using_asrc },
{ "DAC Mono Right Filter", NULL, "DAC MONO R ASRC", is_using_asrc },
{ "DAC Stereo1 Filter", NULL, "DAC STO ASRC", is_using_asrc },
{ "Stereo1 DMIC Mux", NULL, "DMIC STO1 ASRC", can_use_asrc },
{ "Stereo2 DMIC Mux", NULL, "DMIC STO2 ASRC", can_use_asrc },
{ "Mono DMIC L Mux", NULL, "DMIC MONO L ASRC", can_use_asrc },
{ "Mono DMIC R Mux", NULL, "DMIC MONO R ASRC", can_use_asrc },
{ "I2S1", NULL, "I2S1 ASRC" },
{ "I2S2", NULL, "I2S2 ASRC" },
{ "I2S1", NULL, "I2S1 ASRC", can_use_asrc},
{ "I2S2", NULL, "I2S2 ASRC", can_use_asrc},
{ "DMIC1", NULL, "DMIC L1" },
{ "DMIC1", NULL, "DMIC R1" },
@ -1970,12 +2006,6 @@ static const struct snd_soc_dapm_route rt5670_dapm_routes[] = {
{ "PDM1 R Mux", "Stereo DAC", "Stereo DAC MIXR" },
{ "PDM1 R Mux", "Mono DAC", "Mono DAC MIXR" },
{ "PDM1 R Mux", NULL, "PDM1 Power" },
{ "PDM2 L Mux", "Stereo DAC", "Stereo DAC MIXL" },
{ "PDM2 L Mux", "Mono DAC", "Mono DAC MIXL" },
{ "PDM2 L Mux", NULL, "PDM2 Power" },
{ "PDM2 R Mux", "Stereo DAC", "Stereo DAC MIXR" },
{ "PDM2 R Mux", "Mono DAC", "Mono DAC MIXR" },
{ "PDM2 R Mux", NULL, "PDM2 Power" },
{ "HP Amp", NULL, "HPO MIX" },
{ "HP Amp", NULL, "Mic Det Power" },
@ -1993,13 +2023,30 @@ static const struct snd_soc_dapm_route rt5670_dapm_routes[] = {
{ "LOUTR", NULL, "LOUT R Playback" },
{ "LOUTL", NULL, "Improve HP Amp Drv" },
{ "LOUTR", NULL, "Improve HP Amp Drv" },
};
static const struct snd_soc_dapm_route rt5670_specific_dapm_routes[] = {
{ "PDM2 L Mux", "Stereo DAC", "Stereo DAC MIXL" },
{ "PDM2 L Mux", "Mono DAC", "Mono DAC MIXL" },
{ "PDM2 L Mux", NULL, "PDM2 Power" },
{ "PDM2 R Mux", "Stereo DAC", "Stereo DAC MIXR" },
{ "PDM2 R Mux", "Mono DAC", "Mono DAC MIXR" },
{ "PDM2 R Mux", NULL, "PDM2 Power" },
{ "PDM1L", NULL, "PDM1 L Mux" },
{ "PDM1R", NULL, "PDM1 R Mux" },
{ "PDM2L", NULL, "PDM2 L Mux" },
{ "PDM2R", NULL, "PDM2 R Mux" },
};
static const struct snd_soc_dapm_route rt5672_specific_dapm_routes[] = {
{ "SPO Amp", NULL, "PDM1 L Mux" },
{ "SPO Amp", NULL, "PDM1 R Mux" },
{ "SPOLP", NULL, "SPO Amp" },
{ "SPOLN", NULL, "SPO Amp" },
{ "SPORP", NULL, "SPO Amp" },
{ "SPORN", NULL, "SPO Amp" },
};
static int rt5670_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{
@ -2287,6 +2334,8 @@ static int rt5670_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
static int rt5670_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
switch (level) {
case SND_SOC_BIAS_PREPARE:
if (SND_SOC_BIAS_STANDBY == codec->dapm.bias_level) {
@ -2308,16 +2357,27 @@ static int rt5670_set_bias_level(struct snd_soc_codec *codec,
}
break;
case SND_SOC_BIAS_STANDBY:
snd_soc_write(codec, RT5670_PWR_DIG1, 0x0000);
snd_soc_write(codec, RT5670_PWR_DIG2, 0x0001);
snd_soc_write(codec, RT5670_PWR_VOL, 0x0000);
snd_soc_write(codec, RT5670_PWR_MIXER, 0x0001);
snd_soc_write(codec, RT5670_PWR_ANLG1, 0x2800);
snd_soc_write(codec, RT5670_PWR_ANLG2, 0x0004);
snd_soc_update_bits(codec, RT5670_DIG_MISC, 0x1, 0x0);
snd_soc_update_bits(codec, RT5670_PWR_ANLG1,
RT5670_PWR_VREF1 | RT5670_PWR_VREF2 |
RT5670_PWR_FV1 | RT5670_PWR_FV2, 0);
snd_soc_update_bits(codec, RT5670_PWR_ANLG1,
RT5670_LDO_SEL_MASK, 0x1);
break;
case SND_SOC_BIAS_OFF:
if (rt5670->pdata.jd_mode)
snd_soc_update_bits(codec, RT5670_PWR_ANLG1,
RT5670_PWR_VREF1 | RT5670_PWR_MB |
RT5670_PWR_BG | RT5670_PWR_VREF2 |
RT5670_PWR_FV1 | RT5670_PWR_FV2,
RT5670_PWR_MB | RT5670_PWR_BG);
else
snd_soc_update_bits(codec, RT5670_PWR_ANLG1,
RT5670_PWR_VREF1 | RT5670_PWR_MB |
RT5670_PWR_BG | RT5670_PWR_VREF2 |
RT5670_PWR_FV1 | RT5670_PWR_FV2, 0);
snd_soc_update_bits(codec, RT5670_DIG_MISC, 0x1, 0x0);
break;
default:
break;
@ -2331,6 +2391,29 @@ static int rt5670_probe(struct snd_soc_codec *codec)
{
struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
switch (snd_soc_read(codec, RT5670_RESET) & RT5670_ID_MASK) {
case RT5670_ID_5670:
case RT5670_ID_5671:
snd_soc_dapm_new_controls(&codec->dapm,
rt5670_specific_dapm_widgets,
ARRAY_SIZE(rt5670_specific_dapm_widgets));
snd_soc_dapm_add_routes(&codec->dapm,
rt5670_specific_dapm_routes,
ARRAY_SIZE(rt5670_specific_dapm_routes));
break;
case RT5670_ID_5672:
snd_soc_dapm_new_controls(&codec->dapm,
rt5672_specific_dapm_widgets,
ARRAY_SIZE(rt5672_specific_dapm_widgets));
snd_soc_dapm_add_routes(&codec->dapm,
rt5672_specific_dapm_routes,
ARRAY_SIZE(rt5672_specific_dapm_routes));
break;
default:
dev_err(codec->dev,
"The driver is for RT5670 RT5671 or RT5672 only\n");
return -ENODEV;
}
rt5670->codec = codec;
return 0;
@ -2452,10 +2535,20 @@ static const struct regmap_config rt5670_regmap = {
static const struct i2c_device_id rt5670_i2c_id[] = {
{ "rt5670", 0 },
{ "rt5671", 0 },
{ "rt5672", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, rt5670_i2c_id);
#ifdef CONFIG_ACPI
static struct acpi_device_id rt5670_acpi_match[] = {
{ "10EC5670", 0},
{ },
};
MODULE_DEVICE_TABLE(acpi, rt5670_acpi_match);
#endif
static int rt5670_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
@ -2644,6 +2737,7 @@ static struct i2c_driver rt5670_i2c_driver = {
.driver = {
.name = "rt5670",
.owner = THIS_MODULE,
.acpi_match_table = ACPI_PTR(rt5670_acpi_match),
},
.probe = rt5670_i2c_probe,
.remove = rt5670_i2c_remove,

View File

@ -228,6 +228,12 @@
#define RT5670_R_VOL_MASK (0x3f)
#define RT5670_R_VOL_SFT 0
/* SW Reset & Device ID (0x00) */
#define RT5670_ID_MASK (0x3 << 1)
#define RT5670_ID_5670 (0x0 << 1)
#define RT5670_ID_5672 (0x1 << 1)
#define RT5670_ID_5671 (0x2 << 1)
/* Combo Jack Control 1 (0x0a) */
#define RT5670_CBJ_BST1_MASK (0xf << 12)
#define RT5670_CBJ_BST1_SFT (12)

View File

@ -0,0 +1,130 @@
/*
* rt5677-spi.c -- RT5677 ALSA SoC audio codec driver
*
* Copyright 2013 Realtek Semiconductor Corp.
* Author: Oder Chiou <oder_chiou@realtek.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/input.h>
#include <linux/spi/spi.h>
#include <linux/device.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/slab.h>
#include <linux/gpio.h>
#include <linux/sched.h>
#include <linux/kthread.h>
#include <linux/uaccess.h>
#include <linux/miscdevice.h>
#include <linux/regulator/consumer.h>
#include <linux/pm_qos.h>
#include <linux/sysfs.h>
#include <linux/clk.h>
#include <linux/firmware.h>
#include "rt5677-spi.h"
static struct spi_device *g_spi;
/**
* rt5677_spi_write - Write data to SPI.
* @txbuf: Data Buffer for writing.
* @len: Data length.
*
*
* Returns true for success.
*/
int rt5677_spi_write(u8 *txbuf, size_t len)
{
int status;
status = spi_write(g_spi, txbuf, len);
if (status)
dev_err(&g_spi->dev, "rt5677_spi_write error %d\n", status);
return status;
}
EXPORT_SYMBOL_GPL(rt5677_spi_write);
/**
* rt5677_spi_burst_write - Write data to SPI by rt5677 dsp memory address.
* @addr: Start address.
* @txbuf: Data Buffer for writng.
* @len: Data length, it must be a multiple of 8.
*
*
* Returns true for success.
*/
int rt5677_spi_burst_write(u32 addr, const struct firmware *fw)
{
u8 spi_cmd = RT5677_SPI_CMD_BURST_WRITE;
u8 *write_buf;
unsigned int i, end, offset = 0;
write_buf = kmalloc(RT5677_SPI_BUF_LEN + 6, GFP_KERNEL);
if (write_buf == NULL)
return -ENOMEM;
while (offset < fw->size) {
if (offset + RT5677_SPI_BUF_LEN <= fw->size)
end = RT5677_SPI_BUF_LEN;
else
end = fw->size % RT5677_SPI_BUF_LEN;
write_buf[0] = spi_cmd;
write_buf[1] = ((addr + offset) & 0xff000000) >> 24;
write_buf[2] = ((addr + offset) & 0x00ff0000) >> 16;
write_buf[3] = ((addr + offset) & 0x0000ff00) >> 8;
write_buf[4] = ((addr + offset) & 0x000000ff) >> 0;
for (i = 0; i < end; i += 8) {
write_buf[i + 12] = fw->data[offset + i + 0];
write_buf[i + 11] = fw->data[offset + i + 1];
write_buf[i + 10] = fw->data[offset + i + 2];
write_buf[i + 9] = fw->data[offset + i + 3];
write_buf[i + 8] = fw->data[offset + i + 4];
write_buf[i + 7] = fw->data[offset + i + 5];
write_buf[i + 6] = fw->data[offset + i + 6];
write_buf[i + 5] = fw->data[offset + i + 7];
}
write_buf[end + 5] = spi_cmd;
rt5677_spi_write(write_buf, end + 6);
offset += RT5677_SPI_BUF_LEN;
}
kfree(write_buf);
return 0;
}
EXPORT_SYMBOL_GPL(rt5677_spi_burst_write);
static int rt5677_spi_probe(struct spi_device *spi)
{
g_spi = spi;
return 0;
}
static struct spi_driver rt5677_spi_driver = {
.driver = {
.name = "rt5677",
.owner = THIS_MODULE,
},
.probe = rt5677_spi_probe,
};
module_spi_driver(rt5677_spi_driver);
MODULE_DESCRIPTION("ASoC RT5677 SPI driver");
MODULE_AUTHOR("Oder Chiou <oder_chiou@realtek.com>");
MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,21 @@
/*
* rt5677-spi.h -- RT5677 ALSA SoC audio codec driver
*
* Copyright 2013 Realtek Semiconductor Corp.
* Author: Oder Chiou <oder_chiou@realtek.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef __RT5677_SPI_H__
#define __RT5677_SPI_H__
#define RT5677_SPI_BUF_LEN 240
#define RT5677_SPI_CMD_BURST_WRITE 0x05
int rt5677_spi_write(u8 *txbuf, size_t len);
int rt5677_spi_burst_write(u32 addr, const struct firmware *fw);
#endif /* __RT5677_SPI_H__ */

File diff suppressed because it is too large Load Diff

View File

@ -13,6 +13,7 @@
#define __RT5677_H__
#include <sound/rt5677.h>
#include <linux/gpio/driver.h>
/* Info */
#define RT5677_RESET 0x00
@ -305,10 +306,10 @@
#define RT5677_R_MUTE_SFT 7
#define RT5677_VOL_R_MUTE (0x1 << 6)
#define RT5677_VOL_R_SFT 6
#define RT5677_L_VOL_MASK (0x3f << 8)
#define RT5677_L_VOL_SFT 8
#define RT5677_R_VOL_MASK (0x3f)
#define RT5677_R_VOL_SFT 0
#define RT5677_L_VOL_MASK (0x7f << 9)
#define RT5677_L_VOL_SFT 9
#define RT5677_R_VOL_MASK (0x7f << 1)
#define RT5677_R_VOL_SFT 1
/* LOUT1 Control (0x01) */
#define RT5677_LOUT1_L_MUTE (0x1 << 15)
@ -446,16 +447,16 @@
#define RT5677_SEL_DAC2_R_SRC_SFT 0
/* Stereo1 ADC Digital Volume Control (0x1c) */
#define RT5677_STO1_ADC_L_VOL_MASK (0x7f << 8)
#define RT5677_STO1_ADC_L_VOL_SFT 8
#define RT5677_STO1_ADC_R_VOL_MASK (0x7f)
#define RT5677_STO1_ADC_R_VOL_SFT 0
#define RT5677_STO1_ADC_L_VOL_MASK (0x3f << 9)
#define RT5677_STO1_ADC_L_VOL_SFT 9
#define RT5677_STO1_ADC_R_VOL_MASK (0x3f << 1)
#define RT5677_STO1_ADC_R_VOL_SFT 1
/* Mono ADC Digital Volume Control (0x1d) */
#define RT5677_MONO_ADC_L_VOL_MASK (0x7f << 8)
#define RT5677_MONO_ADC_L_VOL_SFT 8
#define RT5677_MONO_ADC_R_VOL_MASK (0x7f)
#define RT5677_MONO_ADC_R_VOL_SFT 0
#define RT5677_MONO_ADC_L_VOL_MASK (0x3f << 9)
#define RT5677_MONO_ADC_L_VOL_SFT 9
#define RT5677_MONO_ADC_R_VOL_MASK (0x3f << 1)
#define RT5677_MONO_ADC_R_VOL_SFT 1
/* Stereo 1/2 ADC Boost Gain Control (0x1e) */
#define RT5677_STO1_ADC_L_BST_MASK (0x3 << 14)
@ -798,7 +799,21 @@
#define RT5677_PDM2_I2C_EXE (0x1 << 1)
#define RT5677_PDM2_I2C_BUSY (0x1 << 0)
/* MX3C TDM1 control 1 (0x3c) */
/* TDM1 control 1 (0x3b) */
#define RT5677_IF1_ADC_MODE_MASK (0x1 << 12)
#define RT5677_IF1_ADC_MODE_SFT 12
#define RT5677_IF1_ADC_MODE_I2S (0x0 << 12)
#define RT5677_IF1_ADC_MODE_TDM (0x1 << 12)
#define RT5677_IF1_ADC1_SWAP_MASK (0x3 << 6)
#define RT5677_IF1_ADC1_SWAP_SFT 6
#define RT5677_IF1_ADC2_SWAP_MASK (0x3 << 4)
#define RT5677_IF1_ADC2_SWAP_SFT 4
#define RT5677_IF1_ADC3_SWAP_MASK (0x3 << 2)
#define RT5677_IF1_ADC3_SWAP_SFT 2
#define RT5677_IF1_ADC4_SWAP_MASK (0x3 << 0)
#define RT5677_IF1_ADC4_SWAP_SFT 0
/* TDM1 control 2 (0x3c) */
#define RT5677_IF1_ADC4_MASK (0x3 << 10)
#define RT5677_IF1_ADC4_SFT 10
#define RT5677_IF1_ADC3_MASK (0x3 << 8)
@ -807,8 +822,44 @@
#define RT5677_IF1_ADC2_SFT 6
#define RT5677_IF1_ADC1_MASK (0x3 << 4)
#define RT5677_IF1_ADC1_SFT 4
#define RT5677_IF1_ADC_CTRL_MASK (0x7 << 0)
#define RT5677_IF1_ADC_CTRL_SFT 0
/* MX41 TDM2 control 1 (0x41) */
/* TDM1 control 4 (0x3e) */
#define RT5677_IF1_DAC0_MASK (0x7 << 12)
#define RT5677_IF1_DAC0_SFT 12
#define RT5677_IF1_DAC1_MASK (0x7 << 8)
#define RT5677_IF1_DAC1_SFT 8
#define RT5677_IF1_DAC2_MASK (0x7 << 4)
#define RT5677_IF1_DAC2_SFT 4
#define RT5677_IF1_DAC3_MASK (0x7 << 0)
#define RT5677_IF1_DAC3_SFT 0
/* TDM1 control 5 (0x3f) */
#define RT5677_IF1_DAC4_MASK (0x7 << 12)
#define RT5677_IF1_DAC4_SFT 12
#define RT5677_IF1_DAC5_MASK (0x7 << 8)
#define RT5677_IF1_DAC5_SFT 8
#define RT5677_IF1_DAC6_MASK (0x7 << 4)
#define RT5677_IF1_DAC6_SFT 4
#define RT5677_IF1_DAC7_MASK (0x7 << 0)
#define RT5677_IF1_DAC7_SFT 0
/* TDM2 control 1 (0x40) */
#define RT5677_IF2_ADC_MODE_MASK (0x1 << 12)
#define RT5677_IF2_ADC_MODE_SFT 12
#define RT5677_IF2_ADC_MODE_I2S (0x0 << 12)
#define RT5677_IF2_ADC_MODE_TDM (0x1 << 12)
#define RT5677_IF2_ADC1_SWAP_MASK (0x3 << 6)
#define RT5677_IF2_ADC1_SWAP_SFT 6
#define RT5677_IF2_ADC2_SWAP_MASK (0x3 << 4)
#define RT5677_IF2_ADC2_SWAP_SFT 4
#define RT5677_IF2_ADC3_SWAP_MASK (0x3 << 2)
#define RT5677_IF2_ADC3_SWAP_SFT 2
#define RT5677_IF2_ADC4_SWAP_MASK (0x3 << 0)
#define RT5677_IF2_ADC4_SWAP_SFT 0
/* TDM2 control 2 (0x41) */
#define RT5677_IF2_ADC4_MASK (0x3 << 10)
#define RT5677_IF2_ADC4_SFT 10
#define RT5677_IF2_ADC3_MASK (0x3 << 8)
@ -817,6 +868,28 @@
#define RT5677_IF2_ADC2_SFT 6
#define RT5677_IF2_ADC1_MASK (0x3 << 4)
#define RT5677_IF2_ADC1_SFT 4
#define RT5677_IF2_ADC_CTRL_MASK (0x7 << 0)
#define RT5677_IF2_ADC_CTRL_SFT 0
/* TDM2 control 4 (0x43) */
#define RT5677_IF2_DAC0_MASK (0x7 << 12)
#define RT5677_IF2_DAC0_SFT 12
#define RT5677_IF2_DAC1_MASK (0x7 << 8)
#define RT5677_IF2_DAC1_SFT 8
#define RT5677_IF2_DAC2_MASK (0x7 << 4)
#define RT5677_IF2_DAC2_SFT 4
#define RT5677_IF2_DAC3_MASK (0x7 << 0)
#define RT5677_IF2_DAC3_SFT 0
/* TDM2 control 5 (0x44) */
#define RT5677_IF2_DAC4_MASK (0x7 << 12)
#define RT5677_IF2_DAC4_SFT 12
#define RT5677_IF2_DAC5_MASK (0x7 << 8)
#define RT5677_IF2_DAC5_SFT 8
#define RT5677_IF2_DAC6_MASK (0x7 << 4)
#define RT5677_IF2_DAC6_SFT 4
#define RT5677_IF2_DAC7_MASK (0x7 << 0)
#define RT5677_IF2_DAC7_SFT 0
/* Digital Microphone Control 1 (0x50) */
#define RT5677_DMIC_1_EN_MASK (0x1 << 15)
@ -1367,6 +1440,48 @@
#define RT5677_SEL_SRC_IB01 (0x1 << 0)
#define RT5677_SEL_SRC_IB01_SFT 0
/* Jack Detect Control 1 (0xb5) */
#define RT5677_SEL_GPIO_JD1_MASK (0x3 << 14)
#define RT5677_SEL_GPIO_JD1_SFT 14
#define RT5677_SEL_GPIO_JD2_MASK (0x3 << 12)
#define RT5677_SEL_GPIO_JD2_SFT 12
#define RT5677_SEL_GPIO_JD3_MASK (0x3 << 10)
#define RT5677_SEL_GPIO_JD3_SFT 10
/* IRQ Control 1 (0xbd) */
#define RT5677_STA_GPIO_JD1 (0x1 << 15)
#define RT5677_STA_GPIO_JD1_SFT 15
#define RT5677_EN_IRQ_GPIO_JD1 (0x1 << 14)
#define RT5677_EN_IRQ_GPIO_JD1_SFT 14
#define RT5677_EN_GPIO_JD1_STICKY (0x1 << 13)
#define RT5677_EN_GPIO_JD1_STICKY_SFT 13
#define RT5677_INV_GPIO_JD1 (0x1 << 12)
#define RT5677_INV_GPIO_JD1_SFT 12
#define RT5677_STA_GPIO_JD2 (0x1 << 11)
#define RT5677_STA_GPIO_JD2_SFT 11
#define RT5677_EN_IRQ_GPIO_JD2 (0x1 << 10)
#define RT5677_EN_IRQ_GPIO_JD2_SFT 10
#define RT5677_EN_GPIO_JD2_STICKY (0x1 << 9)
#define RT5677_EN_GPIO_JD2_STICKY_SFT 9
#define RT5677_INV_GPIO_JD2 (0x1 << 8)
#define RT5677_INV_GPIO_JD2_SFT 8
#define RT5677_STA_MICBIAS1_OVCD (0x1 << 7)
#define RT5677_STA_MICBIAS1_OVCD_SFT 7
#define RT5677_EN_IRQ_MICBIAS1_OVCD (0x1 << 6)
#define RT5677_EN_IRQ_MICBIAS1_OVCD_SFT 6
#define RT5677_EN_MICBIAS1_OVCD_STICKY (0x1 << 5)
#define RT5677_EN_MICBIAS1_OVCD_STICKY_SFT 5
#define RT5677_INV_MICBIAS1_OVCD (0x1 << 4)
#define RT5677_INV_MICBIAS1_OVCD_SFT 4
#define RT5677_STA_GPIO_JD3 (0x1 << 3)
#define RT5677_STA_GPIO_JD3_SFT 3
#define RT5677_EN_IRQ_GPIO_JD3 (0x1 << 2)
#define RT5677_EN_IRQ_GPIO_JD3_SFT 2
#define RT5677_EN_GPIO_JD3_STICKY (0x1 << 1)
#define RT5677_EN_GPIO_JD3_STICKY_SFT 1
#define RT5677_INV_GPIO_JD3 (0x1 << 0)
#define RT5677_INV_GPIO_JD3_SFT 0
/* GPIO status (0xbf) */
#define RT5677_GPIO6_STATUS_MASK (0x1 << 5)
#define RT5677_GPIO6_STATUS_SFT 5
@ -1506,6 +1621,9 @@
#define RT5677_GPIO5_FUNC_GPIO (0x0 << 9)
#define RT5677_GPIO5_FUNC_DMIC (0x1 << 9)
#define RT5677_FIRMWARE1 "rt5677_dsp_fw1.bin"
#define RT5677_FIRMWARE2 "rt5677_dsp_fw2.bin"
/* System Clock Source */
enum {
RT5677_SCLK_S_MCLK,
@ -1541,10 +1659,18 @@ enum {
RT5677_GPIO_NUM,
};
enum {
RT5677_IRQ_JD1,
RT5677_IRQ_JD2,
RT5677_IRQ_JD3,
};
struct rt5677_priv {
struct snd_soc_codec *codec;
struct rt5677_platform_data pdata;
struct regmap *regmap;
struct regmap *regmap, *regmap_physical;
const struct firmware *fw1, *fw2;
struct mutex dsp_cmd_lock, dsp_pri_lock;
int sysclk;
int sysclk_src;
@ -1558,6 +1684,10 @@ struct rt5677_priv {
#ifdef CONFIG_GPIOLIB
struct gpio_chip gpio_chip;
#endif
bool dsp_vad_en;
struct regmap_irq_chip_data *irq_data;
bool is_dsp_mode;
bool is_vref_slow;
};
#endif /* __RT5677_H__ */

View File

@ -16,6 +16,7 @@
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/clk.h>
#include <linux/log2.h>
#include <linux/regmap.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
@ -121,6 +122,13 @@ struct ldo_regulator {
bool enabled;
};
enum sgtl5000_micbias_resistor {
SGTL5000_MICBIAS_OFF = 0,
SGTL5000_MICBIAS_2K = 2,
SGTL5000_MICBIAS_4K = 4,
SGTL5000_MICBIAS_8K = 8,
};
/* sgtl5000 private structure in codec */
struct sgtl5000_priv {
int sysclk; /* sysclk rate */
@ -131,6 +139,8 @@ struct sgtl5000_priv {
struct regmap *regmap;
struct clk *mclk;
int revision;
u8 micbias_resistor;
u8 micbias_voltage;
};
/*
@ -145,12 +155,14 @@ struct sgtl5000_priv {
static int mic_bias_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(w->codec);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
/* change mic bias resistor to 4Kohm */
/* change mic bias resistor */
snd_soc_update_bits(w->codec, SGTL5000_CHIP_MIC_CTRL,
SGTL5000_BIAS_R_MASK,
SGTL5000_BIAS_R_4k << SGTL5000_BIAS_R_SHIFT);
SGTL5000_BIAS_R_MASK,
sgtl5000->micbias_resistor << SGTL5000_BIAS_R_SHIFT);
break;
case SND_SOC_DAPM_PRE_PMD:
@ -530,16 +542,16 @@ static int sgtl5000_set_dai_sysclk(struct snd_soc_dai *codec_dai,
/*
* set clock according to i2s frame clock,
* sgtl5000 provide 2 clock sources.
* 1. sys_mclk. sample freq can only configure to
* sgtl5000 provides 2 clock sources:
* 1. sys_mclk: sample freq can only be configured to
* 1/256, 1/384, 1/512 of sys_mclk.
* 2. pll. can derive any audio clocks.
* 2. pll: can derive any audio clocks.
*
* clock setting rules:
* 1. in slave mode, only sys_mclk can use.
* 2. as constraint by sys_mclk, sample freq should
* set to 32k, 44.1k and above.
* 3. using sys_mclk prefer to pll to save power.
* 1. in slave mode, only sys_mclk can be used
* 2. as constraint by sys_mclk, sample freq should be set to 32 kHz, 44.1 kHz
* and above.
* 3. usage of sys_mclk is preferred over pll to save power.
*/
static int sgtl5000_set_clock(struct snd_soc_codec *codec, int frame_rate)
{
@ -549,8 +561,8 @@ static int sgtl5000_set_clock(struct snd_soc_codec *codec, int frame_rate)
/*
* sample freq should be divided by frame clock,
* if frame clock lower than 44.1khz, sample feq should set to
* 32khz or 44.1khz.
* if frame clock is lower than 44.1 kHz, sample freq should be set to
* 32 kHz or 44.1 kHz.
*/
switch (frame_rate) {
case 8000:
@ -603,9 +615,10 @@ static int sgtl5000_set_clock(struct snd_soc_codec *codec, int frame_rate)
/*
* calculate the divider of mclk/sample_freq,
* factor of freq =96k can only be 256, since mclk in range (12m,27m)
* factor of freq = 96 kHz can only be 256, since mclk is in the range
* of 8 MHz - 27 MHz
*/
switch (sgtl5000->sysclk / sys_fs) {
switch (sgtl5000->sysclk / frame_rate) {
case 256:
clk_ctl |= SGTL5000_MCLK_FREQ_256FS <<
SGTL5000_MCLK_FREQ_SHIFT;
@ -619,7 +632,7 @@ static int sgtl5000_set_clock(struct snd_soc_codec *codec, int frame_rate)
SGTL5000_MCLK_FREQ_SHIFT;
break;
default:
/* if mclk not satisify the divider, use pll */
/* if mclk does not satisfy the divider, use pll */
if (sgtl5000->master) {
clk_ctl |= SGTL5000_MCLK_FREQ_PLL <<
SGTL5000_MCLK_FREQ_SHIFT;
@ -628,7 +641,7 @@ static int sgtl5000_set_clock(struct snd_soc_codec *codec, int frame_rate)
"PLL not supported in slave mode\n");
dev_err(codec->dev, "%d ratio is not supported. "
"SYS_MCLK needs to be 256, 384 or 512 * fs\n",
sgtl5000->sysclk / sys_fs);
sgtl5000->sysclk / frame_rate);
return -EINVAL;
}
}
@ -795,7 +808,7 @@ static int ldo_regulator_enable(struct regulator_dev *dev)
SGTL5000_LINEREG_D_POWERUP,
SGTL5000_LINEREG_D_POWERUP);
/* when internal ldo enabled, simple digital power can be disabled */
/* when internal ldo is enabled, simple digital power can be disabled */
snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
SGTL5000_LINREG_SIMPLE_POWERUP,
0);
@ -1079,7 +1092,7 @@ static bool sgtl5000_readable(struct device *dev, unsigned int reg)
/*
* sgtl5000 has 3 internal power supplies:
* 1. VAG, normally set to vdda/2
* 2. chargepump, set to different value
* 2. charge pump, set to different value
* according to voltage of vdda and vddio
* 3. line out VAG, normally set to vddio/2
*
@ -1325,8 +1338,13 @@ static int sgtl5000_probe(struct snd_soc_codec *codec)
SGTL5000_HP_ZCD_EN |
SGTL5000_ADC_ZCD_EN);
snd_soc_write(codec, SGTL5000_CHIP_MIC_CTRL, 2);
snd_soc_update_bits(codec, SGTL5000_CHIP_MIC_CTRL,
SGTL5000_BIAS_R_MASK,
sgtl5000->micbias_resistor << SGTL5000_BIAS_R_SHIFT);
snd_soc_update_bits(codec, SGTL5000_CHIP_MIC_CTRL,
SGTL5000_BIAS_R_MASK,
sgtl5000->micbias_voltage << SGTL5000_BIAS_R_SHIFT);
/*
* disable DAP
* TODO:
@ -1416,10 +1434,10 @@ static int sgtl5000_i2c_probe(struct i2c_client *client,
{
struct sgtl5000_priv *sgtl5000;
int ret, reg, rev;
unsigned int mclk;
struct device_node *np = client->dev.of_node;
u32 value;
sgtl5000 = devm_kzalloc(&client->dev, sizeof(struct sgtl5000_priv),
GFP_KERNEL);
sgtl5000 = devm_kzalloc(&client->dev, sizeof(*sgtl5000), GFP_KERNEL);
if (!sgtl5000)
return -ENOMEM;
@ -1440,14 +1458,6 @@ static int sgtl5000_i2c_probe(struct i2c_client *client,
return ret;
}
/* SGTL5000 SYS_MCLK should be between 8 and 27 MHz */
mclk = clk_get_rate(sgtl5000->mclk);
if (mclk < 8000000 || mclk > 27000000) {
dev_err(&client->dev, "Invalid SYS_CLK frequency: %u.%03uMHz\n",
mclk / 1000000, mclk / 1000 % 1000);
return -EINVAL;
}
ret = clk_prepare_enable(sgtl5000->mclk);
if (ret)
return ret;
@ -1469,6 +1479,47 @@ static int sgtl5000_i2c_probe(struct i2c_client *client,
dev_info(&client->dev, "sgtl5000 revision 0x%x\n", rev);
sgtl5000->revision = rev;
if (np) {
if (!of_property_read_u32(np,
"micbias-resistor-k-ohms", &value)) {
switch (value) {
case SGTL5000_MICBIAS_OFF:
sgtl5000->micbias_resistor = 0;
break;
case SGTL5000_MICBIAS_2K:
sgtl5000->micbias_resistor = 1;
break;
case SGTL5000_MICBIAS_4K:
sgtl5000->micbias_resistor = 2;
break;
case SGTL5000_MICBIAS_8K:
sgtl5000->micbias_resistor = 3;
break;
default:
sgtl5000->micbias_resistor = 2;
dev_err(&client->dev,
"Unsuitable MicBias resistor\n");
}
} else {
/* default is 4Kohms */
sgtl5000->micbias_resistor = 2;
}
if (!of_property_read_u32(np,
"micbias-voltage-m-volts", &value)) {
/* 1250mV => 0 */
/* steps of 250mV */
if ((value >= 1250) && (value <= 3000))
sgtl5000->micbias_voltage = (value / 250) - 5;
else {
sgtl5000->micbias_voltage = 0;
dev_err(&client->dev,
"Unsuitable MicBias resistor\n");
}
} else {
sgtl5000->micbias_voltage = 0;
}
}
i2c_set_clientdata(client, sgtl5000);
/* Ensure sgtl5000 will start with sane register values */

View File

@ -1,6 +1,6 @@
config SND_SOC_SAMSUNG
tristate "ASoC support for Samsung"
depends on PLAT_SAMSUNG
depends on (PLAT_SAMSUNG || ARCH_EXYNOS)
depends on S3C64XX_PL080 || !ARCH_S3C64XX
depends on S3C24XX_DMAC || !ARCH_S3C24XX
select SND_SOC_GENERIC_DMAENGINE_PCM
@ -239,3 +239,9 @@ config SND_SOC_ODROIDX2
select SND_SAMSUNG_I2S
help
Say Y here to enable audio support for the Odroid-X2/U3.
config SND_SOC_ARNDALE_RT5631_ALC5631
tristate "Audio support for RT5631(ALC5631) on Arndale Board"
depends on SND_SOC_SAMSUNG
select SND_SAMSUNG_I2S
select SND_SOC_RT5631

View File

@ -45,6 +45,7 @@ snd-soc-lowland-objs := lowland.o
snd-soc-littlemill-objs := littlemill.o
snd-soc-bells-objs := bells.o
snd-soc-odroidx2-max98090-objs := odroidx2_max98090.o
snd-soc-arndale-rt5631-objs := arndale_rt5631.o
obj-$(CONFIG_SND_SOC_SAMSUNG_JIVE_WM8750) += snd-soc-jive-wm8750.o
obj-$(CONFIG_SND_SOC_SAMSUNG_NEO1973_WM8753) += snd-soc-neo1973-wm8753.o
@ -71,3 +72,4 @@ obj-$(CONFIG_SND_SOC_LOWLAND) += snd-soc-lowland.o
obj-$(CONFIG_SND_SOC_LITTLEMILL) += snd-soc-littlemill.o
obj-$(CONFIG_SND_SOC_BELLS) += snd-soc-bells.o
obj-$(CONFIG_SND_SOC_ODROIDX2) += snd-soc-odroidx2-max98090.o
obj-$(CONFIG_SND_SOC_ARNDALE_RT5631_ALC5631) += snd-soc-arndale-rt5631.o

View File

@ -0,0 +1,150 @@
/*
* arndale_rt5631.c
*
* Copyright (c) 2014, Insignal Co., Ltd.
*
* Author: Claude <claude@insginal.co.kr>
*
* 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; either version 2 of the License, or (at your
* option) any later version.
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include "i2s.h"
static int arndale_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
int rfs, ret;
unsigned long rclk;
rfs = 256;
rclk = params_rate(params) * rfs;
ret = snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_CDCLK,
0, SND_SOC_CLOCK_OUT);
if (ret < 0)
return ret;
ret = snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_RCLKSRC_0,
0, SND_SOC_CLOCK_OUT);
if (ret < 0)
return ret;
ret = snd_soc_dai_set_sysclk(codec_dai, 0, rclk, SND_SOC_CLOCK_OUT);
if (ret < 0)
return ret;
return 0;
}
static struct snd_soc_ops arndale_ops = {
.hw_params = arndale_hw_params,
};
static struct snd_soc_dai_link arndale_rt5631_dai[] = {
{
.name = "RT5631 HiFi",
.stream_name = "Primary",
.codec_dai_name = "rt5631-hifi",
.dai_fmt = SND_SOC_DAIFMT_I2S
| SND_SOC_DAIFMT_NB_NF
| SND_SOC_DAIFMT_CBS_CFS,
.ops = &arndale_ops,
},
};
static struct snd_soc_card arndale_rt5631 = {
.name = "Arndale RT5631",
.dai_link = arndale_rt5631_dai,
.num_links = ARRAY_SIZE(arndale_rt5631_dai),
};
static int arndale_audio_probe(struct platform_device *pdev)
{
int n, ret;
struct device_node *np = pdev->dev.of_node;
struct snd_soc_card *card = &arndale_rt5631;
card->dev = &pdev->dev;
for (n = 0; np && n < ARRAY_SIZE(arndale_rt5631_dai); n++) {
if (!arndale_rt5631_dai[n].cpu_dai_name) {
arndale_rt5631_dai[n].cpu_of_node = of_parse_phandle(np,
"samsung,audio-cpu", n);
if (!arndale_rt5631_dai[n].cpu_of_node) {
dev_err(&pdev->dev,
"Property 'samsung,audio-cpu' missing or invalid\n");
return -EINVAL;
}
}
if (!arndale_rt5631_dai[n].platform_name)
arndale_rt5631_dai[n].platform_of_node =
arndale_rt5631_dai[n].cpu_of_node;
arndale_rt5631_dai[n].codec_name = NULL;
arndale_rt5631_dai[n].codec_of_node = of_parse_phandle(np,
"samsung,audio-codec", n);
if (!arndale_rt5631_dai[0].codec_of_node) {
dev_err(&pdev->dev,
"Property 'samsung,audio-codec' missing or invalid\n");
return -EINVAL;
}
}
ret = devm_snd_soc_register_card(card->dev, card);
if (ret)
dev_err(&pdev->dev, "snd_soc_register_card() failed:%d\n", ret);
return ret;
}
static int arndale_audio_remove(struct platform_device *pdev)
{
struct snd_soc_card *card = platform_get_drvdata(pdev);
snd_soc_unregister_card(card);
return 0;
}
static const struct of_device_id samsung_arndale_rt5631_of_match[] __maybe_unused = {
{ .compatible = "samsung,arndale-rt5631", },
{ .compatible = "samsung,arndale-alc5631", },
{},
};
MODULE_DEVICE_TABLE(of, samsung_arndale_rt5631_of_match);
static struct platform_driver arndale_audio_driver = {
.driver = {
.name = "arndale-audio",
.owner = THIS_MODULE,
.pm = &snd_soc_pm_ops,
.of_match_table = of_match_ptr(samsung_arndale_rt5631_of_match),
},
.probe = arndale_audio_probe,
.remove = arndale_audio_remove,
};
module_platform_driver(arndale_audio_driver);
MODULE_AUTHOR("Claude <claude@insignal.co.kr>");
MODULE_DESCRIPTION("ALSA SoC Driver for Arndale Board");
MODULE_LICENSE("GPL");

View File

@ -33,8 +33,9 @@
#define I2SLVL3ADDR 0x3c
#define I2SSTR1 0x40
#define I2SVER 0x44
#define I2SFIC2 0x48
#define I2SFIC1 0x48
#define I2STDM 0x4c
#define I2SFSTA 0x50
#define CON_RSTCLR (1 << 31)
#define CON_FRXOFSTATUS (1 << 26)
@ -93,8 +94,6 @@
#define MOD_BLC_24BIT (2 << 13)
#define MOD_BLC_MASK (3 << 13)
#define MOD_IMS_SYSMUX (1 << 10)
#define MOD_SLAVE (1 << 11)
#define MOD_TXONLY (0 << 8)
#define MOD_RXONLY (1 << 8)
#define MOD_TXRX (2 << 8)
@ -132,7 +131,10 @@
#define EXYNOS5420_MOD_BCLK_256FS 8
#define EXYNOS5420_MOD_BCLK_MASK 0xf
#define MOD_CDCLKCON (1 << 12)
#define EXYNOS7_MOD_RCLK_64FS 4
#define EXYNOS7_MOD_RCLK_128FS 5
#define EXYNOS7_MOD_RCLK_96FS 6
#define EXYNOS7_MOD_RCLK_192FS 7
#define PSR_PSREN (1 << 15)

View File

@ -36,9 +36,24 @@ enum samsung_dai_type {
TYPE_SEC,
};
struct samsung_i2s_variant_regs {
unsigned int bfs_off;
unsigned int rfs_off;
unsigned int sdf_off;
unsigned int txr_off;
unsigned int rclksrc_off;
unsigned int mss_off;
unsigned int cdclkcon_off;
unsigned int lrp_off;
unsigned int bfs_mask;
unsigned int rfs_mask;
unsigned int ftx0cnt_off;
};
struct samsung_i2s_dai_data {
int dai_type;
u32 quirks;
const struct samsung_i2s_variant_regs *i2s_variant_regs;
};
struct i2s_dai {
@ -81,6 +96,7 @@ struct i2s_dai {
u32 suspend_i2scon;
u32 suspend_i2spsr;
unsigned long gpios[7]; /* i2s gpio line numbers */
const struct samsung_i2s_variant_regs *variant_regs;
};
/* Lock for cross i/f checks */
@ -95,7 +111,8 @@ static inline bool is_secondary(struct i2s_dai *i2s)
/* If operating in SoC-Slave mode */
static inline bool is_slave(struct i2s_dai *i2s)
{
return (readl(i2s->addr + I2SMOD) & MOD_SLAVE) ? true : false;
u32 mod = readl(i2s->addr + I2SMOD);
return (mod & (1 << i2s->variant_regs->mss_off)) ? true : false;
}
/* If this interface of the controller is transmitting data */
@ -200,14 +217,14 @@ static inline bool is_manager(struct i2s_dai *i2s)
static inline unsigned get_rfs(struct i2s_dai *i2s)
{
u32 rfs;
if (i2s->quirks & QUIRK_SUPPORTS_TDM)
rfs = readl(i2s->addr + I2SMOD) >> EXYNOS5420_MOD_RCLK_SHIFT;
else
rfs = (readl(i2s->addr + I2SMOD) >> MOD_RCLK_SHIFT);
rfs &= MOD_RCLK_MASK;
rfs = readl(i2s->addr + I2SMOD) >> i2s->variant_regs->rfs_off;
rfs &= i2s->variant_regs->rfs_mask;
switch (rfs) {
case 7: return 192;
case 6: return 96;
case 5: return 128;
case 4: return 64;
case 3: return 768;
case 2: return 384;
case 1: return 512;
@ -219,15 +236,23 @@ static inline unsigned get_rfs(struct i2s_dai *i2s)
static inline void set_rfs(struct i2s_dai *i2s, unsigned rfs)
{
u32 mod = readl(i2s->addr + I2SMOD);
int rfs_shift;
int rfs_shift = i2s->variant_regs->rfs_off;
if (i2s->quirks & QUIRK_SUPPORTS_TDM)
rfs_shift = EXYNOS5420_MOD_RCLK_SHIFT;
else
rfs_shift = MOD_RCLK_SHIFT;
mod &= ~(MOD_RCLK_MASK << rfs_shift);
mod &= ~(i2s->variant_regs->rfs_mask << rfs_shift);
switch (rfs) {
case 192:
mod |= (EXYNOS7_MOD_RCLK_192FS << rfs_shift);
break;
case 96:
mod |= (EXYNOS7_MOD_RCLK_96FS << rfs_shift);
break;
case 128:
mod |= (EXYNOS7_MOD_RCLK_128FS << rfs_shift);
break;
case 64:
mod |= (EXYNOS7_MOD_RCLK_64FS << rfs_shift);
break;
case 768:
mod |= (MOD_RCLK_768FS << rfs_shift);
break;
@ -249,14 +274,8 @@ static inline void set_rfs(struct i2s_dai *i2s, unsigned rfs)
static inline unsigned get_bfs(struct i2s_dai *i2s)
{
u32 bfs;
if (i2s->quirks & QUIRK_SUPPORTS_TDM) {
bfs = readl(i2s->addr + I2SMOD) >> EXYNOS5420_MOD_BCLK_SHIFT;
bfs &= EXYNOS5420_MOD_BCLK_MASK;
} else {
bfs = readl(i2s->addr + I2SMOD) >> MOD_BCLK_SHIFT;
bfs &= MOD_BCLK_MASK;
}
bfs = readl(i2s->addr + I2SMOD) >> i2s->variant_regs->bfs_off;
bfs &= i2s->variant_regs->bfs_mask;
switch (bfs) {
case 8: return 256;
@ -275,16 +294,8 @@ static inline unsigned get_bfs(struct i2s_dai *i2s)
static inline void set_bfs(struct i2s_dai *i2s, unsigned bfs)
{
u32 mod = readl(i2s->addr + I2SMOD);
int bfs_shift;
int tdm = i2s->quirks & QUIRK_SUPPORTS_TDM;
if (i2s->quirks & QUIRK_SUPPORTS_TDM) {
bfs_shift = EXYNOS5420_MOD_BCLK_SHIFT;
mod &= ~(EXYNOS5420_MOD_BCLK_MASK << bfs_shift);
} else {
bfs_shift = MOD_BCLK_SHIFT;
mod &= ~(MOD_BCLK_MASK << bfs_shift);
}
int bfs_shift = i2s->variant_regs->bfs_off;
/* Non-TDM I2S controllers do not support BCLK > 48 * FS */
if (!tdm && bfs > 48) {
@ -292,6 +303,8 @@ static inline void set_bfs(struct i2s_dai *i2s, unsigned bfs)
return;
}
mod &= ~(i2s->variant_regs->bfs_mask << bfs_shift);
switch (bfs) {
case 48:
mod |= (MOD_BCLK_48FS << bfs_shift);
@ -346,8 +359,9 @@ static inline int get_blc(struct i2s_dai *i2s)
static void i2s_txctrl(struct i2s_dai *i2s, int on)
{
void __iomem *addr = i2s->addr;
int txr_off = i2s->variant_regs->txr_off;
u32 con = readl(addr + I2SCON);
u32 mod = readl(addr + I2SMOD) & ~MOD_MASK;
u32 mod = readl(addr + I2SMOD) & ~(3 << txr_off);
if (on) {
con |= CON_ACTIVE;
@ -362,9 +376,9 @@ static void i2s_txctrl(struct i2s_dai *i2s, int on)
}
if (any_rx_active(i2s))
mod |= MOD_TXRX;
mod |= 2 << txr_off;
else
mod |= MOD_TXONLY;
mod |= 0 << txr_off;
} else {
if (is_secondary(i2s)) {
con |= CON_TXSDMA_PAUSE;
@ -382,7 +396,7 @@ static void i2s_txctrl(struct i2s_dai *i2s, int on)
con |= CON_TXCH_PAUSE;
if (any_rx_active(i2s))
mod |= MOD_RXONLY;
mod |= 1 << txr_off;
else
con &= ~CON_ACTIVE;
}
@ -395,23 +409,24 @@ static void i2s_txctrl(struct i2s_dai *i2s, int on)
static void i2s_rxctrl(struct i2s_dai *i2s, int on)
{
void __iomem *addr = i2s->addr;
int txr_off = i2s->variant_regs->txr_off;
u32 con = readl(addr + I2SCON);
u32 mod = readl(addr + I2SMOD) & ~MOD_MASK;
u32 mod = readl(addr + I2SMOD) & ~(3 << txr_off);
if (on) {
con |= CON_RXDMA_ACTIVE | CON_ACTIVE;
con &= ~(CON_RXDMA_PAUSE | CON_RXCH_PAUSE);
if (any_tx_active(i2s))
mod |= MOD_TXRX;
mod |= 2 << txr_off;
else
mod |= MOD_RXONLY;
mod |= 1 << txr_off;
} else {
con |= CON_RXDMA_PAUSE | CON_RXCH_PAUSE;
con &= ~CON_RXDMA_ACTIVE;
if (any_tx_active(i2s))
mod |= MOD_TXONLY;
mod |= 0 << txr_off;
else
con &= ~CON_ACTIVE;
}
@ -451,6 +466,9 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai,
struct i2s_dai *i2s = to_info(dai);
struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai;
u32 mod = readl(i2s->addr + I2SMOD);
const struct samsung_i2s_variant_regs *i2s_regs = i2s->variant_regs;
unsigned int cdcon_mask = 1 << i2s_regs->cdclkcon_off;
unsigned int rsrc_mask = 1 << i2s_regs->rclksrc_off;
switch (clk_id) {
case SAMSUNG_I2S_OPCLK:
@ -465,18 +483,18 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai,
if ((rfs && other && other->rfs && (other->rfs != rfs)) ||
(any_active(i2s) &&
(((dir == SND_SOC_CLOCK_IN)
&& !(mod & MOD_CDCLKCON)) ||
&& !(mod & cdcon_mask)) ||
((dir == SND_SOC_CLOCK_OUT)
&& (mod & MOD_CDCLKCON))))) {
&& (mod & cdcon_mask))))) {
dev_err(&i2s->pdev->dev,
"%s:%d Other DAI busy\n", __func__, __LINE__);
return -EAGAIN;
}
if (dir == SND_SOC_CLOCK_IN)
mod |= MOD_CDCLKCON;
mod |= 1 << i2s_regs->cdclkcon_off;
else
mod &= ~MOD_CDCLKCON;
mod &= ~(1 << i2s_regs->cdclkcon_off);
i2s->rfs = rfs;
break;
@ -491,8 +509,8 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai,
if (!any_active(i2s)) {
if (i2s->op_clk && !IS_ERR(i2s->op_clk)) {
if ((clk_id && !(mod & MOD_IMS_SYSMUX)) ||
(!clk_id && (mod & MOD_IMS_SYSMUX))) {
if ((clk_id && !(mod & rsrc_mask)) ||
(!clk_id && (mod & rsrc_mask))) {
clk_disable_unprepare(i2s->op_clk);
clk_put(i2s->op_clk);
} else {
@ -520,8 +538,8 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai,
other->op_clk = i2s->op_clk;
other->rclk_srcrate = i2s->rclk_srcrate;
}
} else if ((!clk_id && (mod & MOD_IMS_SYSMUX))
|| (clk_id && !(mod & MOD_IMS_SYSMUX))) {
} else if ((!clk_id && (mod & rsrc_mask))
|| (clk_id && !(mod & rsrc_mask))) {
dev_err(&i2s->pdev->dev,
"%s:%d Other DAI busy\n", __func__, __LINE__);
return -EAGAIN;
@ -533,11 +551,11 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai,
}
if (clk_id == 0)
mod &= ~MOD_IMS_SYSMUX;
mod &= ~(1 << i2s_regs->rclksrc_off);
else
mod |= MOD_IMS_SYSMUX;
break;
mod |= 1 << i2s_regs->rclksrc_off;
break;
default:
dev_err(&i2s->pdev->dev, "We don't serve that!\n");
return -EINVAL;
@ -553,16 +571,12 @@ static int i2s_set_fmt(struct snd_soc_dai *dai,
{
struct i2s_dai *i2s = to_info(dai);
u32 mod = readl(i2s->addr + I2SMOD);
int lrp_shift, sdf_shift, sdf_mask, lrp_rlow;
int lrp_shift, sdf_shift, sdf_mask, lrp_rlow, mod_slave;
u32 tmp = 0;
if (i2s->quirks & QUIRK_SUPPORTS_TDM) {
lrp_shift = EXYNOS5420_MOD_LRP_SHIFT;
sdf_shift = EXYNOS5420_MOD_SDF_SHIFT;
} else {
lrp_shift = MOD_LRP_SHIFT;
sdf_shift = MOD_SDF_SHIFT;
}
lrp_shift = i2s->variant_regs->lrp_off;
sdf_shift = i2s->variant_regs->sdf_off;
mod_slave = 1 << i2s->variant_regs->mss_off;
sdf_mask = MOD_SDF_MASK << sdf_shift;
lrp_rlow = MOD_LR_RLOW << lrp_shift;
@ -605,7 +619,7 @@ static int i2s_set_fmt(struct snd_soc_dai *dai,
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBM_CFM:
tmp |= MOD_SLAVE;
tmp |= mod_slave;
break;
case SND_SOC_DAIFMT_CBS_CFS:
/* Set default source clock in Master mode */
@ -623,13 +637,13 @@ static int i2s_set_fmt(struct snd_soc_dai *dai,
* channel.
*/
if (any_active(i2s) &&
((mod & (sdf_mask | lrp_rlow | MOD_SLAVE)) != tmp)) {
((mod & (sdf_mask | lrp_rlow | mod_slave)) != tmp)) {
dev_err(&i2s->pdev->dev,
"%s:%d Other DAI busy\n", __func__, __LINE__);
return -EAGAIN;
}
mod &= ~(sdf_mask | lrp_rlow | MOD_SLAVE);
mod &= ~(sdf_mask | lrp_rlow | mod_slave);
mod |= tmp;
writel(mod, i2s->addr + I2SMOD);
@ -751,6 +765,7 @@ static void i2s_shutdown(struct snd_pcm_substream *substream,
struct i2s_dai *i2s = to_info(dai);
struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai;
unsigned long flags;
const struct samsung_i2s_variant_regs *i2s_regs = i2s->variant_regs;
spin_lock_irqsave(&lock, flags);
@ -761,7 +776,7 @@ static void i2s_shutdown(struct snd_pcm_substream *substream,
other->mode |= DAI_MANAGER;
} else {
u32 mod = readl(i2s->addr + I2SMOD);
i2s->cdclk_out = !(mod & MOD_CDCLKCON);
i2s->cdclk_out = !(mod & (1 << i2s_regs->cdclkcon_off));
if (other)
other->cdclk_out = i2s->cdclk_out;
}
@ -914,13 +929,14 @@ i2s_delay(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
struct i2s_dai *i2s = to_info(dai);
u32 reg = readl(i2s->addr + I2SFIC);
snd_pcm_sframes_t delay;
const struct samsung_i2s_variant_regs *i2s_regs = i2s->variant_regs;
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
delay = FIC_RXCOUNT(reg);
else if (is_secondary(i2s))
delay = FICS_TXCOUNT(readl(i2s->addr + I2SFICS));
else
delay = FIC_TXCOUNT(reg);
delay = (reg >> i2s_regs->ftx0cnt_off) & 0x7f;
return delay;
}
@ -956,6 +972,7 @@ static int samsung_i2s_dai_probe(struct snd_soc_dai *dai)
{
struct i2s_dai *i2s = to_info(dai);
struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai;
int ret;
if (other && other->clk) { /* If this is probe on secondary */
samsung_asoc_init_dma_data(dai, &other->sec_dai->dma_playback,
@ -973,9 +990,14 @@ static int samsung_i2s_dai_probe(struct snd_soc_dai *dai)
if (IS_ERR(i2s->clk)) {
dev_err(&i2s->pdev->dev, "failed to get i2s_clock\n");
iounmap(i2s->addr);
return -ENOENT;
return PTR_ERR(i2s->clk);
}
ret = clk_prepare_enable(i2s->clk);
if (ret != 0) {
dev_err(&i2s->pdev->dev, "failed to enable clock: %d\n", ret);
return ret;
}
clk_prepare_enable(i2s->clk);
samsung_asoc_init_dma_data(dai, &i2s->dma_playback, &i2s->dma_capture);
@ -987,7 +1009,7 @@ static int samsung_i2s_dai_probe(struct snd_soc_dai *dai)
if (i2s->quirks & QUIRK_NEED_RSTCLR)
writel(CON_RSTCLR, i2s->addr + I2SCON);
if (i2s->quirks & QUIRK_SEC_DAI)
if (i2s->quirks & QUIRK_SUPPORTS_IDMA)
idma_reg_addr_init(i2s->addr,
i2s->sec_dai->idma_playback.dma_addr);
@ -1199,10 +1221,9 @@ static int samsung_i2s_probe(struct platform_device *pdev)
quirks = i2s_dai_data->quirks;
if (of_property_read_u32(np, "samsung,idma-addr",
&idma_addr)) {
if (quirks & QUIRK_SEC_DAI) {
dev_err(&pdev->dev, "idma address is not"\
if (quirks & QUIRK_SUPPORTS_IDMA) {
dev_info(&pdev->dev, "idma address is not"\
"specified");
return -EINVAL;
}
}
}
@ -1228,6 +1249,7 @@ static int samsung_i2s_probe(struct platform_device *pdev)
pri_dai->dma_capture.dma_size = 4;
pri_dai->base = regs_base;
pri_dai->quirks = quirks;
pri_dai->variant_regs = i2s_dai_data->i2s_variant_regs;
if (quirks & QUIRK_PRI_6CHAN)
pri_dai->i2s_dai_drv.playback.channels_max = 6;
@ -1302,20 +1324,93 @@ static int samsung_i2s_remove(struct platform_device *pdev)
return 0;
}
static const struct samsung_i2s_variant_regs i2sv3_regs = {
.bfs_off = 1,
.rfs_off = 3,
.sdf_off = 5,
.txr_off = 8,
.rclksrc_off = 10,
.mss_off = 11,
.cdclkcon_off = 12,
.lrp_off = 7,
.bfs_mask = 0x3,
.rfs_mask = 0x3,
.ftx0cnt_off = 8,
};
static const struct samsung_i2s_variant_regs i2sv6_regs = {
.bfs_off = 0,
.rfs_off = 4,
.sdf_off = 6,
.txr_off = 8,
.rclksrc_off = 10,
.mss_off = 11,
.cdclkcon_off = 12,
.lrp_off = 15,
.bfs_mask = 0xf,
.rfs_mask = 0x3,
.ftx0cnt_off = 8,
};
static const struct samsung_i2s_variant_regs i2sv7_regs = {
.bfs_off = 0,
.rfs_off = 4,
.sdf_off = 7,
.txr_off = 9,
.rclksrc_off = 11,
.mss_off = 12,
.cdclkcon_off = 22,
.lrp_off = 15,
.bfs_mask = 0xf,
.rfs_mask = 0x7,
.ftx0cnt_off = 0,
};
static const struct samsung_i2s_variant_regs i2sv5_i2s1_regs = {
.bfs_off = 0,
.rfs_off = 3,
.sdf_off = 6,
.txr_off = 8,
.rclksrc_off = 10,
.mss_off = 11,
.cdclkcon_off = 12,
.lrp_off = 15,
.bfs_mask = 0x7,
.rfs_mask = 0x7,
.ftx0cnt_off = 8,
};
static const struct samsung_i2s_dai_data i2sv3_dai_type = {
.dai_type = TYPE_PRI,
.quirks = QUIRK_NO_MUXPSR,
.i2s_variant_regs = &i2sv3_regs,
};
static const struct samsung_i2s_dai_data i2sv5_dai_type = {
.dai_type = TYPE_PRI,
.quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR,
.quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR |
QUIRK_SUPPORTS_IDMA,
.i2s_variant_regs = &i2sv3_regs,
};
static const struct samsung_i2s_dai_data i2sv6_dai_type = {
.dai_type = TYPE_PRI,
.quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR |
QUIRK_SUPPORTS_TDM | QUIRK_SUPPORTS_IDMA,
.i2s_variant_regs = &i2sv6_regs,
};
static const struct samsung_i2s_dai_data i2sv7_dai_type = {
.dai_type = TYPE_PRI,
.quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR |
QUIRK_SUPPORTS_TDM,
.i2s_variant_regs = &i2sv7_regs,
};
static const struct samsung_i2s_dai_data i2sv5_dai_type_i2s1 = {
.dai_type = TYPE_PRI,
.quirks = QUIRK_PRI_6CHAN | QUIRK_NEED_RSTCLR,
.i2s_variant_regs = &i2sv5_i2s1_regs,
};
static const struct samsung_i2s_dai_data samsung_dai_type_pri = {
@ -1329,10 +1424,13 @@ static const struct samsung_i2s_dai_data samsung_dai_type_sec = {
static struct platform_device_id samsung_i2s_driver_ids[] = {
{
.name = "samsung-i2s",
.driver_data = (kernel_ulong_t)&samsung_dai_type_pri,
.driver_data = (kernel_ulong_t)&i2sv3_dai_type,
}, {
.name = "samsung-i2s-sec",
.driver_data = (kernel_ulong_t)&samsung_dai_type_sec,
}, {
.name = "samsung-i2sv4",
.driver_data = (kernel_ulong_t)&i2sv5_dai_type,
},
{},
};
@ -1349,6 +1447,12 @@ static const struct of_device_id exynos_i2s_match[] = {
}, {
.compatible = "samsung,exynos5420-i2s",
.data = &i2sv6_dai_type,
}, {
.compatible = "samsung,exynos7-i2s",
.data = &i2sv7_dai_type,
}, {
.compatible = "samsung,exynos7-i2s1",
.data = &i2sv5_dai_type_i2s1,
},
{},
};