Merge remote-tracking branches 'asoc/topic/max98504', 'asoc/topic/max9867', 'asoc/topic/max9877', 'asoc/topic/mtk' and 'asoc/topic/nau8825' into asoc-next

This commit is contained in:
Mark Brown 2016-07-24 22:07:37 +01:00
36 changed files with 6177 additions and 760 deletions

View File

@ -0,0 +1,44 @@
Maxim MAX98504 class D mono speaker amplifier
This device supports I2C control interface and an IRQ output signal. It features
a PCM and PDM digital audio interface (DAI) and a differential analog input.
Required properties:
- compatible : "maxim,max98504"
- reg : should contain the I2C slave device address
- DVDD-supply, DIOVDD-supply, PVDD-supply: power supplies for the device,
as covered in ../regulator/regulator.txt
- interrupts : should specify the interrupt line the device is connected to,
as described in ../interrupt-controller/interrupts.txt
Optional properties:
- maxim,brownout-threshold - the PVDD brownout threshold, the value must be
from 0, 1...21 range, corresponding to 2.6V, 2.65V...3.65V voltage range
- maxim,brownout-attenuation - the brownout attenuation to the speaker gain
applied during the "attack hold" and "timed hold" phase, the value must be
from 0...6 (dB) range
- maxim,brownout-attack-hold-ms - the brownout attack hold phase time in ms,
0...255 (VBATBROWN_ATTK_HOLD, register 0x0018)
- maxim,brownout-timed-hold-ms - the brownout timed hold phase time in ms,
0...255 (VBATBROWN_TIME_HOLD, register 0x0019)
- maxim,brownout-release-rate-ms - the brownout release phase step time in ms,
0...255 (VBATBROWN_RELEASE, register 0x001A)
The default value when the above properties are not specified is 0,
the maxim,brownout-threshold property must be specified to actually enable
the PVDD brownout protection.
Example:
max98504@31 {
compatible = "maxim,max98504";
reg = <0x31>;
interrupt-parent = <&gpio_bank_0>;
interrupts = <2 0>;
DVDD-supply = <&regulator>;
DIOVDD-supply = <&regulator>;
PVDD-supply = <&regulator>;
};

View File

@ -0,0 +1,150 @@
Mediatek AFE PCM controller for mt2701
Required properties:
- compatible = "mediatek,mt2701-audio";
- reg: register location and size
- interrupts: Should contain AFE interrupt
- clock-names: should have these clock names:
"infra_sys_audio_clk",
"top_audio_mux1_sel",
"top_audio_mux2_sel",
"top_audio_mux1_div",
"top_audio_mux2_div",
"top_audio_48k_timing",
"top_audio_44k_timing",
"top_audpll_mux_sel",
"top_apll_sel",
"top_aud1_pll_98M",
"top_aud2_pll_90M",
"top_hadds2_pll_98M",
"top_hadds2_pll_294M",
"top_audpll",
"top_audpll_d4",
"top_audpll_d8",
"top_audpll_d16",
"top_audpll_d24",
"top_audintbus_sel",
"clk_26m",
"top_syspll1_d4",
"top_aud_k1_src_sel",
"top_aud_k2_src_sel",
"top_aud_k3_src_sel",
"top_aud_k4_src_sel",
"top_aud_k5_src_sel",
"top_aud_k6_src_sel",
"top_aud_k1_src_div",
"top_aud_k2_src_div",
"top_aud_k3_src_div",
"top_aud_k4_src_div",
"top_aud_k5_src_div",
"top_aud_k6_src_div",
"top_aud_i2s1_mclk",
"top_aud_i2s2_mclk",
"top_aud_i2s3_mclk",
"top_aud_i2s4_mclk",
"top_aud_i2s5_mclk",
"top_aud_i2s6_mclk",
"top_asm_m_sel",
"top_asm_h_sel",
"top_univpll2_d4",
"top_univpll2_d2",
"top_syspll_d5";
Example:
afe: mt2701-afe-pcm@11220000 {
compatible = "mediatek,mt2701-audio";
reg = <0 0x11220000 0 0x2000>,
<0 0x112A0000 0 0x20000>;
interrupts = <GIC_SPI 104 IRQ_TYPE_LEVEL_LOW>,
<GIC_SPI 132 IRQ_TYPE_LEVEL_LOW>;
clocks = <&infracfg CLK_INFRA_AUDIO>,
<&topckgen CLK_TOP_AUD_MUX1_SEL>,
<&topckgen CLK_TOP_AUD_MUX2_SEL>,
<&topckgen CLK_TOP_AUD_MUX1_DIV>,
<&topckgen CLK_TOP_AUD_MUX2_DIV>,
<&topckgen CLK_TOP_AUD_48K_TIMING>,
<&topckgen CLK_TOP_AUD_44K_TIMING>,
<&topckgen CLK_TOP_AUDPLL_MUX_SEL>,
<&topckgen CLK_TOP_APLL_SEL>,
<&topckgen CLK_TOP_AUD1PLL_98M>,
<&topckgen CLK_TOP_AUD2PLL_90M>,
<&topckgen CLK_TOP_HADDS2PLL_98M>,
<&topckgen CLK_TOP_HADDS2PLL_294M>,
<&topckgen CLK_TOP_AUDPLL>,
<&topckgen CLK_TOP_AUDPLL_D4>,
<&topckgen CLK_TOP_AUDPLL_D8>,
<&topckgen CLK_TOP_AUDPLL_D16>,
<&topckgen CLK_TOP_AUDPLL_D24>,
<&topckgen CLK_TOP_AUDINTBUS_SEL>,
<&clk26m>,
<&topckgen CLK_TOP_SYSPLL1_D4>,
<&topckgen CLK_TOP_AUD_K1_SRC_SEL>,
<&topckgen CLK_TOP_AUD_K2_SRC_SEL>,
<&topckgen CLK_TOP_AUD_K3_SRC_SEL>,
<&topckgen CLK_TOP_AUD_K4_SRC_SEL>,
<&topckgen CLK_TOP_AUD_K5_SRC_SEL>,
<&topckgen CLK_TOP_AUD_K6_SRC_SEL>,
<&topckgen CLK_TOP_AUD_K1_SRC_DIV>,
<&topckgen CLK_TOP_AUD_K2_SRC_DIV>,
<&topckgen CLK_TOP_AUD_K3_SRC_DIV>,
<&topckgen CLK_TOP_AUD_K4_SRC_DIV>,
<&topckgen CLK_TOP_AUD_K5_SRC_DIV>,
<&topckgen CLK_TOP_AUD_K6_SRC_DIV>,
<&topckgen CLK_TOP_AUD_I2S1_MCLK>,
<&topckgen CLK_TOP_AUD_I2S2_MCLK>,
<&topckgen CLK_TOP_AUD_I2S3_MCLK>,
<&topckgen CLK_TOP_AUD_I2S4_MCLK>,
<&topckgen CLK_TOP_AUD_I2S5_MCLK>,
<&topckgen CLK_TOP_AUD_I2S6_MCLK>,
<&topckgen CLK_TOP_ASM_M_SEL>,
<&topckgen CLK_TOP_ASM_H_SEL>,
<&topckgen CLK_TOP_UNIVPLL2_D4>,
<&topckgen CLK_TOP_UNIVPLL2_D2>,
<&topckgen CLK_TOP_SYSPLL_D5>;
clock-names = "infra_sys_audio_clk",
"top_audio_mux1_sel",
"top_audio_mux2_sel",
"top_audio_mux1_div",
"top_audio_mux2_div",
"top_audio_48k_timing",
"top_audio_44k_timing",
"top_audpll_mux_sel",
"top_apll_sel",
"top_aud1_pll_98M",
"top_aud2_pll_90M",
"top_hadds2_pll_98M",
"top_hadds2_pll_294M",
"top_audpll",
"top_audpll_d4",
"top_audpll_d8",
"top_audpll_d16",
"top_audpll_d24",
"top_audintbus_sel",
"clk_26m",
"top_syspll1_d4",
"top_aud_k1_src_sel",
"top_aud_k2_src_sel",
"top_aud_k3_src_sel",
"top_aud_k4_src_sel",
"top_aud_k5_src_sel",
"top_aud_k6_src_sel",
"top_aud_k1_src_div",
"top_aud_k2_src_div",
"top_aud_k3_src_div",
"top_aud_k4_src_div",
"top_aud_k5_src_div",
"top_aud_k6_src_div",
"top_aud_i2s1_mclk",
"top_aud_i2s2_mclk",
"top_aud_i2s3_mclk",
"top_aud_i2s4_mclk",
"top_aud_i2s5_mclk",
"top_aud_i2s6_mclk",
"top_asm_m_sel",
"top_asm_h_sel",
"top_univpll2_d4",
"top_univpll2_d2",
"top_syspll_d5";
};

View File

@ -0,0 +1,43 @@
MT2701 with CS42448 CODEC
Required properties:
- compatible: "mediatek,mt2701-cs42448-machine"
- mediatek,platform: the phandle of MT2701 ASoC platform
- audio-routing: a list of the connections between audio
- mediatek,audio-codec: the phandles of cs42448 codec
- mediatek,audio-codec-bt-mrg the phandles of bt-sco dummy codec
- pinctrl-names: Should contain only one value - "default"
- pinctrl-0: Should specify pin control groups used for this controller.
- i2s1-in-sel-gpio1, i2s1-in-sel-gpio2: Should specify two gpio pins to
control I2S1-in mux.
Example:
sound:sound {
compatible = "mediatek,mt2701-cs42448-machine";
mediatek,platform = <&afe>;
/* CS42448 Machine name */
audio-routing =
"Line Out Jack", "AOUT1L",
"Line Out Jack", "AOUT1R",
"Line Out Jack", "AOUT2L",
"Line Out Jack", "AOUT2R",
"Line Out Jack", "AOUT3L",
"Line Out Jack", "AOUT3R",
"Line Out Jack", "AOUT4L",
"Line Out Jack", "AOUT4R",
"AIN1L", "AMIC",
"AIN1R", "AMIC",
"AIN2L", "Tuner In",
"AIN2R", "Tuner In",
"AIN3L", "Satellite Tuner In",
"AIN3R", "Satellite Tuner In",
"AIN3L", "AUX In",
"AIN3R", "AUX In";
mediatek,audio-codec = <&cs42448>;
mediatek,audio-codec-bt-mrg = <&bt_sco_codec>;
pinctrl-names = "default";
pinctrl-0 = <&aud_pins_default>;
i2s1-in-sel-gpio1 = <&pio 53 0>;
i2s1-in-sel-gpio2 = <&pio 54 0>;
};

View File

@ -1,8 +1,9 @@
MT8173 with RT5650 CODECS
MT8173 with RT5650 CODECS and HDMI via I2S
Required properties:
- compatible : "mediatek,mt8173-rt5650"
- mediatek,audio-codec: the phandles of rt5650 codecs
and of the hdmi encoder node
- mediatek,platform: the phandle of MT8173 ASoC platform
Optional subnodes:
@ -12,12 +13,17 @@ Required codec-capture subnode properties:
<&rt5650 0> : Default setting. Connect rt5650 I2S1 for capture. (dai_name = rt5645-aif1)
<&rt5650 1> : Connect rt5650 I2S2 for capture. (dai_name = rt5645-aif2)
- mediatek,mclk: the MCLK source
0 : external oscillator, MCLK = 12.288M
1 : internal source from mt8173, MCLK = sampling rate*256
Example:
sound {
compatible = "mediatek,mt8173-rt5650";
mediatek,audio-codec = <&rt5650>;
mediatek,audio-codec = <&rt5650 &hdmi0>;
mediatek,platform = <&afe>;
mediatek,mclk = <0>;
codec-capture {
sound-dai = <&rt5650 1>;
};

View File

@ -557,6 +557,10 @@ config SND_SOC_MAX98357A
config SND_SOC_MAX98371
tristate
config SND_SOC_MAX98504
tristate "Maxim MAX98504 speaker amplifier"
depends on I2C
config SND_SOC_MAX9867
tristate

View File

@ -213,6 +213,7 @@ snd-soc-wm-hubs-objs := wm_hubs.o
# Amp
snd-soc-max9877-objs := max9877.o
snd-soc-max98504-objs := max98504.o
snd-soc-tpa6130a2-objs := tpa6130a2.o
snd-soc-tas2552-objs := tas2552.o
@ -429,4 +430,5 @@ obj-$(CONFIG_SND_SOC_WM_HUBS) += snd-soc-wm-hubs.o
# Amp
obj-$(CONFIG_SND_SOC_MAX9877) += snd-soc-max9877.o
obj-$(CONFIG_SND_SOC_MAX98504) += snd-soc-max98504.o
obj-$(CONFIG_SND_SOC_TPA6130A2) += snd-soc-tpa6130a2.o

383
sound/soc/codecs/max98504.c Normal file
View File

@ -0,0 +1,383 @@
/*
* MAX98504 ALSA SoC Audio driver
*
* Copyright 2013 - 2014 Maxim Integrated Products
* Copyright 2016 Samsung Electronics Co., Ltd.
*
* 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/delay.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <sound/soc.h>
#include "max98504.h"
static const char * const max98504_supply_names[] = {
"DVDD",
"DIOVDD",
"PVDD",
};
#define MAX98504_NUM_SUPPLIES ARRAY_SIZE(max98504_supply_names)
struct max98504_priv {
struct regmap *regmap;
struct regulator_bulk_data supplies[MAX98504_NUM_SUPPLIES];
unsigned int pcm_rx_channels;
bool brownout_enable;
unsigned int brownout_threshold;
unsigned int brownout_attenuation;
unsigned int brownout_attack_hold;
unsigned int brownout_timed_hold;
unsigned int brownout_release_rate;
};
static struct reg_default max98504_reg_defaults[] = {
{ 0x01, 0},
{ 0x02, 0},
{ 0x03, 0},
{ 0x04, 0},
{ 0x10, 0},
{ 0x11, 0},
{ 0x12, 0},
{ 0x13, 0},
{ 0x14, 0},
{ 0x15, 0},
{ 0x16, 0},
{ 0x17, 0},
{ 0x18, 0},
{ 0x19, 0},
{ 0x1A, 0},
{ 0x20, 0},
{ 0x21, 0},
{ 0x22, 0},
{ 0x23, 0},
{ 0x24, 0},
{ 0x25, 0},
{ 0x26, 0},
{ 0x27, 0},
{ 0x28, 0},
{ 0x30, 0},
{ 0x31, 0},
{ 0x32, 0},
{ 0x33, 0},
{ 0x34, 0},
{ 0x35, 0},
{ 0x36, 0},
{ 0x37, 0},
{ 0x38, 0},
{ 0x39, 0},
{ 0x40, 0},
{ 0x41, 0},
};
static bool max98504_volatile_register(struct device *dev, unsigned int reg)
{
switch (reg) {
case MAX98504_INTERRUPT_STATUS:
case MAX98504_INTERRUPT_FLAGS:
case MAX98504_INTERRUPT_FLAG_CLEARS:
case MAX98504_WATCHDOG_CLEAR:
case MAX98504_GLOBAL_ENABLE:
case MAX98504_SOFTWARE_RESET:
return true;
default:
return false;
}
}
static bool max98504_readable_register(struct device *dev, unsigned int reg)
{
switch (reg) {
case MAX98504_SOFTWARE_RESET:
case MAX98504_WATCHDOG_CLEAR:
case MAX98504_INTERRUPT_FLAG_CLEARS:
return false;
default:
return true;
}
}
static int max98504_pcm_rx_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);
struct max98504_priv *max98504 = snd_soc_component_get_drvdata(c);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
regmap_write(max98504->regmap, MAX98504_PCM_RX_ENABLE,
max98504->pcm_rx_channels);
break;
case SND_SOC_DAPM_POST_PMD:
regmap_write(max98504->regmap, MAX98504_PCM_RX_ENABLE, 0);
break;
}
return 0;
}
static int max98504_component_probe(struct snd_soc_component *c)
{
struct max98504_priv *max98504 = snd_soc_component_get_drvdata(c);
struct regmap *map = max98504->regmap;
int ret;
ret = regulator_bulk_enable(MAX98504_NUM_SUPPLIES, max98504->supplies);
if (ret < 0)
return ret;
regmap_write(map, MAX98504_SOFTWARE_RESET, 0x1);
msleep(20);
if (!max98504->brownout_enable)
return 0;
regmap_write(map, MAX98504_PVDD_BROWNOUT_ENABLE, 0x1);
regmap_write(map, MAX98504_PVDD_BROWNOUT_CONFIG_1,
(max98504->brownout_threshold & 0x1f) << 3 |
(max98504->brownout_attenuation & 0x3));
regmap_write(map, MAX98504_PVDD_BROWNOUT_CONFIG_2,
max98504->brownout_attack_hold & 0xff);
regmap_write(map, MAX98504_PVDD_BROWNOUT_CONFIG_3,
max98504->brownout_timed_hold & 0xff);
regmap_write(map, MAX98504_PVDD_BROWNOUT_CONFIG_4,
max98504->brownout_release_rate & 0xff);
return 0;
}
static void max98504_component_remove(struct snd_soc_component *c)
{
struct max98504_priv *max98504 = snd_soc_component_get_drvdata(c);
regulator_bulk_disable(MAX98504_NUM_SUPPLIES, max98504->supplies);
}
static const char *spk_source_mux_text[] = {
"PCM Monomix", "Analog In", "PDM Left", "PDM Right"
};
static const struct soc_enum spk_source_mux_enum =
SOC_ENUM_SINGLE(MAX98504_SPEAKER_SOURCE_SELECT,
0, ARRAY_SIZE(spk_source_mux_text),
spk_source_mux_text);
static const struct snd_kcontrol_new spk_source_mux =
SOC_DAPM_ENUM("SPK Source", spk_source_mux_enum);
static const struct snd_soc_dapm_route max98504_dapm_routes[] = {
{ "SPKOUT", NULL, "Global Enable" },
{ "SPK Source", "PCM Monomix", "DAC PCM" },
{ "SPK Source", "Analog In", "AIN" },
{ "SPK Source", "PDM Left", "DAC PDM" },
{ "SPK Source", "PDM Right", "DAC PDM" },
};
static const struct snd_soc_dapm_widget max98504_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("Global Enable", MAX98504_GLOBAL_ENABLE,
0, 0, NULL, 0),
SND_SOC_DAPM_INPUT("AIN"),
SND_SOC_DAPM_AIF_OUT("AIF2OUTL", "AIF2 Capture", 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_OUT("AIF2OUTR", "AIF2 Capture", 1, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_DAC_E("DAC PCM", NULL, SND_SOC_NOPM, 0, 0,
max98504_pcm_rx_ev,
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
SND_SOC_DAPM_DAC("DAC PDM", NULL, MAX98504_PDM_RX_ENABLE, 0, 0),
SND_SOC_DAPM_MUX("SPK Source", SND_SOC_NOPM, 0, 0, &spk_source_mux),
SND_SOC_DAPM_REG(snd_soc_dapm_spk, "SPKOUT",
MAX98504_SPEAKER_ENABLE, 0, 1, 1, 0),
};
static int max98504_set_tdm_slot(struct snd_soc_dai *dai,
unsigned int tx_mask, unsigned int rx_mask,
int slots, int slot_width)
{
struct max98504_priv *max98504 = snd_soc_dai_get_drvdata(dai);
struct regmap *map = max98504->regmap;
switch (dai->id) {
case MAX98504_DAI_ID_PCM:
regmap_write(map, MAX98504_PCM_TX_ENABLE, tx_mask);
max98504->pcm_rx_channels = rx_mask;
break;
case MAX98504_DAI_ID_PDM:
regmap_write(map, MAX98504_PDM_TX_ENABLE, tx_mask);
break;
default:
WARN_ON(1);
}
return 0;
}
static int max98504_set_channel_map(struct snd_soc_dai *dai,
unsigned int tx_num, unsigned int *tx_slot,
unsigned int rx_num, unsigned int *rx_slot)
{
struct max98504_priv *max98504 = snd_soc_dai_get_drvdata(dai);
struct regmap *map = max98504->regmap;
unsigned int i, sources = 0;
for (i = 0; i < tx_num; i++)
if (tx_slot[i])
sources |= (1 << i);
switch (dai->id) {
case MAX98504_DAI_ID_PCM:
regmap_write(map, MAX98504_PCM_TX_CHANNEL_SOURCES,
sources);
break;
case MAX98504_DAI_ID_PDM:
regmap_write(map, MAX98504_PDM_TX_CONTROL, sources);
break;
default:
WARN_ON(1);
}
regmap_write(map, MAX98504_MEASUREMENT_ENABLE, sources ? 0x3 : 0x01);
return 0;
}
static const struct snd_soc_dai_ops max98504_dai_ops = {
.set_tdm_slot = max98504_set_tdm_slot,
.set_channel_map = max98504_set_channel_map,
};
#define MAX98504_FORMATS (SNDRV_PCM_FMTBIT_S8|SNDRV_PCM_FMTBIT_S16_LE|\
SNDRV_PCM_FMTBIT_S24_LE|SNDRV_PCM_FMTBIT_S32_LE)
#define MAX98504_PDM_RATES (SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|\
SNDRV_PCM_RATE_32000|SNDRV_PCM_RATE_44100|\
SNDRV_PCM_RATE_48000|SNDRV_PCM_RATE_88200|\
SNDRV_PCM_RATE_96000)
static struct snd_soc_dai_driver max98504_dai[] = {
/* TODO: Add the PCM interface definitions */
{
.name = "max98504-aif2",
.id = MAX98504_DAI_ID_PDM,
.playback = {
.stream_name = "AIF2 Playback",
.channels_min = 1,
.channels_max = 2,
.rates = MAX98504_PDM_RATES,
.formats = MAX98504_FORMATS,
},
.capture = {
.stream_name = "AIF2 Capture",
.channels_min = 1,
.channels_max = 2,
.rates = MAX98504_PDM_RATES,
.formats = MAX98504_FORMATS,
},
.ops = &max98504_dai_ops,
},
};
static const struct snd_soc_component_driver max98504_component_driver = {
.probe = max98504_component_probe,
.remove = max98504_component_remove,
.dapm_widgets = max98504_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(max98504_dapm_widgets),
.dapm_routes = max98504_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(max98504_dapm_routes),
};
static const struct regmap_config max98504_regmap = {
.reg_bits = 16,
.val_bits = 8,
.max_register = MAX98504_MAX_REGISTER,
.reg_defaults = max98504_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(max98504_reg_defaults),
.volatile_reg = max98504_volatile_register,
.readable_reg = max98504_readable_register,
.cache_type = REGCACHE_RBTREE,
};
static int max98504_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct device *dev = &client->dev;
struct device_node *node = dev->of_node;
struct max98504_priv *max98504;
int i, ret;
max98504 = devm_kzalloc(dev, sizeof(*max98504), GFP_KERNEL);
if (!max98504)
return -ENOMEM;
if (node) {
if (!of_property_read_u32(node, "maxim,brownout-threshold",
&max98504->brownout_threshold))
max98504->brownout_enable = true;
of_property_read_u32(node, "maxim,brownout-attenuation",
&max98504->brownout_attenuation);
of_property_read_u32(node, "maxim,brownout-attack-hold-ms",
&max98504->brownout_attack_hold);
of_property_read_u32(node, "maxim,brownout-timed-hold-ms",
&max98504->brownout_timed_hold);
of_property_read_u32(node, "maxim,brownout-release-rate-ms",
&max98504->brownout_release_rate);
}
max98504->regmap = devm_regmap_init_i2c(client, &max98504_regmap);
if (IS_ERR(max98504->regmap)) {
ret = PTR_ERR(max98504->regmap);
dev_err(&client->dev, "regmap initialization failed: %d\n", ret);
return ret;
}
for (i = 0; i < MAX98504_NUM_SUPPLIES; i++)
max98504->supplies[i].supply = max98504_supply_names[i];
ret = devm_regulator_bulk_get(dev, MAX98504_NUM_SUPPLIES,
max98504->supplies);
if (ret < 0)
return ret;
i2c_set_clientdata(client, max98504);
return devm_snd_soc_register_component(dev, &max98504_component_driver,
max98504_dai, ARRAY_SIZE(max98504_dai));
}
#ifdef CONFIG_OF
static const struct of_device_id max98504_of_match[] = {
{ .compatible = "maxim,max98504" },
{ },
};
MODULE_DEVICE_TABLE(of, max98504_of_match);
#endif
static const struct i2c_device_id max98504_i2c_id[] = {
{ "max98504" },
{ }
};
MODULE_DEVICE_TABLE(i2c, max98504_i2c_id);
static struct i2c_driver max98504_i2c_driver = {
.driver = {
.name = "max98504",
.of_match_table = of_match_ptr(max98504_of_match),
},
.probe = max98504_i2c_probe,
.id_table = max98504_i2c_id,
};
module_i2c_driver(max98504_i2c_driver);
MODULE_DESCRIPTION("ASoC MAX98504 driver");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,59 @@
/*
* MAX98504 ALSA SoC Audio driver
*
* Copyright 2011 - 2012 Maxim Integrated Products
* Copyright 2016 Samsung Electronics Co., Ltd.
*
* 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 MAX98504_H_
#define MAX98504_H_
/*
* MAX98504 Register Definitions
*/
#define MAX98504_INTERRUPT_STATUS 0x01
#define MAX98504_INTERRUPT_FLAGS 0x02
#define MAX98504_INTERRUPT_ENABLE 0x03
#define MAX98504_INTERRUPT_FLAG_CLEARS 0x04
#define MAX98504_GPIO_ENABLE 0x10
#define MAX98504_GPIO_CONFIG 0x11
#define MAX98504_WATCHDOG_ENABLE 0x12
#define MAX98504_WATCHDOG_CONFIG 0x13
#define MAX98504_WATCHDOG_CLEAR 0x14
#define MAX98504_CLOCK_MONITOR_ENABLE 0x15
#define MAX98504_PVDD_BROWNOUT_ENABLE 0x16
#define MAX98504_PVDD_BROWNOUT_CONFIG_1 0x17
#define MAX98504_PVDD_BROWNOUT_CONFIG_2 0x18
#define MAX98504_PVDD_BROWNOUT_CONFIG_3 0x19
#define MAX98504_PVDD_BROWNOUT_CONFIG_4 0x1a
#define MAX98504_PCM_RX_ENABLE 0x20
#define MAX98504_PCM_TX_ENABLE 0x21
#define MAX98504_PCM_TX_HIZ_CONTROL 0x22
#define MAX98504_PCM_TX_CHANNEL_SOURCES 0x23
#define MAX98504_PCM_MODE_CONFIG 0x24
#define MAX98504_PCM_DSP_CONFIG 0x25
#define MAX98504_PCM_CLOCK_SETUP 0x26
#define MAX98504_PCM_SAMPLE_RATE_SETUP 0x27
#define MAX98504_PCM_TO_SPEAKER_MONOMIX 0x28
#define MAX98504_PDM_TX_ENABLE 0x30
#define MAX98504_PDM_TX_HIZ_CONTROL 0x31
#define MAX98504_PDM_TX_CONTROL 0x32
#define MAX98504_PDM_RX_ENABLE 0x33
#define MAX98504_SPEAKER_ENABLE 0x34
#define MAX98504_SPEAKER_SOURCE_SELECT 0x35
#define MAX98504_MEASUREMENT_ENABLE 0x36
#define MAX98504_ANALOGUE_INPUT_GAIN 0x37
#define MAX98504_TEMPERATURE_LIMIT_CONFIG 0x38
#define MAX98504_GLOBAL_ENABLE 0x40
#define MAX98504_SOFTWARE_RESET 0x41
#define MAX98504_REV_ID 0x7fff
#define MAX98504_MAX_REGISTER 0x7fff
#define MAX98504_DAI_ID_PCM 1
#define MAX98504_DAI_ID_PDM 2
#endif /* MAX98504_H_ */

0
sound/soc/codecs/max9867.c Executable file → Normal file
View File

0
sound/soc/codecs/max9867.h Executable file → Normal file
View File

View File

@ -32,6 +32,4 @@
#define MAX9877_BYPASS (1 << 6)
#define MAX9877_SHDN (1 << 7)
extern int max9877_add_controls(struct snd_soc_codec *codec);
#endif

File diff suppressed because it is too large Load Diff

View File

@ -93,12 +93,21 @@
#define NAU8825_REG_CHARGE_PUMP_INPUT_READ 0x81
#define NAU8825_REG_GENERAL_STATUS 0x82
#define NAU8825_REG_MAX NAU8825_REG_GENERAL_STATUS
/* 16-bit control register address, and 16-bits control register data */
#define NAU8825_REG_ADDR_LEN 16
#define NAU8825_REG_DATA_LEN 16
/* ENA_CTRL (0x1) */
#define NAU8825_ENABLE_DACR_SFT 10
#define NAU8825_ENABLE_DACR (1 << NAU8825_ENABLE_DACR_SFT)
#define NAU8825_ENABLE_DACL_SFT 9
#define NAU8825_ENABLE_DACL (1 << NAU8825_ENABLE_DACL_SFT)
#define NAU8825_ENABLE_ADC_SFT 8
#define NAU8825_ENABLE_ADC (1 << NAU8825_ENABLE_ADC_SFT)
#define NAU8825_ENABLE_ADC_CLK_SFT 7
#define NAU8825_ENABLE_ADC_CLK (1 << NAU8825_ENABLE_ADC_CLK_SFT)
#define NAU8825_ENABLE_DAC_CLK_SFT 6
#define NAU8825_ENABLE_DAC_CLK (1 << NAU8825_ENABLE_DAC_CLK_SFT)
#define NAU8825_ENABLE_SAR_SFT 1
/* CLK_DIVIDER (0x3) */
@ -113,20 +122,28 @@
/* FLL3 (0x06) */
#define NAU8825_FLL_INTEGER_MASK (0x3ff << 0)
#define NAU8825_FLL_CLK_SRC_SFT 10
#define NAU8825_FLL_CLK_SRC_MASK (0x3 << NAU8825_FLL_CLK_SRC_SFT)
#define NAU8825_FLL_CLK_SRC_MCLK (0 << NAU8825_FLL_CLK_SRC_SFT)
#define NAU8825_FLL_CLK_SRC_BLK (0x2 << NAU8825_FLL_CLK_SRC_SFT)
#define NAU8825_FLL_CLK_SRC_FS (0x3 << NAU8825_FLL_CLK_SRC_SFT)
/* FLL4 (0x07) */
#define NAU8825_FLL_REF_DIV_MASK (0x3 << 10)
/* FLL5 (0x08) */
#define NAU8825_FLL_FILTER_SW_MASK (0x1 << 14)
#define NAU8825_FLL_PDB_DAC_EN (0x1 << 15)
#define NAU8825_FLL_LOOP_FTR_EN (0x1 << 14)
#define NAU8825_FLL_CLK_SW_MASK (0x1 << 13)
#define NAU8825_FLL_CLK_SW_N2 (0x1 << 13)
#define NAU8825_FLL_CLK_SW_REF (0x0 << 13)
#define NAU8825_FLL_FTR_SW_MASK (0x1 << 12)
#define NAU8825_FLL_FTR_SW_ACCU (0x1 << 12)
#define NAU8825_FLL_FTR_SW_FILTER (0x0 << 12)
/* FLL6 (0x9) */
#define NAU8825_DCO_EN_MASK (0x1 << 15)
#define NAU8825_DCO_EN (0x1 << 15)
#define NAU8825_DCO_DIS (0x0 << 15)
#define NAU8825_SDM_EN_MASK (0x1 << 14)
#define NAU8825_SDM_EN (0x1 << 14)
#define NAU8825_SDM_DIS (0x0 << 14)
/* HSD_CTRL (0xc) */
#define NAU8825_HSD_AUTO_MODE (1 << 6)
@ -136,6 +153,7 @@
/* JACK_DET_CTRL (0xd) */
#define NAU8825_JACK_DET_RESTART (1 << 9)
#define NAU8825_JACK_DET_DB_BYPASS (1 << 8)
#define NAU8825_JACK_INSERT_DEBOUNCE_SFT 5
#define NAU8825_JACK_INSERT_DEBOUNCE_MASK (0x7 << NAU8825_JACK_INSERT_DEBOUNCE_SFT)
#define NAU8825_JACK_EJECT_DEBOUNCE_SFT 2
@ -145,9 +163,11 @@
/* INTERRUPT_MASK (0xf) */
#define NAU8825_IRQ_OUTPUT_EN (1 << 11)
#define NAU8825_IRQ_HEADSET_COMPLETE_EN (1 << 10)
#define NAU8825_IRQ_RMS_EN (1 << 8)
#define NAU8825_IRQ_KEY_RELEASE_EN (1 << 7)
#define NAU8825_IRQ_KEY_SHORT_PRESS_EN (1 << 5)
#define NAU8825_IRQ_EJECT_EN (1 << 2)
#define NAU8825_IRQ_INSERT_EN (1 << 0)
/* IRQ_STATUS (0x10) */
#define NAU8825_HEADSET_COMPLETION_IRQ (1 << 10)
@ -168,6 +188,7 @@
#define NAU8825_IRQ_KEY_RELEASE_DIS (1 << 7)
#define NAU8825_IRQ_KEY_SHORT_PRESS_DIS (1 << 5)
#define NAU8825_IRQ_EJECT_DIS (1 << 2)
#define NAU8825_IRQ_INSERT_DIS (1 << 0)
/* SAR_CTRL (0x13) */
#define NAU8825_SAR_ADC_EN_SFT 12
@ -217,10 +238,21 @@
/* I2S_PCM_CTRL2 (0x1d) */
#define NAU8825_I2S_TRISTATE (1 << 15) /* 0 - normal mode, 1 - Hi-Z output */
#define NAU8825_I2S_DRV_SFT 12
#define NAU8825_I2S_DRV_MASK (0x3 << NAU8825_I2S_DRV_SFT)
#define NAU8825_I2S_MS_SFT 3
#define NAU8825_I2S_MS_MASK (1 << NAU8825_I2S_MS_SFT)
#define NAU8825_I2S_MS_MASTER (1 << NAU8825_I2S_MS_SFT)
#define NAU8825_I2S_MS_SLAVE (0 << NAU8825_I2S_MS_SFT)
#define NAU8825_I2S_BLK_DIV_MASK 0x7
/* BIQ_CTRL (0x20) */
#define NAU8825_BIQ_WRT_SFT 4
#define NAU8825_BIQ_WRT_EN (1 << NAU8825_BIQ_WRT_SFT)
#define NAU8825_BIQ_PATH_SFT 0
#define NAU8825_BIQ_PATH_MASK (1 << NAU8825_BIQ_PATH_SFT)
#define NAU8825_BIQ_PATH_ADC (0 << NAU8825_BIQ_PATH_SFT)
#define NAU8825_BIQ_PATH_DAC (1 << NAU8825_BIQ_PATH_SFT)
/* ADC_RATE (0x2b) */
#define NAU8825_ADC_SYNC_DOWN_SFT 0
@ -239,22 +271,72 @@
#define NAU8825_DAC_OVERSAMPLE_128 2
#define NAU8825_DAC_OVERSAMPLE_32 4
/* ADC_DGAIN_CTRL (0x30) */
#define NAU8825_ADC_DIG_VOL_MASK 0xff
/* MUTE_CTRL (0x31) */
#define NAU8825_DAC_ZERO_CROSSING_EN (1 << 9)
#define NAU8825_DAC_SOFT_MUTE (1 << 9)
/* HSVOL_CTRL (0x32) */
#define NAU8825_HP_MUTE (1 << 15)
#define NAU8825_HP_MUTE_AUTO (1 << 14)
#define NAU8825_HPL_MUTE (1 << 13)
#define NAU8825_HPR_MUTE (1 << 12)
#define NAU8825_HPL_VOL_SFT 6
#define NAU8825_HPL_VOL_MASK (0x3f << NAU8825_HPL_VOL_SFT)
#define NAU8825_HPR_VOL_SFT 0
#define NAU8825_HPR_VOL_MASK (0x3f << NAU8825_HPR_VOL_SFT)
#define NAU8825_HP_VOL_MIN 0x36
/* DACL_CTRL (0x33) */
#define NAU8825_DACL_CH_SEL_SFT 9
#define NAU8825_DACL_CH_SEL_MASK (0x1 << NAU8825_DACL_CH_SEL_SFT)
#define NAU8825_DACL_CH_SEL_L (0x0 << NAU8825_DACL_CH_SEL_SFT)
#define NAU8825_DACL_CH_SEL_R (0x1 << NAU8825_DACL_CH_SEL_SFT)
#define NAU8825_DACL_CH_VOL_MASK 0xff
/* DACR_CTRL (0x34) */
#define NAU8825_DACR_CH_SEL_SFT 9
#define NAU8825_DACR_CH_SEL_MASK (0x1 << NAU8825_DACR_CH_SEL_SFT)
#define NAU8825_DACR_CH_SEL_L (0x0 << NAU8825_DACR_CH_SEL_SFT)
#define NAU8825_DACR_CH_SEL_R (0x1 << NAU8825_DACR_CH_SEL_SFT)
#define NAU8825_DACR_CH_VOL_MASK 0xff
/* IMM_MODE_CTRL (0x4C) */
#define NAU8825_IMM_THD_SFT 8
#define NAU8825_IMM_THD_MASK (0x3f << NAU8825_IMM_THD_SFT)
#define NAU8825_IMM_GEN_VOL_SFT 6
#define NAU8825_IMM_GEN_VOL_MASK (0x3 << NAU8825_IMM_GEN_VOL_SFT)
#define NAU8825_IMM_GEN_VOL_1_2nd (0x0 << NAU8825_IMM_GEN_VOL_SFT)
#define NAU8825_IMM_GEN_VOL_1_4th (0x1 << NAU8825_IMM_GEN_VOL_SFT)
#define NAU8825_IMM_GEN_VOL_1_8th (0x2 << NAU8825_IMM_GEN_VOL_SFT)
#define NAU8825_IMM_GEN_VOL_1_16th (0x3 << NAU8825_IMM_GEN_VOL_SFT)
#define NAU8825_IMM_CYC_SFT 4
#define NAU8825_IMM_CYC_MASK (0x3 << NAU8825_IMM_CYC_SFT)
#define NAU8825_IMM_CYC_1024 (0x0 << NAU8825_IMM_CYC_SFT)
#define NAU8825_IMM_CYC_2048 (0x1 << NAU8825_IMM_CYC_SFT)
#define NAU8825_IMM_CYC_4096 (0x2 << NAU8825_IMM_CYC_SFT)
#define NAU8825_IMM_CYC_8192 (0x3 << NAU8825_IMM_CYC_SFT)
#define NAU8825_IMM_EN (1 << 3)
#define NAU8825_IMM_DAC_SRC_MASK 0x7
#define NAU8825_IMM_DAC_SRC_BIQ 0x0
#define NAU8825_IMM_DAC_SRC_DRC 0x1
#define NAU8825_IMM_DAC_SRC_MIX 0x2
#define NAU8825_IMM_DAC_SRC_SIN 0x3
/* CLASSG_CTRL (0x50) */
#define NAU8825_CLASSG_TIMER_SFT 8
#define NAU8825_CLASSG_TIMER_MASK (0x3f << NAU8825_CLASSG_TIMER_SFT)
#define NAU8825_CLASSG_TIMER_1ms (0x1 << NAU8825_CLASSG_TIMER_SFT)
#define NAU8825_CLASSG_TIMER_2ms (0x2 << NAU8825_CLASSG_TIMER_SFT)
#define NAU8825_CLASSG_TIMER_8ms (0x4 << NAU8825_CLASSG_TIMER_SFT)
#define NAU8825_CLASSG_TIMER_16ms (0x8 << NAU8825_CLASSG_TIMER_SFT)
#define NAU8825_CLASSG_TIMER_32ms (0x10 << NAU8825_CLASSG_TIMER_SFT)
#define NAU8825_CLASSG_TIMER_64ms (0x20 << NAU8825_CLASSG_TIMER_SFT)
#define NAU8825_CLASSG_LDAC_EN (0x1 << 2)
#define NAU8825_CLASSG_RDAC_EN (0x1 << 1)
#define NAU8825_CLASSG_EN (1 << 0)
/* I2C_DEVICE_ID (0x58) */
@ -263,7 +345,12 @@
#define NAU8825_SOFTWARE_ID_NAU8825 0x0
/* BIAS_ADJ (0x66) */
#define NAU8825_BIAS_TESTDAC_EN (0x3 << 8)
#define NAU8825_BIAS_HPR_IMP (1 << 15)
#define NAU8825_BIAS_HPL_IMP (1 << 14)
#define NAU8825_BIAS_TESTDAC_SFT 8
#define NAU8825_BIAS_TESTDAC_EN (0x3 << NAU8825_BIAS_TESTDAC_SFT)
#define NAU8825_BIAS_TESTDACR_EN (0x2 << NAU8825_BIAS_TESTDAC_SFT)
#define NAU8825_BIAS_TESTDACL_EN (0x1 << NAU8825_BIAS_TESTDAC_SFT)
#define NAU8825_BIAS_VMID (1 << 6)
#define NAU8825_BIAS_VMID_SEL_SFT 4
#define NAU8825_BIAS_VMID_SEL_MASK (3 << NAU8825_BIAS_VMID_SEL_SFT)
@ -282,6 +369,11 @@
#define NAU8825_POWERUP_ADCL (1 << 6)
/* RDAC (0x73) */
#define NAU8825_RDAC_FS_BCLK_ENB (1 << 15)
#define NAU8825_RDAC_EN_SFT 12
#define NAU8825_RDAC_EN (0x3 << NAU8825_RDAC_EN_SFT)
#define NAU8825_RDAC_CLK_EN_SFT 8
#define NAU8825_RDAC_CLK_EN (0x3 << NAU8825_RDAC_CLK_EN_SFT)
#define NAU8825_RDAC_CLK_DELAY_SFT 4
#define NAU8825_RDAC_CLK_DELAY_MASK (0x7 << NAU8825_RDAC_CLK_DELAY_SFT)
#define NAU8825_RDAC_VREF_SFT 2
@ -318,8 +410,21 @@
/* System Clock Source */
enum {
NAU8825_CLK_MCLK = 0,
NAU8825_CLK_DIS = 0,
NAU8825_CLK_MCLK,
NAU8825_CLK_INTERNAL,
NAU8825_CLK_FLL_MCLK,
NAU8825_CLK_FLL_BLK,
NAU8825_CLK_FLL_FS,
};
/* Cross talk detection state */
enum {
NAU8825_XTALK_PREPARE = 0,
NAU8825_XTALK_HPR_R2L,
NAU8825_XTALK_HPL_R2L,
NAU8825_XTALK_IMM,
NAU8825_XTALK_DONE,
};
struct nau8825 {
@ -328,6 +433,8 @@ struct nau8825 {
struct snd_soc_dapm_context *dapm;
struct snd_soc_jack *jack;
struct clk *mclk;
struct work_struct xtalk_work;
struct semaphore xtalk_sem;
int irq;
int mclk_freq; /* 0 - mclk is disabled */
int button_pressed;
@ -346,6 +453,12 @@ struct nau8825 {
int key_debounce;
int jack_insert_debounce;
int jack_eject_debounce;
int high_imped;
int xtalk_state;
int xtalk_event;
int xtalk_event_mask;
bool xtalk_protect;
int imp_rms[NAU8825_XTALK_IMM];
};
int nau8825_enable_jack_detect(struct snd_soc_codec *codec,

View File

@ -1,15 +1,40 @@
config SND_SOC_MEDIATEK
tristate "ASoC support for Mediatek chip"
tristate
config SND_SOC_MT2701
tristate "ASoC support for Mediatek MT2701 chip"
depends on ARCH_MEDIATEK
select SND_SOC_MEDIATEK
help
This adds ASoC platform driver support for Mediatek chip
This adds ASoC driver for Mediatek MT2701 boards
that can be used with other codecs.
Select Y if you have such device.
If unsure select "N".
config SND_SOC_MT2701_CS42448
tristate "ASoc Audio driver for MT2701 with CS42448 codec"
depends on SND_SOC_MT2701
select SND_SOC_CS42XX8_I2C
select SND_SOC_BT_SCO
help
This adds ASoC driver for Mediatek MT2701 boards
with the CS42448 codecs.
Select Y if you have such device.
If unsure select "N".
config SND_SOC_MT8173
tristate "ASoC support for Mediatek MT8173 chip"
depends on ARCH_MEDIATEK
select SND_SOC_MEDIATEK
help
This adds ASoC platform driver support for Mediatek MT8173 chip
that can be used with other codecs.
Select Y if you have such device.
Ex: MT8173
config SND_SOC_MT8173_MAX98090
tristate "ASoC Audio driver for MT8173 with MAX98090 codec"
depends on SND_SOC_MEDIATEK && I2C
depends on SND_SOC_MT8173 && I2C
select SND_SOC_MAX98090
help
This adds ASoC driver for Mediatek MT8173 boards
@ -19,8 +44,9 @@ config SND_SOC_MT8173_MAX98090
config SND_SOC_MT8173_RT5650
tristate "ASoC Audio driver for MT8173 with RT5650 codec"
depends on SND_SOC_MEDIATEK && I2C
depends on SND_SOC_MT8173 && I2C
select SND_SOC_RT5645
select SND_SOC_HDMI_CODEC
help
This adds ASoC driver for Mediatek MT8173 boards
with the RT5650 audio codec.
@ -29,7 +55,7 @@ config SND_SOC_MT8173_RT5650
config SND_SOC_MT8173_RT5650_RT5514
tristate "ASoC Audio driver for MT8173 with RT5650 RT5514 codecs"
depends on SND_SOC_MEDIATEK && I2C
depends on SND_SOC_MT8173 && I2C
select SND_SOC_RT5645
select SND_SOC_RT5514
help
@ -40,7 +66,7 @@ config SND_SOC_MT8173_RT5650_RT5514
config SND_SOC_MT8173_RT5650_RT5676
tristate "ASoC Audio driver for MT8173 with RT5650 RT5676 codecs"
depends on SND_SOC_MEDIATEK && I2C
depends on SND_SOC_MT8173 && I2C
select SND_SOC_RT5645
select SND_SOC_RT5677
select SND_SOC_HDMI_CODEC

View File

@ -1,7 +1,3 @@
# MTK Platform Support
obj-$(CONFIG_SND_SOC_MEDIATEK) += mtk-afe-pcm.o
# Machine support
obj-$(CONFIG_SND_SOC_MT8173_MAX98090) += mt8173-max98090.o
obj-$(CONFIG_SND_SOC_MT8173_RT5650) += mt8173-rt5650.o
obj-$(CONFIG_SND_SOC_MT8173_RT5650_RT5514) += mt8173-rt5650-rt5514.o
obj-$(CONFIG_SND_SOC_MT8173_RT5650_RT5676) += mt8173-rt5650-rt5676.o
obj-$(CONFIG_SND_SOC_MEDIATEK) += common/
obj-$(CONFIG_SND_SOC_MT2701) += mt2701/
obj-$(CONFIG_SND_SOC_MT8173) += mt8173/

View File

@ -0,0 +1,16 @@
#
# Copyright (C) 2015 MediaTek Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# platform driver
snd-soc-mtk-common-objs := mtk-afe-platform-driver.o mtk-afe-fe-dai.o
obj-$(CONFIG_SND_SOC_MEDIATEK) += snd-soc-mtk-common.o

View File

@ -0,0 +1,379 @@
/*
* mtk-afe-fe-dais.c -- Mediatek afe fe dai operator
*
* Copyright (c) 2016 MediaTek Inc.
* Author: Garlic Tseng <garlic.tseng@mediatek.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 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/module.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <sound/soc.h>
#include "mtk-afe-fe-dai.h"
#include "mtk-base-afe.h"
#define AFE_BASE_END_OFFSET 8
int mtk_regmap_update_bits(struct regmap *map, int reg, unsigned int mask,
unsigned int val)
{
if (reg < 0)
return 0;
return regmap_update_bits(map, reg, mask, val);
}
int mtk_regmap_write(struct regmap *map, int reg, unsigned int val)
{
if (reg < 0)
return 0;
return regmap_write(map, reg, val);
}
int mtk_afe_fe_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
struct snd_pcm_runtime *runtime = substream->runtime;
int memif_num = rtd->cpu_dai->id;
struct mtk_base_afe_memif *memif = &afe->memif[memif_num];
const struct snd_pcm_hardware *mtk_afe_hardware = afe->mtk_afe_hardware;
int ret;
memif->substream = substream;
snd_pcm_hw_constraint_step(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 16);
/* enable agent */
mtk_regmap_update_bits(afe->regmap, memif->data->agent_disable_reg,
1 << memif->data->agent_disable_shift,
0 << memif->data->agent_disable_shift);
snd_soc_set_runtime_hwparams(substream, mtk_afe_hardware);
/*
* Capture cannot use ping-pong buffer since hw_ptr at IRQ may be
* smaller than period_size due to AFE's internal buffer.
* This easily leads to overrun when avail_min is period_size.
* One more period can hold the possible unread buffer.
*/
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
int periods_max = mtk_afe_hardware->periods_max;
ret = snd_pcm_hw_constraint_minmax(runtime,
SNDRV_PCM_HW_PARAM_PERIODS,
3, periods_max);
if (ret < 0) {
dev_err(afe->dev, "hw_constraint_minmax failed\n");
return ret;
}
}
ret = snd_pcm_hw_constraint_integer(runtime,
SNDRV_PCM_HW_PARAM_PERIODS);
if (ret < 0)
dev_err(afe->dev, "snd_pcm_hw_constraint_integer failed\n");
/* dynamic allocate irq to memif */
if (memif->irq_usage < 0) {
int irq_id = mtk_dynamic_irq_acquire(afe);
if (irq_id != afe->irqs_size) {
/* link */
memif->irq_usage = irq_id;
} else {
dev_err(afe->dev, "%s() error: no more asys irq\n",
__func__);
ret = -EBUSY;
}
}
return ret;
}
EXPORT_SYMBOL_GPL(mtk_afe_fe_startup);
void mtk_afe_fe_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
struct mtk_base_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
int irq_id;
irq_id = memif->irq_usage;
mtk_regmap_update_bits(afe->regmap, memif->data->agent_disable_reg,
1 << memif->data->agent_disable_shift,
1 << memif->data->agent_disable_shift);
if (!memif->const_irq) {
mtk_dynamic_irq_release(afe, irq_id);
memif->irq_usage = -1;
memif->substream = NULL;
}
}
EXPORT_SYMBOL_GPL(mtk_afe_fe_shutdown);
int mtk_afe_fe_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
struct mtk_base_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
int msb_at_bit33 = 0;
int ret, fs = 0;
ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
if (ret < 0)
return ret;
msb_at_bit33 = upper_32_bits(substream->runtime->dma_addr) ? 1 : 0;
memif->phys_buf_addr = lower_32_bits(substream->runtime->dma_addr);
memif->buffer_size = substream->runtime->dma_bytes;
/* start */
mtk_regmap_write(afe->regmap, memif->data->reg_ofs_base,
memif->phys_buf_addr);
/* end */
mtk_regmap_write(afe->regmap,
memif->data->reg_ofs_base + AFE_BASE_END_OFFSET,
memif->phys_buf_addr + memif->buffer_size - 1);
/* set MSB to 33-bit */
mtk_regmap_update_bits(afe->regmap, memif->data->msb_reg,
1 << memif->data->msb_shift,
msb_at_bit33 << memif->data->msb_shift);
/* set channel */
if (memif->data->mono_shift >= 0) {
unsigned int mono = (params_channels(params) == 1) ? 1 : 0;
mtk_regmap_update_bits(afe->regmap, memif->data->mono_reg,
1 << memif->data->mono_shift,
mono << memif->data->mono_shift);
}
/* set rate */
if (memif->data->fs_shift < 0)
return 0;
fs = afe->memif_fs(substream, params_rate(params));
if (fs < 0)
return -EINVAL;
mtk_regmap_update_bits(afe->regmap, memif->data->fs_reg,
memif->data->fs_maskbit << memif->data->fs_shift,
fs << memif->data->fs_shift);
return 0;
}
EXPORT_SYMBOL_GPL(mtk_afe_fe_hw_params);
int mtk_afe_fe_hw_free(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
return snd_pcm_lib_free_pages(substream);
}
EXPORT_SYMBOL_GPL(mtk_afe_fe_hw_free);
int mtk_afe_fe_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_pcm_runtime * const runtime = substream->runtime;
struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
struct mtk_base_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
struct mtk_base_afe_irq *irqs = &afe->irqs[memif->irq_usage];
const struct mtk_base_irq_data *irq_data = irqs->irq_data;
unsigned int counter = runtime->period_size;
int fs;
dev_dbg(afe->dev, "%s %s cmd=%d\n", __func__, memif->data->name, cmd);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
if (memif->data->enable_shift >= 0)
mtk_regmap_update_bits(afe->regmap,
memif->data->enable_reg,
1 << memif->data->enable_shift,
1 << memif->data->enable_shift);
/* set irq counter */
mtk_regmap_update_bits(afe->regmap, irq_data->irq_cnt_reg,
irq_data->irq_cnt_maskbit
<< irq_data->irq_cnt_shift,
counter << irq_data->irq_cnt_shift);
/* set irq fs */
fs = afe->irq_fs(substream, runtime->rate);
if (fs < 0)
return -EINVAL;
mtk_regmap_update_bits(afe->regmap, irq_data->irq_fs_reg,
irq_data->irq_fs_maskbit
<< irq_data->irq_fs_shift,
fs << irq_data->irq_fs_shift);
/* enable interrupt */
mtk_regmap_update_bits(afe->regmap, irq_data->irq_en_reg,
1 << irq_data->irq_en_shift,
1 << irq_data->irq_en_shift);
return 0;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
mtk_regmap_update_bits(afe->regmap, memif->data->enable_reg,
1 << memif->data->enable_shift, 0);
/* disable interrupt */
mtk_regmap_update_bits(afe->regmap, irq_data->irq_en_reg,
1 << irq_data->irq_en_shift,
0 << irq_data->irq_en_shift);
/* and clear pending IRQ */
mtk_regmap_write(afe->regmap, irq_data->irq_clr_reg,
1 << irq_data->irq_clr_shift);
return 0;
default:
return -EINVAL;
}
}
EXPORT_SYMBOL_GPL(mtk_afe_fe_trigger);
int mtk_afe_fe_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
struct mtk_base_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
int hd_audio = 0;
/* set hd mode */
switch (substream->runtime->format) {
case SNDRV_PCM_FORMAT_S16_LE:
hd_audio = 0;
break;
case SNDRV_PCM_FORMAT_S32_LE:
hd_audio = 1;
break;
case SNDRV_PCM_FORMAT_S24_LE:
hd_audio = 1;
break;
default:
dev_err(afe->dev, "%s() error: unsupported format %d\n",
__func__, substream->runtime->format);
break;
}
mtk_regmap_update_bits(afe->regmap, memif->data->hd_reg,
1 << memif->data->hd_shift,
hd_audio << memif->data->hd_shift);
return 0;
}
EXPORT_SYMBOL_GPL(mtk_afe_fe_prepare);
const struct snd_soc_dai_ops mtk_afe_fe_ops = {
.startup = mtk_afe_fe_startup,
.shutdown = mtk_afe_fe_shutdown,
.hw_params = mtk_afe_fe_hw_params,
.hw_free = mtk_afe_fe_hw_free,
.prepare = mtk_afe_fe_prepare,
.trigger = mtk_afe_fe_trigger,
};
EXPORT_SYMBOL_GPL(mtk_afe_fe_ops);
static DEFINE_MUTEX(irqs_lock);
int mtk_dynamic_irq_acquire(struct mtk_base_afe *afe)
{
int i;
mutex_lock(&afe->irq_alloc_lock);
for (i = 0; i < afe->irqs_size; ++i) {
if (afe->irqs[i].irq_occupyed == 0) {
afe->irqs[i].irq_occupyed = 1;
mutex_unlock(&afe->irq_alloc_lock);
return i;
}
}
mutex_unlock(&afe->irq_alloc_lock);
return afe->irqs_size;
}
EXPORT_SYMBOL_GPL(mtk_dynamic_irq_acquire);
int mtk_dynamic_irq_release(struct mtk_base_afe *afe, int irq_id)
{
mutex_lock(&afe->irq_alloc_lock);
if (irq_id >= 0 && irq_id < afe->irqs_size) {
afe->irqs[irq_id].irq_occupyed = 0;
mutex_unlock(&afe->irq_alloc_lock);
return 0;
}
mutex_unlock(&afe->irq_alloc_lock);
return -EINVAL;
}
EXPORT_SYMBOL_GPL(mtk_dynamic_irq_release);
int mtk_afe_dai_suspend(struct snd_soc_dai *dai)
{
struct mtk_base_afe *afe = dev_get_drvdata(dai->dev);
struct device *dev = afe->dev;
struct regmap *regmap = afe->regmap;
int i;
if (pm_runtime_status_suspended(dev) || afe->suspended)
return 0;
if (!afe->reg_back_up)
afe->reg_back_up =
devm_kcalloc(dev, afe->reg_back_up_list_num,
sizeof(unsigned int), GFP_KERNEL);
for (i = 0; i < afe->reg_back_up_list_num; i++)
regmap_read(regmap, afe->reg_back_up_list[i],
&afe->reg_back_up[i]);
afe->suspended = true;
afe->runtime_suspend(dev);
return 0;
}
EXPORT_SYMBOL_GPL(mtk_afe_dai_suspend);
int mtk_afe_dai_resume(struct snd_soc_dai *dai)
{
struct mtk_base_afe *afe = dev_get_drvdata(dai->dev);
struct device *dev = afe->dev;
struct regmap *regmap = afe->regmap;
int i = 0;
if (pm_runtime_status_suspended(dev) || !afe->suspended)
return 0;
afe->runtime_resume(dev);
if (!afe->reg_back_up)
dev_dbg(dev, "%s no reg_backup\n", __func__);
for (i = 0; i < afe->reg_back_up_list_num; i++)
mtk_regmap_write(regmap, afe->reg_back_up_list[i],
afe->reg_back_up[i]);
afe->suspended = false;
return 0;
}
EXPORT_SYMBOL_GPL(mtk_afe_dai_resume);
MODULE_DESCRIPTION("Mediatek simple fe dai operator");
MODULE_AUTHOR("Garlic Tseng <garlic.tseng@mediatek.com>");
MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,45 @@
/*
* mtk-afe-fe-dais.h -- Mediatek afe fe dai operator definition
*
* Copyright (c) 2016 MediaTek Inc.
* Author: Garlic Tseng <garlic.tseng@mediatek.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 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef _MTK_AFE_FE_DAI_H_
#define _MTK_AFE_FE_DAI_H_
struct snd_soc_dai_ops;
struct mtk_base_afe;
struct mtk_base_afe_memif;
int mtk_afe_fe_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai);
void mtk_afe_fe_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai);
int mtk_afe_fe_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai);
int mtk_afe_fe_hw_free(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai);
int mtk_afe_fe_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai);
int mtk_afe_fe_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai);
extern const struct snd_soc_dai_ops mtk_afe_fe_ops;
int mtk_dynamic_irq_acquire(struct mtk_base_afe *afe);
int mtk_dynamic_irq_release(struct mtk_base_afe *afe, int irq_id);
int mtk_afe_dai_suspend(struct snd_soc_dai *dai);
int mtk_afe_dai_resume(struct snd_soc_dai *dai);
#endif

View File

@ -0,0 +1,90 @@
/*
* mtk-afe-platform-driver.c -- Mediatek afe platform driver
*
* Copyright (c) 2016 MediaTek Inc.
* Author: Garlic Tseng <garlic.tseng@mediatek.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 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/module.h>
#include <linux/dma-mapping.h>
#include <sound/soc.h>
#include "mtk-afe-platform-driver.h"
#include "mtk-base-afe.h"
static snd_pcm_uframes_t mtk_afe_pcm_pointer
(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
struct mtk_base_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
const struct mtk_base_memif_data *memif_data = memif->data;
struct regmap *regmap = afe->regmap;
struct device *dev = afe->dev;
int reg_ofs_base = memif_data->reg_ofs_base;
int reg_ofs_cur = memif_data->reg_ofs_cur;
unsigned int hw_ptr = 0, hw_base = 0;
int ret, pcm_ptr_bytes;
ret = regmap_read(regmap, reg_ofs_cur, &hw_ptr);
if (ret || hw_ptr == 0) {
dev_err(dev, "%s hw_ptr err\n", __func__);
pcm_ptr_bytes = 0;
goto POINTER_RETURN_FRAMES;
}
ret = regmap_read(regmap, reg_ofs_base, &hw_base);
if (ret || hw_base == 0) {
dev_err(dev, "%s hw_ptr err\n", __func__);
pcm_ptr_bytes = 0;
goto POINTER_RETURN_FRAMES;
}
pcm_ptr_bytes = hw_ptr - hw_base;
POINTER_RETURN_FRAMES:
return bytes_to_frames(substream->runtime, pcm_ptr_bytes);
}
static const struct snd_pcm_ops mtk_afe_pcm_ops = {
.ioctl = snd_pcm_lib_ioctl,
.pointer = mtk_afe_pcm_pointer,
};
static int mtk_afe_pcm_new(struct snd_soc_pcm_runtime *rtd)
{
size_t size;
struct snd_card *card = rtd->card->snd_card;
struct snd_pcm *pcm = rtd->pcm;
struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
size = afe->mtk_afe_hardware->buffer_bytes_max;
return snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
card->dev, size, size);
}
static void mtk_afe_pcm_free(struct snd_pcm *pcm)
{
snd_pcm_lib_preallocate_free_for_all(pcm);
}
const struct snd_soc_platform_driver mtk_afe_pcm_platform = {
.ops = &mtk_afe_pcm_ops,
.pcm_new = mtk_afe_pcm_new,
.pcm_free = mtk_afe_pcm_free,
};
EXPORT_SYMBOL_GPL(mtk_afe_pcm_platform);
MODULE_DESCRIPTION("Mediatek simple platform driver");
MODULE_AUTHOR("Garlic Tseng <garlic.tseng@mediatek.com>");
MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,23 @@
/*
* mtk-afe-platform-driver.h -- Mediatek afe platform driver definition
*
* Copyright (c) 2016 MediaTek Inc.
* Author: Garlic Tseng <garlic.tseng@mediatek.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 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef _MTK_AFE_PLATFORM_DRIVER_H_
#define _MTK_AFE_PLATFORM_DRIVER_H_
extern const struct snd_soc_platform_driver mtk_afe_pcm_platform;
#endif

View File

@ -0,0 +1,104 @@
/*
* mtk-base-afe.h -- Mediatek base afe structure
*
* Copyright (c) 2016 MediaTek Inc.
* Author: Garlic Tseng <garlic.tseng@mediatek.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 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef _MTK_BASE_AFE_H_
#define _MTK_BASE_AFE_H_
struct mtk_base_memif_data {
int id;
const char *name;
int reg_ofs_base;
int reg_ofs_cur;
int fs_reg;
int fs_shift;
int fs_maskbit;
int mono_reg;
int mono_shift;
int enable_reg;
int enable_shift;
int hd_reg;
int hd_shift;
int msb_reg;
int msb_shift;
int agent_disable_reg;
int agent_disable_shift;
};
struct mtk_base_irq_data {
int id;
int irq_cnt_reg;
int irq_cnt_shift;
int irq_cnt_maskbit;
int irq_fs_reg;
int irq_fs_shift;
int irq_fs_maskbit;
int irq_en_reg;
int irq_en_shift;
int irq_clr_reg;
int irq_clr_shift;
};
struct device;
struct mtk_base_afe_memif;
struct mtk_base_afe_irq;
struct regmap;
struct snd_pcm_substream;
struct snd_soc_dai;
struct mtk_base_afe {
void __iomem *base_addr;
struct device *dev;
struct regmap *regmap;
struct mutex irq_alloc_lock; /* dynamic alloc irq lock */
unsigned int const *reg_back_up_list;
unsigned int *reg_back_up;
unsigned int reg_back_up_list_num;
int (*runtime_suspend)(struct device *dev);
int (*runtime_resume)(struct device *dev);
bool suspended;
struct mtk_base_afe_memif *memif;
int memif_size;
struct mtk_base_afe_irq *irqs;
int irqs_size;
const struct snd_pcm_hardware *mtk_afe_hardware;
int (*memif_fs)(struct snd_pcm_substream *substream,
unsigned int rate);
int (*irq_fs)(struct snd_pcm_substream *substream,
unsigned int rate);
void *platform_priv;
};
struct mtk_base_afe_memif {
unsigned int phys_buf_addr;
int buffer_size;
struct snd_pcm_substream *substream;
const struct mtk_base_memif_data *data;
int irq_usage;
int const_irq;
};
struct mtk_base_afe_irq {
const struct mtk_base_irq_data *irq_data;
int irq_occupyed;
};
#endif

View File

@ -0,0 +1,19 @@
#
# Copyright (C) 2015 MediaTek Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# platform driver
snd-soc-mt2701-afe-objs := mt2701-afe-pcm.o mt2701-afe-clock-ctrl.o
obj-$(CONFIG_SND_SOC_MT2701) += snd-soc-mt2701-afe.o
# machine driver
obj-$(CONFIG_SND_SOC_MT2701_CS42448) += mt2701-cs42448.o

View File

@ -0,0 +1,464 @@
/*
* mt2701-afe-clock-ctrl.c -- Mediatek 2701 afe clock ctrl
*
* Copyright (c) 2016 MediaTek Inc.
* Author: Garlic Tseng <garlic.tseng@mediatek.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 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <sound/soc.h>
#include <linux/regmap.h>
#include <linux/pm_runtime.h>
#include "mt2701-afe-common.h"
#include "mt2701-afe-clock-ctrl.h"
static const char *aud_clks[MT2701_CLOCK_NUM] = {
[MT2701_AUD_INFRA_SYS_AUDIO] = "infra_sys_audio_clk",
[MT2701_AUD_AUD_MUX1_SEL] = "top_audio_mux1_sel",
[MT2701_AUD_AUD_MUX2_SEL] = "top_audio_mux2_sel",
[MT2701_AUD_AUD_MUX1_DIV] = "top_audio_mux1_div",
[MT2701_AUD_AUD_MUX2_DIV] = "top_audio_mux2_div",
[MT2701_AUD_AUD_48K_TIMING] = "top_audio_48k_timing",
[MT2701_AUD_AUD_44K_TIMING] = "top_audio_44k_timing",
[MT2701_AUD_AUDPLL_MUX_SEL] = "top_audpll_mux_sel",
[MT2701_AUD_APLL_SEL] = "top_apll_sel",
[MT2701_AUD_AUD1PLL_98M] = "top_aud1_pll_98M",
[MT2701_AUD_AUD2PLL_90M] = "top_aud2_pll_90M",
[MT2701_AUD_HADDS2PLL_98M] = "top_hadds2_pll_98M",
[MT2701_AUD_HADDS2PLL_294M] = "top_hadds2_pll_294M",
[MT2701_AUD_AUDPLL] = "top_audpll",
[MT2701_AUD_AUDPLL_D4] = "top_audpll_d4",
[MT2701_AUD_AUDPLL_D8] = "top_audpll_d8",
[MT2701_AUD_AUDPLL_D16] = "top_audpll_d16",
[MT2701_AUD_AUDPLL_D24] = "top_audpll_d24",
[MT2701_AUD_AUDINTBUS] = "top_audintbus_sel",
[MT2701_AUD_CLK_26M] = "clk_26m",
[MT2701_AUD_SYSPLL1_D4] = "top_syspll1_d4",
[MT2701_AUD_AUD_K1_SRC_SEL] = "top_aud_k1_src_sel",
[MT2701_AUD_AUD_K2_SRC_SEL] = "top_aud_k2_src_sel",
[MT2701_AUD_AUD_K3_SRC_SEL] = "top_aud_k3_src_sel",
[MT2701_AUD_AUD_K4_SRC_SEL] = "top_aud_k4_src_sel",
[MT2701_AUD_AUD_K5_SRC_SEL] = "top_aud_k5_src_sel",
[MT2701_AUD_AUD_K6_SRC_SEL] = "top_aud_k6_src_sel",
[MT2701_AUD_AUD_K1_SRC_DIV] = "top_aud_k1_src_div",
[MT2701_AUD_AUD_K2_SRC_DIV] = "top_aud_k2_src_div",
[MT2701_AUD_AUD_K3_SRC_DIV] = "top_aud_k3_src_div",
[MT2701_AUD_AUD_K4_SRC_DIV] = "top_aud_k4_src_div",
[MT2701_AUD_AUD_K5_SRC_DIV] = "top_aud_k5_src_div",
[MT2701_AUD_AUD_K6_SRC_DIV] = "top_aud_k6_src_div",
[MT2701_AUD_AUD_I2S1_MCLK] = "top_aud_i2s1_mclk",
[MT2701_AUD_AUD_I2S2_MCLK] = "top_aud_i2s2_mclk",
[MT2701_AUD_AUD_I2S3_MCLK] = "top_aud_i2s3_mclk",
[MT2701_AUD_AUD_I2S4_MCLK] = "top_aud_i2s4_mclk",
[MT2701_AUD_AUD_I2S5_MCLK] = "top_aud_i2s5_mclk",
[MT2701_AUD_AUD_I2S6_MCLK] = "top_aud_i2s6_mclk",
[MT2701_AUD_ASM_M_SEL] = "top_asm_m_sel",
[MT2701_AUD_ASM_H_SEL] = "top_asm_h_sel",
[MT2701_AUD_UNIVPLL2_D4] = "top_univpll2_d4",
[MT2701_AUD_UNIVPLL2_D2] = "top_univpll2_d2",
[MT2701_AUD_SYSPLL_D5] = "top_syspll_d5",
};
int mt2701_init_clock(struct mtk_base_afe *afe)
{
struct mt2701_afe_private *afe_priv = afe->platform_priv;
int i = 0;
for (i = 0; i < MT2701_CLOCK_NUM; i++) {
afe_priv->clocks[i] = devm_clk_get(afe->dev, aud_clks[i]);
if (IS_ERR(aud_clks[i])) {
dev_warn(afe->dev, "%s devm_clk_get %s fail\n",
__func__, aud_clks[i]);
return PTR_ERR(aud_clks[i]);
}
}
return 0;
}
int mt2701_afe_enable_clock(struct mtk_base_afe *afe)
{
int ret = 0;
ret = mt2701_turn_on_a1sys_clock(afe);
if (ret) {
dev_err(afe->dev, "%s turn_on_a1sys_clock fail %d\n",
__func__, ret);
return ret;
}
ret = mt2701_turn_on_a2sys_clock(afe);
if (ret) {
dev_err(afe->dev, "%s turn_on_a2sys_clock fail %d\n",
__func__, ret);
mt2701_turn_off_a1sys_clock(afe);
return ret;
}
ret = mt2701_turn_on_afe_clock(afe);
if (ret) {
dev_err(afe->dev, "%s turn_on_afe_clock fail %d\n",
__func__, ret);
mt2701_turn_off_a1sys_clock(afe);
mt2701_turn_off_a2sys_clock(afe);
return ret;
}
regmap_update_bits(afe->regmap, ASYS_TOP_CON,
AUDIO_TOP_CON0_A1SYS_A2SYS_ON,
AUDIO_TOP_CON0_A1SYS_A2SYS_ON);
regmap_update_bits(afe->regmap, AFE_DAC_CON0,
AFE_DAC_CON0_AFE_ON,
AFE_DAC_CON0_AFE_ON);
regmap_write(afe->regmap, PWR2_TOP_CON,
PWR2_TOP_CON_INIT_VAL);
regmap_write(afe->regmap, PWR1_ASM_CON1,
PWR1_ASM_CON1_INIT_VAL);
regmap_write(afe->regmap, PWR2_ASM_CON1,
PWR2_ASM_CON1_INIT_VAL);
return 0;
}
void mt2701_afe_disable_clock(struct mtk_base_afe *afe)
{
mt2701_turn_off_afe_clock(afe);
mt2701_turn_off_a1sys_clock(afe);
mt2701_turn_off_a2sys_clock(afe);
regmap_update_bits(afe->regmap, ASYS_TOP_CON,
AUDIO_TOP_CON0_A1SYS_A2SYS_ON, 0);
regmap_update_bits(afe->regmap, AFE_DAC_CON0,
AFE_DAC_CON0_AFE_ON, 0);
}
int mt2701_turn_on_a1sys_clock(struct mtk_base_afe *afe)
{
struct mt2701_afe_private *afe_priv = afe->platform_priv;
int ret = 0;
/* Set Mux */
ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_AUD_MUX1_SEL]);
if (ret) {
dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
__func__, aud_clks[MT2701_AUD_AUD_MUX1_SEL], ret);
goto A1SYS_CLK_AUD_MUX1_SEL_ERR;
}
ret = clk_set_parent(afe_priv->clocks[MT2701_AUD_AUD_MUX1_SEL],
afe_priv->clocks[MT2701_AUD_AUD1PLL_98M]);
if (ret) {
dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n", __func__,
aud_clks[MT2701_AUD_AUD_MUX1_SEL],
aud_clks[MT2701_AUD_AUD1PLL_98M], ret);
goto A1SYS_CLK_AUD_MUX1_SEL_ERR;
}
/* Set Divider */
ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_AUD_MUX1_DIV]);
if (ret) {
dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
__func__,
aud_clks[MT2701_AUD_AUD_MUX1_DIV],
ret);
goto A1SYS_CLK_AUD_MUX1_DIV_ERR;
}
ret = clk_set_rate(afe_priv->clocks[MT2701_AUD_AUD_MUX1_DIV],
MT2701_AUD_AUD_MUX1_DIV_RATE);
if (ret) {
dev_err(afe->dev, "%s clk_set_parent %s-%d fail %d\n", __func__,
aud_clks[MT2701_AUD_AUD_MUX1_DIV],
MT2701_AUD_AUD_MUX1_DIV_RATE, ret);
goto A1SYS_CLK_AUD_MUX1_DIV_ERR;
}
/* Enable clock gate */
ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_AUD_48K_TIMING]);
if (ret) {
dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
__func__, aud_clks[MT2701_AUD_AUD_48K_TIMING], ret);
goto A1SYS_CLK_AUD_48K_ERR;
}
/* Enable infra audio */
ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]);
if (ret) {
dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
__func__, aud_clks[MT2701_AUD_INFRA_SYS_AUDIO], ret);
goto A1SYS_CLK_INFRA_ERR;
}
return 0;
A1SYS_CLK_INFRA_ERR:
clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]);
A1SYS_CLK_AUD_48K_ERR:
clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_48K_TIMING]);
A1SYS_CLK_AUD_MUX1_DIV_ERR:
clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_MUX1_DIV]);
A1SYS_CLK_AUD_MUX1_SEL_ERR:
clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_MUX1_SEL]);
return ret;
}
void mt2701_turn_off_a1sys_clock(struct mtk_base_afe *afe)
{
struct mt2701_afe_private *afe_priv = afe->platform_priv;
clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]);
clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_48K_TIMING]);
clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_MUX1_DIV]);
clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_MUX1_SEL]);
}
int mt2701_turn_on_a2sys_clock(struct mtk_base_afe *afe)
{
struct mt2701_afe_private *afe_priv = afe->platform_priv;
int ret = 0;
/* Set Mux */
ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_AUD_MUX2_SEL]);
if (ret) {
dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
__func__, aud_clks[MT2701_AUD_AUD_MUX2_SEL], ret);
goto A2SYS_CLK_AUD_MUX2_SEL_ERR;
}
ret = clk_set_parent(afe_priv->clocks[MT2701_AUD_AUD_MUX2_SEL],
afe_priv->clocks[MT2701_AUD_AUD2PLL_90M]);
if (ret) {
dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n", __func__,
aud_clks[MT2701_AUD_AUD_MUX2_SEL],
aud_clks[MT2701_AUD_AUD2PLL_90M], ret);
goto A2SYS_CLK_AUD_MUX2_SEL_ERR;
}
/* Set Divider */
ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_AUD_MUX2_DIV]);
if (ret) {
dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
__func__, aud_clks[MT2701_AUD_AUD_MUX2_DIV], ret);
goto A2SYS_CLK_AUD_MUX2_DIV_ERR;
}
ret = clk_set_rate(afe_priv->clocks[MT2701_AUD_AUD_MUX2_DIV],
MT2701_AUD_AUD_MUX2_DIV_RATE);
if (ret) {
dev_err(afe->dev, "%s clk_set_parent %s-%d fail %d\n", __func__,
aud_clks[MT2701_AUD_AUD_MUX2_DIV],
MT2701_AUD_AUD_MUX2_DIV_RATE, ret);
goto A2SYS_CLK_AUD_MUX2_DIV_ERR;
}
/* Enable clock gate */
ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_AUD_44K_TIMING]);
if (ret) {
dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
__func__, aud_clks[MT2701_AUD_AUD_44K_TIMING], ret);
goto A2SYS_CLK_AUD_44K_ERR;
}
/* Enable infra audio */
ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]);
if (ret) {
dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
__func__, aud_clks[MT2701_AUD_INFRA_SYS_AUDIO], ret);
goto A2SYS_CLK_INFRA_ERR;
}
return 0;
A2SYS_CLK_INFRA_ERR:
clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]);
A2SYS_CLK_AUD_44K_ERR:
clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_44K_TIMING]);
A2SYS_CLK_AUD_MUX2_DIV_ERR:
clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_MUX2_DIV]);
A2SYS_CLK_AUD_MUX2_SEL_ERR:
clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_MUX2_SEL]);
return ret;
}
void mt2701_turn_off_a2sys_clock(struct mtk_base_afe *afe)
{
struct mt2701_afe_private *afe_priv = afe->platform_priv;
clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]);
clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_44K_TIMING]);
clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_MUX2_DIV]);
clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_MUX2_SEL]);
}
int mt2701_turn_on_afe_clock(struct mtk_base_afe *afe)
{
struct mt2701_afe_private *afe_priv = afe->platform_priv;
int ret;
/* enable INFRA_SYS */
ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]);
if (ret) {
dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
__func__, aud_clks[MT2701_AUD_INFRA_SYS_AUDIO], ret);
goto AFE_AUD_INFRA_ERR;
}
/* Set MT2701_AUD_AUDINTBUS to MT2701_AUD_SYSPLL1_D4 */
ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_AUDINTBUS]);
if (ret) {
dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
__func__, aud_clks[MT2701_AUD_AUDINTBUS], ret);
goto AFE_AUD_AUDINTBUS_ERR;
}
ret = clk_set_parent(afe_priv->clocks[MT2701_AUD_AUDINTBUS],
afe_priv->clocks[MT2701_AUD_SYSPLL1_D4]);
if (ret) {
dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n", __func__,
aud_clks[MT2701_AUD_AUDINTBUS],
aud_clks[MT2701_AUD_SYSPLL1_D4], ret);
goto AFE_AUD_AUDINTBUS_ERR;
}
/* Set MT2701_AUD_ASM_H_SEL to MT2701_AUD_UNIVPLL2_D2 */
ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_ASM_H_SEL]);
if (ret) {
dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
__func__, aud_clks[MT2701_AUD_ASM_H_SEL], ret);
goto AFE_AUD_ASM_H_ERR;
}
ret = clk_set_parent(afe_priv->clocks[MT2701_AUD_ASM_H_SEL],
afe_priv->clocks[MT2701_AUD_UNIVPLL2_D2]);
if (ret) {
dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n", __func__,
aud_clks[MT2701_AUD_ASM_H_SEL],
aud_clks[MT2701_AUD_UNIVPLL2_D2], ret);
goto AFE_AUD_ASM_H_ERR;
}
/* Set MT2701_AUD_ASM_M_SEL to MT2701_AUD_UNIVPLL2_D4 */
ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_ASM_M_SEL]);
if (ret) {
dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
__func__, aud_clks[MT2701_AUD_ASM_M_SEL], ret);
goto AFE_AUD_ASM_M_ERR;
}
ret = clk_set_parent(afe_priv->clocks[MT2701_AUD_ASM_M_SEL],
afe_priv->clocks[MT2701_AUD_UNIVPLL2_D4]);
if (ret) {
dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n", __func__,
aud_clks[MT2701_AUD_ASM_M_SEL],
aud_clks[MT2701_AUD_UNIVPLL2_D4], ret);
goto AFE_AUD_ASM_M_ERR;
}
regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
AUDIO_TOP_CON0_PDN_AFE, 0);
regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
AUDIO_TOP_CON0_PDN_APLL_CK, 0);
regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
AUDIO_TOP_CON4_PDN_A1SYS, 0);
regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
AUDIO_TOP_CON4_PDN_A2SYS, 0);
regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
AUDIO_TOP_CON4_PDN_AFE_CONN, 0);
return 0;
AFE_AUD_ASM_M_ERR:
clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_ASM_M_SEL]);
AFE_AUD_ASM_H_ERR:
clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_ASM_H_SEL]);
AFE_AUD_AUDINTBUS_ERR:
clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUDINTBUS]);
AFE_AUD_INFRA_ERR:
clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]);
return ret;
}
void mt2701_turn_off_afe_clock(struct mtk_base_afe *afe)
{
struct mt2701_afe_private *afe_priv = afe->platform_priv;
clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]);
clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUDINTBUS]);
clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_ASM_H_SEL]);
clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_ASM_M_SEL]);
regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
AUDIO_TOP_CON0_PDN_AFE, AUDIO_TOP_CON0_PDN_AFE);
regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
AUDIO_TOP_CON0_PDN_APLL_CK,
AUDIO_TOP_CON0_PDN_APLL_CK);
regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
AUDIO_TOP_CON4_PDN_A1SYS,
AUDIO_TOP_CON4_PDN_A1SYS);
regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
AUDIO_TOP_CON4_PDN_A2SYS,
AUDIO_TOP_CON4_PDN_A2SYS);
regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
AUDIO_TOP_CON4_PDN_AFE_CONN,
AUDIO_TOP_CON4_PDN_AFE_CONN);
}
void mt2701_mclk_configuration(struct mtk_base_afe *afe, int id, int domain,
int mclk)
{
struct mt2701_afe_private *afe_priv = afe->platform_priv;
int ret;
int aud_src_div_id = MT2701_AUD_AUD_K1_SRC_DIV + id;
int aud_src_clk_id = MT2701_AUD_AUD_K1_SRC_SEL + id;
/* Set MCLK Kx_SRC_SEL(domain) */
ret = clk_prepare_enable(afe_priv->clocks[aud_src_clk_id]);
if (ret)
dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
__func__, aud_clks[aud_src_clk_id], ret);
if (domain == 0) {
ret = clk_set_parent(afe_priv->clocks[aud_src_clk_id],
afe_priv->clocks[MT2701_AUD_AUD_MUX1_SEL]);
if (ret)
dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
__func__, aud_clks[aud_src_clk_id],
aud_clks[MT2701_AUD_AUD_MUX1_SEL], ret);
} else {
ret = clk_set_parent(afe_priv->clocks[aud_src_clk_id],
afe_priv->clocks[MT2701_AUD_AUD_MUX2_SEL]);
if (ret)
dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
__func__, aud_clks[aud_src_clk_id],
aud_clks[MT2701_AUD_AUD_MUX2_SEL], ret);
}
clk_disable_unprepare(afe_priv->clocks[aud_src_clk_id]);
/* Set MCLK Kx_SRC_DIV(divider) */
ret = clk_prepare_enable(afe_priv->clocks[aud_src_div_id]);
if (ret)
dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
__func__, aud_clks[aud_src_div_id], ret);
ret = clk_set_rate(afe_priv->clocks[aud_src_div_id], mclk);
if (ret)
dev_err(afe->dev, "%s clk_set_rate %s-%d fail %d\n", __func__,
aud_clks[aud_src_div_id], mclk, ret);
clk_disable_unprepare(afe_priv->clocks[aud_src_div_id]);
}
MODULE_DESCRIPTION("MT2701 afe clock control");
MODULE_AUTHOR("Garlic Tseng <garlic.tseng@mediatek.com>");
MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,38 @@
/*
* mt2701-afe-clock-ctrl.h -- Mediatek 2701 afe clock ctrl definition
*
* Copyright (c) 2016 MediaTek Inc.
* Author: Garlic Tseng <garlic.tseng@mediatek.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 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef _MT2701_AFE_CLOCK_CTRL_H_
#define _MT2701_AFE_CLOCK_CTRL_H_
struct mtk_base_afe;
int mt2701_init_clock(struct mtk_base_afe *afe);
int mt2701_afe_enable_clock(struct mtk_base_afe *afe);
void mt2701_afe_disable_clock(struct mtk_base_afe *afe);
int mt2701_turn_on_a1sys_clock(struct mtk_base_afe *afe);
void mt2701_turn_off_a1sys_clock(struct mtk_base_afe *afe);
int mt2701_turn_on_a2sys_clock(struct mtk_base_afe *afe);
void mt2701_turn_off_a2sys_clock(struct mtk_base_afe *afe);
int mt2701_turn_on_afe_clock(struct mtk_base_afe *afe);
void mt2701_turn_off_afe_clock(struct mtk_base_afe *afe);
void mt2701_mclk_configuration(struct mtk_base_afe *afe, int id, int domain,
int mclk);
#endif

View File

@ -0,0 +1,172 @@
/*
* mt2701-afe-common.h -- Mediatek 2701 audio driver definitions
*
* Copyright (c) 2016 MediaTek Inc.
* Author: Garlic Tseng <garlic.tseng@mediatek.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 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef _MT_2701_AFE_COMMON_H_
#define _MT_2701_AFE_COMMON_H_
#include <sound/soc.h>
#include <linux/clk.h>
#include <linux/regmap.h>
#include "mt2701-reg.h"
#include "../common/mtk-base-afe.h"
#define MT2701_STREAM_DIR_NUM (SNDRV_PCM_STREAM_LAST + 1)
#define MT2701_PLL_DOMAIN_0_RATE 98304000
#define MT2701_PLL_DOMAIN_1_RATE 90316800
#define MT2701_AUD_AUD_MUX1_DIV_RATE (MT2701_PLL_DOMAIN_0_RATE / 2)
#define MT2701_AUD_AUD_MUX2_DIV_RATE (MT2701_PLL_DOMAIN_1_RATE / 2)
enum {
MT2701_I2S_1,
MT2701_I2S_2,
MT2701_I2S_3,
MT2701_I2S_4,
MT2701_I2S_NUM,
};
enum {
MT2701_MEMIF_DL1,
MT2701_MEMIF_DL2,
MT2701_MEMIF_DL3,
MT2701_MEMIF_DL4,
MT2701_MEMIF_DL5,
MT2701_MEMIF_DL_SINGLE_NUM,
MT2701_MEMIF_DLM = MT2701_MEMIF_DL_SINGLE_NUM,
MT2701_MEMIF_UL1,
MT2701_MEMIF_UL2,
MT2701_MEMIF_UL3,
MT2701_MEMIF_UL4,
MT2701_MEMIF_UL5,
MT2701_MEMIF_DLBT,
MT2701_MEMIF_ULBT,
MT2701_MEMIF_NUM,
MT2701_IO_I2S = MT2701_MEMIF_NUM,
MT2701_IO_2ND_I2S,
MT2701_IO_3RD_I2S,
MT2701_IO_4TH_I2S,
MT2701_IO_5TH_I2S,
MT2701_IO_6TH_I2S,
MT2701_IO_MRG,
};
enum {
MT2701_IRQ_ASYS_START,
MT2701_IRQ_ASYS_IRQ1 = MT2701_IRQ_ASYS_START,
MT2701_IRQ_ASYS_IRQ2,
MT2701_IRQ_ASYS_IRQ3,
MT2701_IRQ_ASYS_END,
};
/* 2701 clock def */
enum audio_system_clock_type {
MT2701_AUD_INFRA_SYS_AUDIO,
MT2701_AUD_AUD_MUX1_SEL,
MT2701_AUD_AUD_MUX2_SEL,
MT2701_AUD_AUD_MUX1_DIV,
MT2701_AUD_AUD_MUX2_DIV,
MT2701_AUD_AUD_48K_TIMING,
MT2701_AUD_AUD_44K_TIMING,
MT2701_AUD_AUDPLL_MUX_SEL,
MT2701_AUD_APLL_SEL,
MT2701_AUD_AUD1PLL_98M,
MT2701_AUD_AUD2PLL_90M,
MT2701_AUD_HADDS2PLL_98M,
MT2701_AUD_HADDS2PLL_294M,
MT2701_AUD_AUDPLL,
MT2701_AUD_AUDPLL_D4,
MT2701_AUD_AUDPLL_D8,
MT2701_AUD_AUDPLL_D16,
MT2701_AUD_AUDPLL_D24,
MT2701_AUD_AUDINTBUS,
MT2701_AUD_CLK_26M,
MT2701_AUD_SYSPLL1_D4,
MT2701_AUD_AUD_K1_SRC_SEL,
MT2701_AUD_AUD_K2_SRC_SEL,
MT2701_AUD_AUD_K3_SRC_SEL,
MT2701_AUD_AUD_K4_SRC_SEL,
MT2701_AUD_AUD_K5_SRC_SEL,
MT2701_AUD_AUD_K6_SRC_SEL,
MT2701_AUD_AUD_K1_SRC_DIV,
MT2701_AUD_AUD_K2_SRC_DIV,
MT2701_AUD_AUD_K3_SRC_DIV,
MT2701_AUD_AUD_K4_SRC_DIV,
MT2701_AUD_AUD_K5_SRC_DIV,
MT2701_AUD_AUD_K6_SRC_DIV,
MT2701_AUD_AUD_I2S1_MCLK,
MT2701_AUD_AUD_I2S2_MCLK,
MT2701_AUD_AUD_I2S3_MCLK,
MT2701_AUD_AUD_I2S4_MCLK,
MT2701_AUD_AUD_I2S5_MCLK,
MT2701_AUD_AUD_I2S6_MCLK,
MT2701_AUD_ASM_M_SEL,
MT2701_AUD_ASM_H_SEL,
MT2701_AUD_UNIVPLL2_D4,
MT2701_AUD_UNIVPLL2_D2,
MT2701_AUD_SYSPLL_D5,
MT2701_CLOCK_NUM
};
static const unsigned int mt2701_afe_backup_list[] = {
AUDIO_TOP_CON0,
AUDIO_TOP_CON4,
AUDIO_TOP_CON5,
ASYS_TOP_CON,
AFE_CONN0,
AFE_CONN1,
AFE_CONN2,
AFE_CONN3,
AFE_CONN15,
AFE_CONN16,
AFE_CONN17,
AFE_CONN18,
AFE_CONN19,
AFE_CONN20,
AFE_CONN21,
AFE_CONN22,
AFE_DAC_CON0,
AFE_MEMIF_PBUF_SIZE,
};
struct snd_pcm_substream;
struct mtk_base_irq_data;
struct mt2701_i2s_data {
int i2s_ctrl_reg;
int i2s_pwn_shift;
int i2s_asrc_fs_shift;
int i2s_asrc_fs_mask;
};
enum mt2701_i2s_dir {
I2S_OUT,
I2S_IN,
I2S_DIR_NUM,
};
struct mt2701_i2s_path {
int dai_id;
int mclk_rate;
int on[I2S_DIR_NUM];
int occupied[I2S_DIR_NUM];
const struct mt2701_i2s_data *i2s_data[2];
};
struct mt2701_afe_private {
struct clk *clocks[MT2701_CLOCK_NUM];
struct mt2701_i2s_path i2s_path[MT2701_I2S_NUM];
bool mrg_enable[MT2701_STREAM_DIR_NUM];
};
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,412 @@
/*
* mt2701-cs42448.c -- MT2701 CS42448 ALSA SoC machine driver
*
* Copyright (c) 2016 MediaTek Inc.
* Author: Ir Lian <ir.lian@mediatek.com>
* Garlic Tseng <garlic.tseng@mediatek.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 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/module.h>
#include <sound/soc.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/pinctrl/consumer.h>
#include <linux/of_gpio.h>
#include "mt2701-afe-common.h"
struct mt2701_cs42448_private {
int i2s1_in_mux;
int i2s1_in_mux_gpio_sel_1;
int i2s1_in_mux_gpio_sel_2;
};
static const char * const i2sin_mux_switch_text[] = {
"ADC_SDOUT2",
"ADC_SDOUT3",
"I2S_IN_1",
"I2S_IN_2",
};
static const struct soc_enum i2sin_mux_enum =
SOC_ENUM_SINGLE_EXT(4, i2sin_mux_switch_text);
static int mt2701_cs42448_i2sin1_mux_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
struct mt2701_cs42448_private *priv = snd_soc_card_get_drvdata(card);
ucontrol->value.integer.value[0] = priv->i2s1_in_mux;
return 0;
}
static int mt2701_cs42448_i2sin1_mux_set(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
struct mt2701_cs42448_private *priv = snd_soc_card_get_drvdata(card);
if (ucontrol->value.integer.value[0] == priv->i2s1_in_mux)
return 0;
switch (ucontrol->value.integer.value[0]) {
case 0:
gpio_set_value(priv->i2s1_in_mux_gpio_sel_1, 0);
gpio_set_value(priv->i2s1_in_mux_gpio_sel_2, 0);
break;
case 1:
gpio_set_value(priv->i2s1_in_mux_gpio_sel_1, 1);
gpio_set_value(priv->i2s1_in_mux_gpio_sel_2, 0);
break;
case 2:
gpio_set_value(priv->i2s1_in_mux_gpio_sel_1, 0);
gpio_set_value(priv->i2s1_in_mux_gpio_sel_2, 1);
break;
case 3:
gpio_set_value(priv->i2s1_in_mux_gpio_sel_1, 1);
gpio_set_value(priv->i2s1_in_mux_gpio_sel_2, 1);
break;
default:
dev_warn(card->dev, "%s invalid setting\n", __func__);
}
priv->i2s1_in_mux = ucontrol->value.integer.value[0];
return 0;
}
static const struct snd_soc_dapm_widget
mt2701_cs42448_asoc_card_dapm_widgets[] = {
SND_SOC_DAPM_LINE("Line Out Jack", NULL),
SND_SOC_DAPM_MIC("AMIC", NULL),
SND_SOC_DAPM_LINE("Tuner In", NULL),
SND_SOC_DAPM_LINE("Satellite Tuner In", NULL),
SND_SOC_DAPM_LINE("AUX In", NULL),
};
static const struct snd_kcontrol_new mt2701_cs42448_controls[] = {
SOC_DAPM_PIN_SWITCH("Line Out Jack"),
SOC_DAPM_PIN_SWITCH("AMIC"),
SOC_DAPM_PIN_SWITCH("Tuner In"),
SOC_DAPM_PIN_SWITCH("Satellite Tuner In"),
SOC_DAPM_PIN_SWITCH("AUX In"),
SOC_ENUM_EXT("I2SIN1_MUX_Switch", i2sin_mux_enum,
mt2701_cs42448_i2sin1_mux_get,
mt2701_cs42448_i2sin1_mux_set),
};
static const unsigned int mt2701_cs42448_sampling_rates[] = {48000};
static struct snd_pcm_hw_constraint_list mt2701_cs42448_constraints_rates = {
.count = ARRAY_SIZE(mt2701_cs42448_sampling_rates),
.list = mt2701_cs42448_sampling_rates,
.mask = 0,
};
static int mt2701_cs42448_fe_ops_startup(struct snd_pcm_substream *substream)
{
int err;
err = snd_pcm_hw_constraint_list(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_RATE,
&mt2701_cs42448_constraints_rates);
if (err < 0) {
dev_err(substream->pcm->card->dev,
"%s snd_pcm_hw_constraint_list failed: 0x%x\n",
__func__, err);
return err;
}
return 0;
}
static struct snd_soc_ops mt2701_cs42448_48k_fe_ops = {
.startup = mt2701_cs42448_fe_ops_startup,
};
static int mt2701_cs42448_be_ops_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;
unsigned int mclk_rate;
unsigned int rate = params_rate(params);
unsigned int div_mclk_over_bck = rate > 192000 ? 2 : 4;
unsigned int div_bck_over_lrck = 64;
mclk_rate = rate * div_bck_over_lrck * div_mclk_over_bck;
/* mt2701 mclk */
snd_soc_dai_set_sysclk(cpu_dai, 0, mclk_rate, SND_SOC_CLOCK_OUT);
/* codec mclk */
snd_soc_dai_set_sysclk(codec_dai, 0, mclk_rate, SND_SOC_CLOCK_IN);
return 0;
}
static struct snd_soc_ops mt2701_cs42448_be_ops = {
.hw_params = mt2701_cs42448_be_ops_hw_params
};
enum {
DAI_LINK_FE_MULTI_CH_OUT,
DAI_LINK_FE_PCM0_IN,
DAI_LINK_FE_PCM1_IN,
DAI_LINK_FE_BT_OUT,
DAI_LINK_FE_BT_IN,
DAI_LINK_BE_I2S0,
DAI_LINK_BE_I2S1,
DAI_LINK_BE_I2S2,
DAI_LINK_BE_I2S3,
DAI_LINK_BE_MRG_BT,
};
static struct snd_soc_dai_link mt2701_cs42448_dai_links[] = {
/* FE */
[DAI_LINK_FE_MULTI_CH_OUT] = {
.name = "mt2701-cs42448-multi-ch-out",
.stream_name = "mt2701-cs42448-multi-ch-out",
.cpu_dai_name = "PCM_multi",
.codec_name = "snd-soc-dummy",
.codec_dai_name = "snd-soc-dummy-dai",
.trigger = {SND_SOC_DPCM_TRIGGER_POST,
SND_SOC_DPCM_TRIGGER_POST},
.ops = &mt2701_cs42448_48k_fe_ops,
.dynamic = 1,
.dpcm_playback = 1,
},
[DAI_LINK_FE_PCM0_IN] = {
.name = "mt2701-cs42448-pcm0",
.stream_name = "mt2701-cs42448-pcm0-data-UL",
.cpu_dai_name = "PCM0",
.codec_name = "snd-soc-dummy",
.codec_dai_name = "snd-soc-dummy-dai",
.trigger = {SND_SOC_DPCM_TRIGGER_POST,
SND_SOC_DPCM_TRIGGER_POST},
.ops = &mt2701_cs42448_48k_fe_ops,
.dynamic = 1,
.dpcm_capture = 1,
},
[DAI_LINK_FE_PCM1_IN] = {
.name = "mt2701-cs42448-pcm1-data-UL",
.stream_name = "mt2701-cs42448-pcm1-data-UL",
.cpu_dai_name = "PCM1",
.codec_name = "snd-soc-dummy",
.codec_dai_name = "snd-soc-dummy-dai",
.trigger = {SND_SOC_DPCM_TRIGGER_POST,
SND_SOC_DPCM_TRIGGER_POST},
.ops = &mt2701_cs42448_48k_fe_ops,
.dynamic = 1,
.dpcm_capture = 1,
},
[DAI_LINK_FE_BT_OUT] = {
.name = "mt2701-cs42448-pcm-BT-out",
.stream_name = "mt2701-cs42448-pcm-BT",
.cpu_dai_name = "PCM_BT_DL",
.codec_name = "snd-soc-dummy",
.codec_dai_name = "snd-soc-dummy-dai",
.trigger = {SND_SOC_DPCM_TRIGGER_POST,
SND_SOC_DPCM_TRIGGER_POST},
.dynamic = 1,
.dpcm_playback = 1,
},
[DAI_LINK_FE_BT_IN] = {
.name = "mt2701-cs42448-pcm-BT-in",
.stream_name = "mt2701-cs42448-pcm-BT",
.cpu_dai_name = "PCM_BT_UL",
.codec_name = "snd-soc-dummy",
.codec_dai_name = "snd-soc-dummy-dai",
.trigger = {SND_SOC_DPCM_TRIGGER_POST,
SND_SOC_DPCM_TRIGGER_POST},
.dynamic = 1,
.dpcm_capture = 1,
},
/* BE */
[DAI_LINK_BE_I2S0] = {
.name = "mt2701-cs42448-I2S0",
.cpu_dai_name = "I2S0",
.no_pcm = 1,
.codec_dai_name = "cs42448",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS
| SND_SOC_DAIFMT_GATED,
.ops = &mt2701_cs42448_be_ops,
.dpcm_playback = 1,
.dpcm_capture = 1,
},
[DAI_LINK_BE_I2S1] = {
.name = "mt2701-cs42448-I2S1",
.cpu_dai_name = "I2S1",
.no_pcm = 1,
.codec_dai_name = "cs42448",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS
| SND_SOC_DAIFMT_GATED,
.ops = &mt2701_cs42448_be_ops,
.dpcm_playback = 1,
.dpcm_capture = 1,
},
[DAI_LINK_BE_I2S2] = {
.name = "mt2701-cs42448-I2S2",
.cpu_dai_name = "I2S2",
.no_pcm = 1,
.codec_dai_name = "cs42448",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS
| SND_SOC_DAIFMT_GATED,
.ops = &mt2701_cs42448_be_ops,
.dpcm_playback = 1,
.dpcm_capture = 1,
},
[DAI_LINK_BE_I2S3] = {
.name = "mt2701-cs42448-I2S3",
.cpu_dai_name = "I2S3",
.no_pcm = 1,
.codec_dai_name = "cs42448",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS
| SND_SOC_DAIFMT_GATED,
.ops = &mt2701_cs42448_be_ops,
.dpcm_playback = 1,
.dpcm_capture = 1,
},
[DAI_LINK_BE_MRG_BT] = {
.name = "mt2701-cs42448-MRG-BT",
.cpu_dai_name = "MRG BT",
.no_pcm = 1,
.codec_dai_name = "bt-sco-pcm-wb",
.dpcm_playback = 1,
.dpcm_capture = 1,
},
};
static struct snd_soc_card mt2701_cs42448_soc_card = {
.name = "mt2701-cs42448",
.owner = THIS_MODULE,
.dai_link = mt2701_cs42448_dai_links,
.num_links = ARRAY_SIZE(mt2701_cs42448_dai_links),
.controls = mt2701_cs42448_controls,
.num_controls = ARRAY_SIZE(mt2701_cs42448_controls),
.dapm_widgets = mt2701_cs42448_asoc_card_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(mt2701_cs42448_asoc_card_dapm_widgets),
};
static int mt2701_cs42448_machine_probe(struct platform_device *pdev)
{
struct snd_soc_card *card = &mt2701_cs42448_soc_card;
int ret;
int i;
struct device_node *platform_node, *codec_node, *codec_node_bt_mrg;
struct mt2701_cs42448_private *priv =
devm_kzalloc(&pdev->dev, sizeof(struct mt2701_cs42448_private),
GFP_KERNEL);
struct device *dev = &pdev->dev;
if (!priv)
return -ENOMEM;
platform_node = of_parse_phandle(pdev->dev.of_node,
"mediatek,platform", 0);
if (!platform_node) {
dev_err(&pdev->dev, "Property 'platform' missing or invalid\n");
return -EINVAL;
}
for (i = 0; i < card->num_links; i++) {
if (mt2701_cs42448_dai_links[i].platform_name)
continue;
mt2701_cs42448_dai_links[i].platform_of_node = platform_node;
}
card->dev = dev;
codec_node = of_parse_phandle(pdev->dev.of_node,
"mediatek,audio-codec", 0);
if (!codec_node) {
dev_err(&pdev->dev,
"Property 'audio-codec' missing or invalid\n");
return -EINVAL;
}
for (i = 0; i < card->num_links; i++) {
if (mt2701_cs42448_dai_links[i].codec_name)
continue;
mt2701_cs42448_dai_links[i].codec_of_node = codec_node;
}
codec_node_bt_mrg = of_parse_phandle(pdev->dev.of_node,
"mediatek,audio-codec-bt-mrg", 0);
if (!codec_node_bt_mrg) {
dev_err(&pdev->dev,
"Property 'audio-codec-bt-mrg' missing or invalid\n");
return -EINVAL;
}
mt2701_cs42448_dai_links[DAI_LINK_BE_MRG_BT].codec_of_node
= codec_node_bt_mrg;
ret = snd_soc_of_parse_audio_routing(card, "audio-routing");
if (ret) {
dev_err(&pdev->dev, "failed to parse audio-routing: %d\n", ret);
return ret;
}
priv->i2s1_in_mux_gpio_sel_1 =
of_get_named_gpio(dev->of_node, "i2s1-in-sel-gpio1", 0);
if (gpio_is_valid(priv->i2s1_in_mux_gpio_sel_1)) {
ret = devm_gpio_request(dev, priv->i2s1_in_mux_gpio_sel_1,
"i2s1_in_mux_gpio_sel_1");
if (ret)
dev_warn(&pdev->dev, "%s devm_gpio_request fail %d\n",
__func__, ret);
gpio_direction_output(priv->i2s1_in_mux_gpio_sel_1, 0);
}
priv->i2s1_in_mux_gpio_sel_2 =
of_get_named_gpio(dev->of_node, "i2s1-in-sel-gpio2", 0);
if (gpio_is_valid(priv->i2s1_in_mux_gpio_sel_2)) {
ret = devm_gpio_request(dev, priv->i2s1_in_mux_gpio_sel_2,
"i2s1_in_mux_gpio_sel_2");
if (ret)
dev_warn(&pdev->dev, "%s devm_gpio_request fail2 %d\n",
__func__, ret);
gpio_direction_output(priv->i2s1_in_mux_gpio_sel_2, 0);
}
snd_soc_card_set_drvdata(card, priv);
ret = devm_snd_soc_register_card(&pdev->dev, card);
if (ret)
dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n",
__func__, ret);
return ret;
}
#ifdef CONFIG_OF
static const struct of_device_id mt2701_cs42448_machine_dt_match[] = {
{.compatible = "mediatek,mt2701-cs42448-machine",},
{}
};
#endif
static struct platform_driver mt2701_cs42448_machine = {
.driver = {
.name = "mt2701-cs42448",
#ifdef CONFIG_OF
.of_match_table = mt2701_cs42448_machine_dt_match,
#endif
},
.probe = mt2701_cs42448_machine_probe,
};
module_platform_driver(mt2701_cs42448_machine);
/* Module information */
MODULE_DESCRIPTION("MT2701 CS42448 ALSA SoC machine driver");
MODULE_AUTHOR("Ir Lian <ir.lian@mediatek.com>");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("mt2701 cs42448 soc card");

View File

@ -0,0 +1,186 @@
/*
* mt2701-reg.h -- Mediatek 2701 audio driver reg definition
*
* Copyright (c) 2016 MediaTek Inc.
* Author: Garlic Tseng <garlic.tseng@mediatek.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 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef _MT2701_REG_H_
#define _MT2701_REG_H_
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/pm_runtime.h>
#include <sound/soc.h>
#include "mt2701-afe-common.h"
/*****************************************************************************
* R E G I S T E R D E F I N I T I O N
*****************************************************************************/
#define AUDIO_TOP_CON0 0x0000
#define AUDIO_TOP_CON4 0x0010
#define AUDIO_TOP_CON5 0x0014
#define AFE_DAIBT_CON0 0x001c
#define AFE_MRGIF_CON 0x003c
#define ASMI_TIMING_CON1 0x0100
#define ASMO_TIMING_CON1 0x0104
#define PWR1_ASM_CON1 0x0108
#define ASYS_TOP_CON 0x0600
#define ASYS_I2SIN1_CON 0x0604
#define ASYS_I2SIN2_CON 0x0608
#define ASYS_I2SIN3_CON 0x060c
#define ASYS_I2SIN4_CON 0x0610
#define ASYS_I2SIN5_CON 0x0614
#define ASYS_I2SO1_CON 0x061C
#define ASYS_I2SO2_CON 0x0620
#define ASYS_I2SO3_CON 0x0624
#define ASYS_I2SO4_CON 0x0628
#define ASYS_I2SO5_CON 0x062c
#define PWR2_TOP_CON 0x0634
#define AFE_CONN0 0x06c0
#define AFE_CONN1 0x06c4
#define AFE_CONN2 0x06c8
#define AFE_CONN3 0x06cc
#define AFE_CONN14 0x06f8
#define AFE_CONN15 0x06fc
#define AFE_CONN16 0x0700
#define AFE_CONN17 0x0704
#define AFE_CONN18 0x0708
#define AFE_CONN19 0x070c
#define AFE_CONN20 0x0710
#define AFE_CONN21 0x0714
#define AFE_CONN22 0x0718
#define AFE_CONN23 0x071c
#define AFE_CONN24 0x0720
#define AFE_CONN41 0x0764
#define ASYS_IRQ1_CON 0x0780
#define ASYS_IRQ2_CON 0x0784
#define ASYS_IRQ3_CON 0x0788
#define ASYS_IRQ_CLR 0x07c0
#define ASYS_IRQ_STATUS 0x07c4
#define PWR2_ASM_CON1 0x1070
#define AFE_DAC_CON0 0x1200
#define AFE_DAC_CON1 0x1204
#define AFE_DAC_CON2 0x1208
#define AFE_DAC_CON3 0x120c
#define AFE_DAC_CON4 0x1210
#define AFE_MEMIF_HD_CON1 0x121c
#define AFE_MEMIF_PBUF_SIZE 0x1238
#define AFE_MEMIF_HD_CON0 0x123c
#define AFE_DL1_BASE 0x1240
#define AFE_DL1_CUR 0x1244
#define AFE_DL2_BASE 0x1250
#define AFE_DL2_CUR 0x1254
#define AFE_DL3_BASE 0x1260
#define AFE_DL3_CUR 0x1264
#define AFE_DL4_BASE 0x1270
#define AFE_DL4_CUR 0x1274
#define AFE_DL5_BASE 0x1280
#define AFE_DL5_CUR 0x1284
#define AFE_DLMCH_BASE 0x12a0
#define AFE_DLMCH_CUR 0x12a4
#define AFE_ARB1_BASE 0x12b0
#define AFE_ARB1_CUR 0x12b4
#define AFE_VUL_BASE 0x1300
#define AFE_VUL_CUR 0x130c
#define AFE_UL2_BASE 0x1310
#define AFE_UL2_END 0x1318
#define AFE_UL2_CUR 0x131c
#define AFE_UL3_BASE 0x1320
#define AFE_UL3_END 0x1328
#define AFE_UL3_CUR 0x132c
#define AFE_UL4_BASE 0x1330
#define AFE_UL4_END 0x1338
#define AFE_UL4_CUR 0x133c
#define AFE_UL5_BASE 0x1340
#define AFE_UL5_END 0x1348
#define AFE_UL5_CUR 0x134c
#define AFE_DAI_BASE 0x1370
#define AFE_DAI_CUR 0x137c
/* AUDIO_TOP_CON0 (0x0000) */
#define AUDIO_TOP_CON0_A1SYS_A2SYS_ON (0x3 << 0)
#define AUDIO_TOP_CON0_PDN_AFE (0x1 << 2)
#define AUDIO_TOP_CON0_PDN_APLL_CK (0x1 << 23)
/* AUDIO_TOP_CON4 (0x0010) */
#define AUDIO_TOP_CON4_I2SO1_PWN (0x1 << 6)
#define AUDIO_TOP_CON4_PDN_A1SYS (0x1 << 21)
#define AUDIO_TOP_CON4_PDN_A2SYS (0x1 << 22)
#define AUDIO_TOP_CON4_PDN_AFE_CONN (0x1 << 23)
#define AUDIO_TOP_CON4_PDN_MRGIF (0x1 << 25)
/* AFE_DAIBT_CON0 (0x001c) */
#define AFE_DAIBT_CON0_DAIBT_EN (0x1 << 0)
#define AFE_DAIBT_CON0_BT_FUNC_EN (0x1 << 1)
#define AFE_DAIBT_CON0_BT_FUNC_RDY (0x1 << 3)
#define AFE_DAIBT_CON0_BT_WIDE_MODE_EN (0x1 << 9)
#define AFE_DAIBT_CON0_MRG_USE (0x1 << 12)
/* PWR1_ASM_CON1 (0x0108) */
#define PWR1_ASM_CON1_INIT_VAL (0x492)
/* AFE_MRGIF_CON (0x003c) */
#define AFE_MRGIF_CON_MRG_EN (0x1 << 0)
#define AFE_MRGIF_CON_MRG_I2S_EN (0x1 << 16)
#define AFE_MRGIF_CON_I2S_MODE_MASK (0xf << 20)
#define AFE_MRGIF_CON_I2S_MODE_32K (0x4 << 20)
/* ASYS_I2SO1_CON (0x061c) */
#define ASYS_I2SO1_CON_FS (0x1f << 8)
#define ASYS_I2SO1_CON_FS_SET(x) ((x) << 8)
#define ASYS_I2SO1_CON_MULTI_CH (0x1 << 16)
#define ASYS_I2SO1_CON_SIDEGEN (0x1 << 30)
#define ASYS_I2SO1_CON_I2S_EN (0x1 << 0)
/* 0:EIAJ 1:I2S */
#define ASYS_I2SO1_CON_I2S_MODE (0x1 << 3)
#define ASYS_I2SO1_CON_WIDE_MODE (0x1 << 1)
#define ASYS_I2SO1_CON_WIDE_MODE_SET(x) ((x) << 1)
/* PWR2_TOP_CON (0x0634) */
#define PWR2_TOP_CON_INIT_VAL (0xffe1ffff)
/* ASYS_IRQ_CLR (0x07c0) */
#define ASYS_IRQ_CLR_ALL (0xffffffff)
/* PWR2_ASM_CON1 (0x1070) */
#define PWR2_ASM_CON1_INIT_VAL (0x492492)
/* AFE_DAC_CON0 (0x1200) */
#define AFE_DAC_CON0_AFE_ON (0x1 << 0)
/* AFE_MEMIF_PBUF_SIZE (0x1238) */
#define AFE_MEMIF_PBUF_SIZE_DLM_MASK (0x1 << 29)
#define AFE_MEMIF_PBUF_SIZE_PAIR_INTERLEAVE (0x0 << 29)
#define AFE_MEMIF_PBUF_SIZE_FULL_INTERLEAVE (0x1 << 29)
#define DLMCH_BIT_WIDTH_MASK (0x1 << 28)
#define AFE_MEMIF_PBUF_SIZE_DLM_CH_MASK (0xf << 24)
#define AFE_MEMIF_PBUF_SIZE_DLM_CH(x) ((x) << 24)
#define AFE_MEMIF_PBUF_SIZE_DLM_BYTE_MASK (0x3 << 12)
#define AFE_MEMIF_PBUF_SIZE_DLM_32BYTES (0x1 << 12)
/* I2S in/out register bit control */
#define ASYS_I2S_CON_FS (0x1f << 8)
#define ASYS_I2S_CON_FS_SET(x) ((x) << 8)
#define ASYS_I2S_CON_RESET (0x1 << 30)
#define ASYS_I2S_CON_I2S_EN (0x1 << 0)
#define ASYS_I2S_CON_I2S_COUPLE_MODE (0x1 << 17)
/* 0:EIAJ 1:I2S */
#define ASYS_I2S_CON_I2S_MODE (0x1 << 3)
#define ASYS_I2S_CON_WIDE_MODE (0x1 << 1)
#define ASYS_I2S_CON_WIDE_MODE_SET(x) ((x) << 1)
#define ASYS_I2S_IN_PHASE_FIX (0x1 << 31)
#define AFE_END_ADDR 0x15e0
#endif

View File

@ -0,0 +1,7 @@
# MTK Platform Support
obj-$(CONFIG_SND_SOC_MT8173) += mt8173-afe-pcm.o
# Machine support
obj-$(CONFIG_SND_SOC_MT8173_MAX98090) += mt8173-max98090.o
obj-$(CONFIG_SND_SOC_MT8173_RT5650) += mt8173-rt5650.o
obj-$(CONFIG_SND_SOC_MT8173_RT5650_RT5514) += mt8173-rt5650-rt5514.o
obj-$(CONFIG_SND_SOC_MT8173_RT5650_RT5676) += mt8173-rt5650-rt5676.o

View File

@ -0,0 +1,73 @@
/*
* mt8173_afe_common.h -- Mediatek 8173 audio driver common definitions
*
* Copyright (c) 2015 MediaTek Inc.
* Author: Koro Chen <koro.chen@mediatek.com>
* Sascha Hauer <s.hauer@pengutronix.de>
* Hidalgo Huang <hidalgo.huang@mediatek.com>
* Ir Lian <ir.lian@mediatek.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 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef _MT8173_AFE_COMMON_H_
#define _MT8173_AFE_COMMON_H_
#include <linux/clk.h>
#include <linux/regmap.h>
enum {
MT8173_AFE_MEMIF_DL1,
MT8173_AFE_MEMIF_DL2,
MT8173_AFE_MEMIF_VUL,
MT8173_AFE_MEMIF_DAI,
MT8173_AFE_MEMIF_AWB,
MT8173_AFE_MEMIF_MOD_DAI,
MT8173_AFE_MEMIF_HDMI,
MT8173_AFE_MEMIF_NUM,
MT8173_AFE_IO_MOD_PCM1 = MT8173_AFE_MEMIF_NUM,
MT8173_AFE_IO_MOD_PCM2,
MT8173_AFE_IO_PMIC,
MT8173_AFE_IO_I2S,
MT8173_AFE_IO_2ND_I2S,
MT8173_AFE_IO_HW_GAIN1,
MT8173_AFE_IO_HW_GAIN2,
MT8173_AFE_IO_MRG_O,
MT8173_AFE_IO_MRG_I,
MT8173_AFE_IO_DAIBT,
MT8173_AFE_IO_HDMI,
};
enum {
MT8173_AFE_IRQ_DL1,
MT8173_AFE_IRQ_DL2,
MT8173_AFE_IRQ_VUL,
MT8173_AFE_IRQ_DAI,
MT8173_AFE_IRQ_AWB,
MT8173_AFE_IRQ_MOD_DAI,
MT8173_AFE_IRQ_HDMI,
MT8173_AFE_IRQ_NUM,
};
enum {
MT8173_CLK_INFRASYS_AUD,
MT8173_CLK_TOP_PDN_AUD,
MT8173_CLK_TOP_PDN_AUD_BUS,
MT8173_CLK_I2S0_M,
MT8173_CLK_I2S1_M,
MT8173_CLK_I2S2_M,
MT8173_CLK_I2S3_M,
MT8173_CLK_I2S3_B,
MT8173_CLK_BCK0,
MT8173_CLK_BCK1,
MT8173_CLK_NUM
};
#endif

View File

@ -18,7 +18,7 @@
#include <sound/soc.h>
#include <sound/jack.h>
#include <linux/gpio.h>
#include "../codecs/max98090.h"
#include "../../codecs/max98090.h"
static struct snd_soc_jack mt8173_max98090_jack;

View File

@ -19,7 +19,7 @@
#include <linux/of_gpio.h>
#include <sound/soc.h>
#include <sound/jack.h>
#include "../codecs/rt5645.h"
#include "../../codecs/rt5645.h"
#define MCLK_FOR_CODECS 12288000

View File

@ -19,8 +19,8 @@
#include <linux/of_gpio.h>
#include <sound/soc.h>
#include <sound/jack.h>
#include "../codecs/rt5645.h"
#include "../codecs/rt5677.h"
#include "../../codecs/rt5645.h"
#include "../../codecs/rt5677.h"
#define MCLK_FOR_CODECS 12288000

View File

@ -19,10 +19,24 @@
#include <linux/of_gpio.h>
#include <sound/soc.h>
#include <sound/jack.h>
#include "../codecs/rt5645.h"
#include "../../codecs/rt5645.h"
#define MCLK_FOR_CODECS 12288000
enum mt8173_rt5650_mclk {
MT8173_RT5650_MCLK_EXTERNAL = 0,
MT8173_RT5650_MCLK_INTERNAL,
};
struct mt8173_rt5650_platform_data {
enum mt8173_rt5650_mclk pll_from;
/* 0 = external oscillator; 1 = internal source from mt8173 */
};
static struct mt8173_rt5650_platform_data mt8173_rt5650_priv = {
.pll_from = MT8173_RT5650_MCLK_EXTERNAL,
};
static const struct snd_soc_dapm_widget mt8173_rt5650_widgets[] = {
SND_SOC_DAPM_SPK("Speaker", NULL),
SND_SOC_DAPM_MIC("Int Mic", NULL),
@ -54,13 +68,29 @@ static int mt8173_rt5650_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
unsigned int mclk_clock;
int i, ret;
switch (mt8173_rt5650_priv.pll_from) {
case MT8173_RT5650_MCLK_EXTERNAL:
/* mclk = 12.288M */
mclk_clock = MCLK_FOR_CODECS;
break;
case MT8173_RT5650_MCLK_INTERNAL:
/* mclk = sampling rate*256 */
mclk_clock = params_rate(params) * 256;
break;
default:
/* mclk = 12.288M */
mclk_clock = MCLK_FOR_CODECS;
break;
}
for (i = 0; i < rtd->num_codecs; i++) {
struct snd_soc_dai *codec_dai = rtd->codec_dais[i];
/* pll from mclk 12.288M */
ret = snd_soc_dai_set_pll(codec_dai, 0, 0, MCLK_FOR_CODECS,
/* pll from mclk */
ret = snd_soc_dai_set_pll(codec_dai, 0, 0, mclk_clock,
params_rate(params) * 512);
if (ret)
return ret;
@ -139,7 +169,9 @@ static struct snd_soc_dai_link_component mt8173_rt5650_codecs[] = {
enum {
DAI_LINK_PLAYBACK,
DAI_LINK_CAPTURE,
DAI_LINK_HDMI,
DAI_LINK_CODEC_I2S,
DAI_LINK_HDMI_I2S,
};
/* Digital audio interface glue - connects codec <---> CPU */
@ -165,6 +197,16 @@ static struct snd_soc_dai_link mt8173_rt5650_dais[] = {
.dynamic = 1,
.dpcm_capture = 1,
},
[DAI_LINK_HDMI] = {
.name = "HDMI",
.stream_name = "HDMI PCM",
.cpu_dai_name = "HDMI",
.codec_name = "snd-soc-dummy",
.codec_dai_name = "snd-soc-dummy-dai",
.trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
.dynamic = 1,
.dpcm_playback = 1,
},
/* Back End DAI links */
[DAI_LINK_CODEC_I2S] = {
.name = "Codec",
@ -180,6 +222,13 @@ static struct snd_soc_dai_link mt8173_rt5650_dais[] = {
.dpcm_playback = 1,
.dpcm_capture = 1,
},
[DAI_LINK_HDMI_I2S] = {
.name = "HDMI BE",
.cpu_dai_name = "HDMIO",
.no_pcm = 1,
.codec_dai_name = "i2s-hifi",
.dpcm_playback = 1,
},
};
static struct snd_soc_card mt8173_rt5650_card = {
@ -243,6 +292,24 @@ static int mt8173_rt5650_dev_probe(struct platform_device *pdev)
mt8173_rt5650_codecs[1].dai_name = codec_capture_dai;
}
if (device_property_present(&pdev->dev, "mediatek,mclk")) {
ret = device_property_read_u32(&pdev->dev,
"mediatek,mclk",
&mt8173_rt5650_priv.pll_from);
if (ret) {
dev_err(&pdev->dev,
"%s snd_soc_register_card fail %d\n",
__func__, ret);
}
}
mt8173_rt5650_dais[DAI_LINK_HDMI_I2S].codec_of_node =
of_parse_phandle(pdev->dev.of_node, "mediatek,audio-codec", 1);
if (!mt8173_rt5650_dais[DAI_LINK_HDMI_I2S].codec_of_node) {
dev_err(&pdev->dev,
"Property 'audio-codec' missing or invalid\n");
return -EINVAL;
}
card->dev = &pdev->dev;
platform_set_drvdata(pdev, card);

View File

@ -1,101 +0,0 @@
/*
* mtk_afe_common.h -- Mediatek audio driver common definitions
*
* Copyright (c) 2015 MediaTek Inc.
* Author: Koro Chen <koro.chen@mediatek.com>
* Sascha Hauer <s.hauer@pengutronix.de>
* Hidalgo Huang <hidalgo.huang@mediatek.com>
* Ir Lian <ir.lian@mediatek.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 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef _MTK_AFE_COMMON_H_
#define _MTK_AFE_COMMON_H_
#include <linux/clk.h>
#include <linux/regmap.h>
enum {
MTK_AFE_MEMIF_DL1,
MTK_AFE_MEMIF_DL2,
MTK_AFE_MEMIF_VUL,
MTK_AFE_MEMIF_DAI,
MTK_AFE_MEMIF_AWB,
MTK_AFE_MEMIF_MOD_DAI,
MTK_AFE_MEMIF_HDMI,
MTK_AFE_MEMIF_NUM,
MTK_AFE_IO_MOD_PCM1 = MTK_AFE_MEMIF_NUM,
MTK_AFE_IO_MOD_PCM2,
MTK_AFE_IO_PMIC,
MTK_AFE_IO_I2S,
MTK_AFE_IO_2ND_I2S,
MTK_AFE_IO_HW_GAIN1,
MTK_AFE_IO_HW_GAIN2,
MTK_AFE_IO_MRG_O,
MTK_AFE_IO_MRG_I,
MTK_AFE_IO_DAIBT,
MTK_AFE_IO_HDMI,
};
enum {
MTK_AFE_IRQ_1,
MTK_AFE_IRQ_2,
MTK_AFE_IRQ_3,
MTK_AFE_IRQ_4,
MTK_AFE_IRQ_5,
MTK_AFE_IRQ_6,
MTK_AFE_IRQ_7,
MTK_AFE_IRQ_8,
MTK_AFE_IRQ_NUM,
};
enum {
MTK_CLK_INFRASYS_AUD,
MTK_CLK_TOP_PDN_AUD,
MTK_CLK_TOP_PDN_AUD_BUS,
MTK_CLK_I2S0_M,
MTK_CLK_I2S1_M,
MTK_CLK_I2S2_M,
MTK_CLK_I2S3_M,
MTK_CLK_I2S3_B,
MTK_CLK_BCK0,
MTK_CLK_BCK1,
MTK_CLK_NUM
};
struct mtk_afe;
struct snd_pcm_substream;
struct mtk_afe_memif_data {
int id;
const char *name;
int reg_ofs_base;
int reg_ofs_cur;
int fs_shift;
int mono_shift;
int enable_shift;
int irq_reg_cnt;
int irq_cnt_shift;
int irq_en_shift;
int irq_fs_shift;
int irq_clr_shift;
int msb_shift;
};
struct mtk_afe_memif {
unsigned int phys_buf_addr;
int buffer_size;
struct snd_pcm_substream *substream;
const struct mtk_afe_memif_data *data;
const struct mtk_afe_irq_data *irqdata;
};
#endif