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:
Коммит
4c90877646
|
@ -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>;
|
||||
};
|
|
@ -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 */
|
||||
};
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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 = <®_3p3v>;
|
||||
VDDIO-supply = <®_3p3v>;
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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");
|
||||
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);
|
||||
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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");
|
|
@ -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__ */
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -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__ */
|
||||
|
|
|
@ -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->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);
|
||||
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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");
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
{},
|
||||
};
|
||||
|
|
Загрузка…
Ссылка в новой задаче