summaryrefslogtreecommitdiff
path: root/sound/soc/sunxi/sun4i-codec.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/sunxi/sun4i-codec.c')
-rw-r--r--sound/soc/sunxi/sun4i-codec.c466
1 files changed, 428 insertions, 38 deletions
diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c
index 933a0913237c..93733ff2e32a 100644
--- a/sound/soc/sunxi/sun4i-codec.c
+++ b/sound/soc/sunxi/sun4i-codec.c
@@ -5,6 +5,7 @@
* Copyright 2015 Maxime Ripard <maxime.ripard@free-electrons.com>
* Copyright 2015 Adam Sampson <ats@offog.org>
* Copyright 2016 Chen-Yu Tsai <wens@csie.org>
+ * Copyright 2018 Mesih Kilinc <mesihkilinc@gmail.com>
*
* Based on the Allwinner SDK driver, released under the GPL.
*/
@@ -21,6 +22,7 @@
#include <linux/gpio/consumer.h>
#include <sound/core.h>
+#include <sound/jack.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
@@ -265,6 +267,64 @@
/* TODO H3 DAP (Digital Audio Processing) bits */
+#define SUN4I_DMA_MAX_BURST (8)
+
+/* suniv specific registers */
+
+#define SUNIV_DMA_MAX_BURST (4)
+
+/* Codec DAC digital controls and FIFO registers */
+#define SUNIV_CODEC_ADC_FIFOC (0x10)
+#define SUNIV_CODEC_ADC_FIFOC_EN_AD (28)
+#define SUNIV_CODEC_ADC_FIFOS (0x14)
+#define SUNIV_CODEC_ADC_RXDATA (0x18)
+
+/* Output mixer and gain controls */
+#define SUNIV_CODEC_OM_DACA_CTRL (0x20)
+#define SUNIV_CODEC_OM_DACA_CTRL_DACAREN (31)
+#define SUNIV_CODEC_OM_DACA_CTRL_DACALEN (30)
+#define SUNIV_CODEC_OM_DACA_CTRL_RMIXEN (29)
+#define SUNIV_CODEC_OM_DACA_CTRL_LMIXEN (28)
+#define SUNIV_CODEC_OM_DACA_CTRL_RHPPAMUTE (27)
+#define SUNIV_CODEC_OM_DACA_CTRL_LHPPAMUTE (26)
+#define SUNIV_CODEC_OM_DACA_CTRL_RHPIS (25)
+#define SUNIV_CODEC_OM_DACA_CTRL_LHPIS (24)
+#define SUNIV_CODEC_OM_DACA_CTRL_HPCOM_CTL (22)
+#define SUNIV_CODEC_OM_DACA_CTRL_COMPTEN (21)
+#define SUNIV_CODEC_OM_DACA_CTRL_RMIXMUTE_MICIN (20)
+#define SUNIV_CODEC_OM_DACA_CTRL_RMIXMUTE_LINEIN (19)
+#define SUNIV_CODEC_OM_DACA_CTRL_RMIXMUTE_FMIN (18)
+#define SUNIV_CODEC_OM_DACA_CTRL_RMIXMUTE_RDAC (17)
+#define SUNIV_CODEC_OM_DACA_CTRL_RMIXMUTE_LDAC (16)
+#define SUNIV_CODEC_OM_DACA_CTRL_HPPAEN (15)
+#define SUNIV_CODEC_OM_DACA_CTRL_LMIXMUTE_MICIN (12)
+#define SUNIV_CODEC_OM_DACA_CTRL_LMIXMUTE_LINEIN (11)
+#define SUNIV_CODEC_OM_DACA_CTRL_LMIXMUTE_FMIN (10)
+#define SUNIV_CODEC_OM_DACA_CTRL_LMIXMUTE_LDAC (9)
+#define SUNIV_CODEC_OM_DACA_CTRL_LMIXMUTE_RDAC (8)
+#define SUNIV_CODEC_OM_DACA_CTRL_LTLNMUTE (7)
+#define SUNIV_CODEC_OM_DACA_CTRL_RTLNMUTE (6)
+#define SUNIV_CODEC_OM_DACA_CTRL_HPVOL (0)
+
+/* Analog Input Mixer controls */
+#define SUNIV_CODEC_ADC_ACTL (0x24)
+#define SUNIV_CODEC_ADC_ADCEN (31)
+#define SUNIV_CODEC_ADC_MICG (24)
+#define SUNIV_CODEC_ADC_LINEINVOL (21)
+#define SUNIV_CODEC_ADC_ADCG (16)
+#define SUNIV_CODEC_ADC_ADCMIX_MIC (13)
+#define SUNIV_CODEC_ADC_ADCMIX_FMINL (12)
+#define SUNIV_CODEC_ADC_ADCMIX_FMINR (11)
+#define SUNIV_CODEC_ADC_ADCMIX_LINEIN (10)
+#define SUNIV_CODEC_ADC_ADCMIX_LOUT (9)
+#define SUNIV_CODEC_ADC_ADCMIX_ROUT (8)
+#define SUNIV_CODEC_ADC_PASPEEDSELECT (7)
+#define SUNIV_CODEC_ADC_FMINVOL (4)
+#define SUNIV_CODEC_ADC_MICAMPEN (3)
+#define SUNIV_CODEC_ADC_MICBOOST (0)
+
+#define SUNIV_CODEC_ADC_DBG (0x4c)
+
struct sun4i_codec {
struct device *dev;
struct regmap *regmap;
@@ -272,6 +332,7 @@ struct sun4i_codec {
struct clk *clk_module;
struct reset_control *rst;
struct gpio_desc *gpio_pa;
+ struct gpio_desc *gpio_hp;
/* ADC_FIFOC register is at different offset on different SoCs */
struct regmap_field *reg_adc_fifoc;
@@ -1255,6 +1316,228 @@ static const struct snd_soc_component_driver sun8i_a23_codec_codec = {
.endianness = 1,
};
+/*suniv F1C100s codec */
+
+/* headphone controls */
+static const char * const suniv_codec_hp_src_enum_text[] = {
+ "DAC", "Mixer",
+};
+
+static SOC_ENUM_DOUBLE_DECL(suniv_codec_hp_src_enum,
+ SUNIV_CODEC_OM_DACA_CTRL,
+ SUNIV_CODEC_OM_DACA_CTRL_LHPIS,
+ SUNIV_CODEC_OM_DACA_CTRL_RHPIS,
+ suniv_codec_hp_src_enum_text);
+
+static const struct snd_kcontrol_new suniv_codec_hp_src[] = {
+ SOC_DAPM_ENUM("Headphone Source Playback Route",
+ suniv_codec_hp_src_enum),
+};
+
+/* mixer controls */
+static const struct snd_kcontrol_new suniv_codec_adc_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Right Out Capture Switch", SUNIV_CODEC_ADC_ACTL,
+ SUNIV_CODEC_ADC_ADCMIX_ROUT, 1, 0),
+ SOC_DAPM_SINGLE("Left Out Capture Switch", SUNIV_CODEC_ADC_ACTL,
+ SUNIV_CODEC_ADC_ADCMIX_LOUT, 1, 0),
+ SOC_DAPM_SINGLE("Line In Capture Switch", SUNIV_CODEC_ADC_ACTL,
+ SUNIV_CODEC_ADC_ADCMIX_LINEIN, 1, 0),
+ SOC_DAPM_SINGLE("Right FM In Capture Switch", SUNIV_CODEC_ADC_ACTL,
+ SUNIV_CODEC_ADC_ADCMIX_FMINR, 1, 0),
+ SOC_DAPM_SINGLE("Left FM In Capture Switch", SUNIV_CODEC_ADC_ACTL,
+ SUNIV_CODEC_ADC_ADCMIX_FMINL, 1, 0),
+ SOC_DAPM_SINGLE("Mic Capture Switch", SUNIV_CODEC_ADC_ACTL,
+ SUNIV_CODEC_ADC_ADCMIX_MIC, 1, 0),
+};
+
+static const struct snd_kcontrol_new suniv_codec_dac_lmixer_controls[] = {
+ SOC_DAPM_SINGLE("Right DAC Playback Switch", SUNIV_CODEC_OM_DACA_CTRL,
+ SUNIV_CODEC_OM_DACA_CTRL_LMIXMUTE_RDAC, 1, 0),
+ SOC_DAPM_SINGLE("Left DAC Playback Switch", SUNIV_CODEC_OM_DACA_CTRL,
+ SUNIV_CODEC_OM_DACA_CTRL_LMIXMUTE_LDAC, 1, 0),
+ SOC_DAPM_SINGLE("FM In Playback Switch", SUNIV_CODEC_OM_DACA_CTRL,
+ SUNIV_CODEC_OM_DACA_CTRL_LMIXMUTE_FMIN, 1, 0),
+ SOC_DAPM_SINGLE("Line In Playback Switch", SUNIV_CODEC_OM_DACA_CTRL,
+ SUNIV_CODEC_OM_DACA_CTRL_LMIXMUTE_LINEIN, 1, 0),
+ SOC_DAPM_SINGLE("Mic In Playback Switch", SUNIV_CODEC_OM_DACA_CTRL,
+ SUNIV_CODEC_OM_DACA_CTRL_LMIXMUTE_MICIN, 1, 0),
+};
+
+static const struct snd_kcontrol_new suniv_codec_dac_rmixer_controls[] = {
+ SOC_DAPM_SINGLE("Left DAC Playback Switch", SUNIV_CODEC_OM_DACA_CTRL,
+ SUNIV_CODEC_OM_DACA_CTRL_RMIXMUTE_LDAC, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC Playback Switch", SUNIV_CODEC_OM_DACA_CTRL,
+ SUNIV_CODEC_OM_DACA_CTRL_RMIXMUTE_RDAC, 1, 0),
+ SOC_DAPM_SINGLE("FM In Playback Switch", SUNIV_CODEC_OM_DACA_CTRL,
+ SUNIV_CODEC_OM_DACA_CTRL_RMIXMUTE_FMIN, 1, 0),
+ SOC_DAPM_SINGLE("Line In Playback Switch", SUNIV_CODEC_OM_DACA_CTRL,
+ SUNIV_CODEC_OM_DACA_CTRL_RMIXMUTE_LINEIN, 1, 0),
+ SOC_DAPM_SINGLE("Mic In Playback Switch", SUNIV_CODEC_OM_DACA_CTRL,
+ SUNIV_CODEC_OM_DACA_CTRL_RMIXMUTE_MICIN, 1, 0),
+};
+
+static const DECLARE_TLV_DB_SCALE(suniv_codec_dvol_scale, -7308, 116, 0);
+static const DECLARE_TLV_DB_SCALE(suniv_codec_hp_vol_scale, -6300, 100, 1);
+static const DECLARE_TLV_DB_SCALE(suniv_codec_out_mixer_pregain_scale,
+ -450, 150, 0);
+
+static const DECLARE_TLV_DB_RANGE(suniv_codec_mic_gain_scale,
+ 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
+ 1, 7, TLV_DB_SCALE_ITEM(2400, 300, 0),
+);
+
+static const struct snd_kcontrol_new suniv_codec_codec_widgets[] = {
+ SOC_SINGLE_TLV("DAC Playback Volume", SUN4I_CODEC_DAC_DPC,
+ SUN4I_CODEC_DAC_DPC_DVOL, 0x3f, 1,
+ suniv_codec_dvol_scale),
+ SOC_SINGLE_TLV("Headphone Playback Volume",
+ SUNIV_CODEC_OM_DACA_CTRL,
+ SUNIV_CODEC_OM_DACA_CTRL_HPVOL, 0x3f, 0,
+ suniv_codec_hp_vol_scale),
+ SOC_DOUBLE("Headphone Playback Switch",
+ SUNIV_CODEC_OM_DACA_CTRL,
+ SUNIV_CODEC_OM_DACA_CTRL_LHPPAMUTE,
+ SUNIV_CODEC_OM_DACA_CTRL_RHPPAMUTE, 1, 0),
+ SOC_SINGLE_TLV("Line In Playback Volume",
+ SUNIV_CODEC_ADC_ACTL, SUNIV_CODEC_ADC_LINEINVOL,
+ 0x7, 0, suniv_codec_out_mixer_pregain_scale),
+ SOC_SINGLE_TLV("FM In Playback Volume",
+ SUNIV_CODEC_ADC_ACTL, SUNIV_CODEC_ADC_FMINVOL,
+ 0x7, 0, suniv_codec_out_mixer_pregain_scale),
+ SOC_SINGLE_TLV("Mic In Playback Volume",
+ SUNIV_CODEC_ADC_ACTL, SUNIV_CODEC_ADC_MICG,
+ 0x7, 0, suniv_codec_out_mixer_pregain_scale),
+
+ /* Microphone Amp boost gains */
+ SOC_SINGLE_TLV("Mic Boost Volume", SUNIV_CODEC_ADC_ACTL,
+ SUNIV_CODEC_ADC_MICBOOST, 0x7, 0,
+ suniv_codec_mic_gain_scale),
+ SOC_SINGLE_TLV("ADC Capture Volume",
+ SUNIV_CODEC_ADC_ACTL, SUNIV_CODEC_ADC_ADCG,
+ 0x7, 0, suniv_codec_out_mixer_pregain_scale),
+};
+
+static const struct snd_soc_dapm_widget suniv_codec_codec_dapm_widgets[] = {
+ /* Microphone inputs */
+ SND_SOC_DAPM_INPUT("MIC"),
+
+ /* Microphone Bias */
+ /* deleted: HBIAS, MBIAS */
+
+ /* Mic input path */
+ SND_SOC_DAPM_PGA("Mic Amplifier", SUNIV_CODEC_ADC_ACTL,
+ SUNIV_CODEC_ADC_MICAMPEN, 0, NULL, 0),
+
+ /* Line In */
+ SND_SOC_DAPM_INPUT("LINEIN"),
+
+ /* FM In */
+ SND_SOC_DAPM_INPUT("FMINR"),
+ SND_SOC_DAPM_INPUT("FMINL"),
+
+ /* Digital parts of the ADCs */
+ SND_SOC_DAPM_SUPPLY("ADC Enable", SUNIV_CODEC_ADC_FIFOC,
+ SUNIV_CODEC_ADC_FIFOC_EN_AD, 0,
+ NULL, 0),
+
+ /* Analog parts of the ADCs */
+ SND_SOC_DAPM_ADC("ADC", "Codec Capture", SUNIV_CODEC_ADC_ACTL,
+ SUNIV_CODEC_ADC_ADCEN, 0),
+
+ /* ADC Mixers */
+ SOC_MIXER_ARRAY("ADC Mixer", SUNIV_CODEC_ADC_ACTL,
+ SND_SOC_NOPM, 0,
+ suniv_codec_adc_mixer_controls),
+
+ /* Digital parts of the DACs */
+ SND_SOC_DAPM_SUPPLY("DAC Enable", SUN4I_CODEC_DAC_DPC,
+ SUN4I_CODEC_DAC_DPC_EN_DA, 0,
+ NULL, 0),
+
+ /* Analog parts of the DACs */
+ SND_SOC_DAPM_DAC("Left DAC", "Codec Playback",
+ SUNIV_CODEC_OM_DACA_CTRL,
+ SUNIV_CODEC_OM_DACA_CTRL_DACALEN, 0),
+ SND_SOC_DAPM_DAC("Right DAC", "Codec Playback",
+ SUNIV_CODEC_OM_DACA_CTRL,
+ SUNIV_CODEC_OM_DACA_CTRL_DACAREN, 0),
+
+ /* Mixers */
+ SOC_MIXER_ARRAY("Left Mixer", SUNIV_CODEC_OM_DACA_CTRL,
+ SUNIV_CODEC_OM_DACA_CTRL_LMIXEN, 0,
+ suniv_codec_dac_lmixer_controls),
+ SOC_MIXER_ARRAY("Right Mixer", SUNIV_CODEC_OM_DACA_CTRL,
+ SUNIV_CODEC_OM_DACA_CTRL_RMIXEN, 0,
+ suniv_codec_dac_rmixer_controls),
+
+ /* Headphone output path */
+ SND_SOC_DAPM_MUX("Headphone Source Playback Route",
+ SND_SOC_NOPM, 0, 0, suniv_codec_hp_src),
+ SND_SOC_DAPM_OUT_DRV("Headphone Amp", SUNIV_CODEC_OM_DACA_CTRL,
+ SUNIV_CODEC_OM_DACA_CTRL_HPPAEN, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("HPCOM Protection", SUNIV_CODEC_OM_DACA_CTRL,
+ SUNIV_CODEC_OM_DACA_CTRL_COMPTEN, 0, NULL, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_supply, "HPCOM", SUNIV_CODEC_OM_DACA_CTRL,
+ SUNIV_CODEC_OM_DACA_CTRL_HPCOM_CTL, 0x3, 0x3, 0),
+ SND_SOC_DAPM_OUTPUT("HP"),
+};
+
+static const struct snd_soc_dapm_route suniv_codec_codec_dapm_routes[] = {
+ /* DAC Routes */
+ { "Left DAC", NULL, "DAC Enable" },
+ { "Right DAC", NULL, "DAC Enable" },
+
+ /* Microphone Routes */
+ { "Mic Amplifier", NULL, "MIC"},
+
+ /* Left Mixer Routes */
+ { "Left Mixer", "Right DAC Playback Switch", "Right DAC" },
+ { "Left Mixer", "Left DAC Playback Switch", "Left DAC" },
+ { "Left Mixer", "FM In Playback Switch", "FMINL" },
+ { "Left Mixer", "Line In Playback Switch", "LINEIN" },
+ { "Left Mixer", "Mic In Playback Switch", "Mic Amplifier" },
+
+ /* Right Mixer Routes */
+ { "Right Mixer", "Left DAC Playback Switch", "Left DAC" },
+ { "Right Mixer", "Right DAC Playback Switch", "Right DAC" },
+ { "Right Mixer", "FM In Playback Switch", "FMINR" },
+ { "Right Mixer", "Line In Playback Switch", "LINEIN" },
+ { "Right Mixer", "Mic In Playback Switch", "Mic Amplifier" },
+
+ /* ADC Mixer Routes */
+ { "ADC Mixer", "Right Out Capture Switch", "Right Mixer" },
+ { "ADC Mixer", "Left Out Capture Switch", "Left Mixer" },
+ { "ADC Mixer", "Line In Capture Switch", "LINEIN" },
+ { "ADC Mixer", "Right FM In Capture Switch", "FMINR" },
+ { "ADC Mixer", "Left FM In Capture Switch", "FMINL" },
+ { "ADC Mixer", "Mic Capture Switch", "Mic Amplifier" },
+
+ /* Headphone Routes */
+ { "Headphone Source Playback Route", "DAC", "Left DAC" },
+ { "Headphone Source Playback Route", "DAC", "Right DAC" },
+ { "Headphone Source Playback Route", "Mixer", "Left Mixer" },
+ { "Headphone Source Playback Route", "Mixer", "Right Mixer" },
+ { "Headphone Amp", NULL, "Headphone Source Playback Route" },
+ { "HP", NULL, "Headphone Amp" },
+ { "HPCOM", NULL, "HPCOM Protection" },
+
+ /* ADC Routes */
+ { "ADC", NULL, "ADC Mixer" },
+ { "ADC", NULL, "ADC Enable" },
+};
+
+static const struct snd_soc_component_driver suniv_codec_codec = {
+ .controls = suniv_codec_codec_widgets,
+ .num_controls = ARRAY_SIZE(suniv_codec_codec_widgets),
+ .dapm_widgets = suniv_codec_codec_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(suniv_codec_codec_dapm_widgets),
+ .dapm_routes = suniv_codec_codec_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(suniv_codec_codec_dapm_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
static const struct snd_soc_component_driver sun4i_codec_component = {
.name = "sun4i-codec",
.legacy_dai_naming = 1,
@@ -1302,6 +1585,49 @@ static struct snd_soc_dai_driver dummy_cpu_dai = {
.ops = &dummy_dai_ops,
};
+static struct snd_soc_jack sun4i_headphone_jack;
+
+static struct snd_soc_jack_pin sun4i_headphone_jack_pins[] = {
+ { .pin = "Headphone", .mask = SND_JACK_HEADPHONE },
+};
+
+static struct snd_soc_jack_gpio sun4i_headphone_jack_gpio = {
+ .name = "hp-det",
+ .report = SND_JACK_HEADPHONE,
+ .debounce_time = 150,
+};
+
+static int sun4i_codec_machine_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct sun4i_codec *scodec = snd_soc_card_get_drvdata(card);
+ int ret;
+
+ if (scodec->gpio_hp) {
+ ret = snd_soc_card_jack_new_pins(card, "Headphone Jack",
+ SND_JACK_HEADPHONE,
+ &sun4i_headphone_jack,
+ sun4i_headphone_jack_pins,
+ ARRAY_SIZE(sun4i_headphone_jack_pins));
+ if (ret) {
+ dev_err(rtd->dev,
+ "Headphone jack creation failed: %d\n", ret);
+ return ret;
+ }
+
+ sun4i_headphone_jack_gpio.desc = scodec->gpio_hp;
+ ret = snd_soc_jack_add_gpios(&sun4i_headphone_jack, 1,
+ &sun4i_headphone_jack_gpio);
+
+ if (ret) {
+ dev_err(rtd->dev, "Headphone GPIO not added: %d\n", ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
static struct snd_soc_dai_link *sun4i_codec_create_link(struct device *dev,
int *num_links)
{
@@ -1327,6 +1653,7 @@ static struct snd_soc_dai_link *sun4i_codec_create_link(struct device *dev,
link->codecs->name = dev_name(dev);
link->platforms->name = dev_name(dev);
link->dai_fmt = SND_SOC_DAIFMT_I2S;
+ link->init = sun4i_codec_machine_init;
*num_links = 1;
@@ -1635,10 +1962,11 @@ static const struct snd_soc_component_driver sun50i_h616_codec_codec = {
};
static const struct snd_kcontrol_new sun50i_h616_card_controls[] = {
- SOC_DAPM_PIN_SWITCH("LINEOUT"),
+ SOC_DAPM_PIN_SWITCH("Speaker"),
};
static const struct snd_soc_dapm_widget sun50i_h616_codec_card_dapm_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone", NULL),
SND_SOC_DAPM_LINE("Line Out", NULL),
SND_SOC_DAPM_SPK("Speaker", sun4i_codec_spk_event),
};
@@ -1685,6 +2013,7 @@ static struct snd_soc_card *sun50i_h616_codec_create_card(struct device *dev)
card->dev = dev;
card->owner = THIS_MODULE;
card->name = "H616 Audio Codec";
+ card->long_name = "h616-audio-codec";
card->driver_name = "sun4i-codec";
card->controls = sun50i_h616_card_controls;
card->num_controls = ARRAY_SIZE(sun50i_h616_card_controls);
@@ -1701,6 +2030,56 @@ static struct snd_soc_card *sun50i_h616_codec_create_card(struct device *dev)
return card;
};
+static const struct snd_soc_dapm_widget suniv_codec_card_dapm_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone", NULL),
+ SND_SOC_DAPM_LINE("Line In", NULL),
+ SND_SOC_DAPM_LINE("Right FM In", NULL),
+ SND_SOC_DAPM_LINE("Left FM In", NULL),
+ SND_SOC_DAPM_MIC("Mic", NULL),
+ SND_SOC_DAPM_SPK("Speaker", sun4i_codec_spk_event),
+};
+
+/* Connect digital side enables to analog side widgets */
+static const struct snd_soc_dapm_route suniv_codec_card_routes[] = {
+ /* ADC Routes */
+ { "ADC", NULL, "ADC Enable" },
+ { "Codec Capture", NULL, "ADC" },
+
+ /* DAC Routes */
+ { "Left DAC", NULL, "DAC Enable" },
+ { "Right DAC", NULL, "DAC Enable" },
+ { "Left DAC", NULL, "Codec Playback" },
+ { "Right DAC", NULL, "Codec Playback" },
+};
+
+static struct snd_soc_card *suniv_codec_create_card(struct device *dev)
+{
+ struct snd_soc_card *card;
+ int ret;
+
+ card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
+ if (!card)
+ return ERR_PTR(-ENOMEM);
+
+ card->dai_link = sun4i_codec_create_link(dev, &card->num_links);
+ if (!card->dai_link)
+ return ERR_PTR(-ENOMEM);
+
+ card->dev = dev;
+ card->name = "F1C100s Audio Codec";
+ card->dapm_widgets = suniv_codec_card_dapm_widgets;
+ card->num_dapm_widgets = ARRAY_SIZE(suniv_codec_card_dapm_widgets);
+ card->dapm_routes = suniv_codec_card_routes;
+ card->num_dapm_routes = ARRAY_SIZE(suniv_codec_card_routes);
+ card->fully_routed = true;
+
+ ret = snd_soc_of_parse_audio_routing(card, "allwinner,audio-routing");
+ if (ret)
+ dev_warn(dev, "failed to parse audio-routing: %d\n", ret);
+
+ return card;
+};
+
static const struct regmap_config sun4i_codec_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
@@ -1751,6 +2130,13 @@ static const struct regmap_config sun50i_h616_codec_regmap_config = {
.cache_type = REGCACHE_NONE,
};
+static const struct regmap_config suniv_codec_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = SUNIV_CODEC_ADC_DBG,
+};
+
struct sun4i_codec_quirks {
const struct regmap_config *regmap_config;
const struct snd_soc_component_driver *codec;
@@ -1761,6 +2147,7 @@ struct sun4i_codec_quirks {
unsigned int reg_adc_rxdata; /* RX FIFO offset for DMA config */
bool has_reset;
bool playback_only;
+ u32 dma_max_burst;
};
static const struct sun4i_codec_quirks sun4i_codec_quirks = {
@@ -1771,6 +2158,7 @@ static const struct sun4i_codec_quirks sun4i_codec_quirks = {
.reg_dac_fifoc = REG_FIELD(SUN4I_CODEC_DAC_FIFOC, 0, 31),
.reg_dac_txdata = SUN4I_CODEC_DAC_TXDATA,
.reg_adc_rxdata = SUN4I_CODEC_ADC_RXDATA,
+ .dma_max_burst = SUN4I_DMA_MAX_BURST,
};
static const struct sun4i_codec_quirks sun6i_a31_codec_quirks = {
@@ -1782,6 +2170,7 @@ static const struct sun4i_codec_quirks sun6i_a31_codec_quirks = {
.reg_dac_txdata = SUN4I_CODEC_DAC_TXDATA,
.reg_adc_rxdata = SUN6I_CODEC_ADC_RXDATA,
.has_reset = true,
+ .dma_max_burst = SUN4I_DMA_MAX_BURST,
};
static const struct sun4i_codec_quirks sun7i_codec_quirks = {
@@ -1792,6 +2181,7 @@ static const struct sun4i_codec_quirks sun7i_codec_quirks = {
.reg_dac_fifoc = REG_FIELD(SUN4I_CODEC_DAC_FIFOC, 0, 31),
.reg_dac_txdata = SUN4I_CODEC_DAC_TXDATA,
.reg_adc_rxdata = SUN4I_CODEC_ADC_RXDATA,
+ .dma_max_burst = SUN4I_DMA_MAX_BURST,
};
static const struct sun4i_codec_quirks sun8i_a23_codec_quirks = {
@@ -1803,6 +2193,7 @@ static const struct sun4i_codec_quirks sun8i_a23_codec_quirks = {
.reg_dac_txdata = SUN4I_CODEC_DAC_TXDATA,
.reg_adc_rxdata = SUN6I_CODEC_ADC_RXDATA,
.has_reset = true,
+ .dma_max_burst = SUN4I_DMA_MAX_BURST,
};
static const struct sun4i_codec_quirks sun8i_h3_codec_quirks = {
@@ -1819,6 +2210,7 @@ static const struct sun4i_codec_quirks sun8i_h3_codec_quirks = {
.reg_dac_txdata = SUN8I_H3_CODEC_DAC_TXDATA,
.reg_adc_rxdata = SUN6I_CODEC_ADC_RXDATA,
.has_reset = true,
+ .dma_max_burst = SUN4I_DMA_MAX_BURST,
};
static const struct sun4i_codec_quirks sun8i_v3s_codec_quirks = {
@@ -1834,6 +2226,7 @@ static const struct sun4i_codec_quirks sun8i_v3s_codec_quirks = {
.reg_dac_txdata = SUN8I_H3_CODEC_DAC_TXDATA,
.reg_adc_rxdata = SUN6I_CODEC_ADC_RXDATA,
.has_reset = true,
+ .dma_max_burst = SUN4I_DMA_MAX_BURST,
};
static const struct sun4i_codec_quirks sun50i_h616_codec_quirks = {
@@ -1843,6 +2236,19 @@ static const struct sun4i_codec_quirks sun50i_h616_codec_quirks = {
.reg_dac_fifoc = REG_FIELD(SUN50I_H616_CODEC_DAC_FIFOC, 0, 31),
.reg_dac_txdata = SUN8I_H3_CODEC_DAC_TXDATA,
.has_reset = true,
+ .dma_max_burst = SUN4I_DMA_MAX_BURST,
+};
+
+static const struct sun4i_codec_quirks suniv_f1c100s_codec_quirks = {
+ .regmap_config = &suniv_codec_regmap_config,
+ .codec = &suniv_codec_codec,
+ .create_card = suniv_codec_create_card,
+ .reg_adc_fifoc = REG_FIELD(SUNIV_CODEC_ADC_FIFOC, 0, 31),
+ .reg_dac_fifoc = REG_FIELD(SUN4I_CODEC_DAC_FIFOC, 0, 31),
+ .reg_dac_txdata = SUN4I_CODEC_DAC_TXDATA,
+ .reg_adc_rxdata = SUNIV_CODEC_ADC_RXDATA,
+ .has_reset = true,
+ .dma_max_burst = SUNIV_DMA_MAX_BURST,
};
static const struct of_device_id sun4i_codec_of_match[] = {
@@ -1874,6 +2280,10 @@ static const struct of_device_id sun4i_codec_of_match[] = {
.compatible = "allwinner,sun50i-h616-codec",
.data = &sun50i_h616_codec_quirks,
},
+ {
+ .compatible = "allwinner,suniv-f1c100s-codec",
+ .data = &suniv_f1c100s_codec_quirks,
+ },
{}
};
MODULE_DEVICE_TABLE(of, sun4i_codec_of_match);
@@ -1911,7 +2321,7 @@ static int sun4i_codec_probe(struct platform_device *pdev)
}
/* Get the clocks from the DT */
- scodec->clk_apb = devm_clk_get(&pdev->dev, "apb");
+ scodec->clk_apb = devm_clk_get_enabled(&pdev->dev, "apb");
if (IS_ERR(scodec->clk_apb)) {
dev_err(&pdev->dev, "Failed to get the APB clock\n");
return PTR_ERR(scodec->clk_apb);
@@ -1924,8 +2334,7 @@ static int sun4i_codec_probe(struct platform_device *pdev)
}
if (quirks->has_reset) {
- scodec->rst = devm_reset_control_get_exclusive(&pdev->dev,
- NULL);
+ scodec->rst = devm_reset_control_get_exclusive_deasserted(&pdev->dev, NULL);
if (IS_ERR(scodec->rst)) {
dev_err(&pdev->dev, "Failed to get reset control\n");
return PTR_ERR(scodec->rst);
@@ -1940,6 +2349,13 @@ static int sun4i_codec_probe(struct platform_device *pdev)
return ret;
}
+ scodec->gpio_hp = devm_gpiod_get_optional(&pdev->dev, "hp-det", GPIOD_IN);
+ if (IS_ERR(scodec->gpio_hp)) {
+ ret = PTR_ERR(scodec->gpio_hp);
+ dev_err_probe(&pdev->dev, ret, "Failed to get hp-det gpio\n");
+ return ret;
+ }
+
/* reg_field setup */
scodec->reg_adc_fifoc = devm_regmap_field_alloc(&pdev->dev,
scodec->regmap,
@@ -1961,32 +2377,16 @@ static int sun4i_codec_probe(struct platform_device *pdev)
return ret;
}
- /* Enable the bus clock */
- if (clk_prepare_enable(scodec->clk_apb)) {
- dev_err(&pdev->dev, "Failed to enable the APB clock\n");
- return -EINVAL;
- }
-
- /* Deassert the reset control */
- if (scodec->rst) {
- ret = reset_control_deassert(scodec->rst);
- if (ret) {
- dev_err(&pdev->dev,
- "Failed to deassert the reset control\n");
- goto err_clk_disable;
- }
- }
-
/* DMA configuration for TX FIFO */
scodec->playback_dma_data.addr = res->start + quirks->reg_dac_txdata;
- scodec->playback_dma_data.maxburst = 8;
+ scodec->playback_dma_data.maxburst = quirks->dma_max_burst;
scodec->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
if (!quirks->playback_only) {
/* DMA configuration for RX FIFO */
scodec->capture_dma_data.addr = res->start +
quirks->reg_adc_rxdata;
- scodec->capture_dma_data.maxburst = 8;
+ scodec->capture_dma_data.maxburst = quirks->dma_max_burst;
scodec->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
}
@@ -1994,7 +2394,7 @@ static int sun4i_codec_probe(struct platform_device *pdev)
&sun4i_codec_dai, 1);
if (ret) {
dev_err(&pdev->dev, "Failed to register our codec\n");
- goto err_assert_reset;
+ return ret;
}
ret = devm_snd_soc_register_component(&pdev->dev,
@@ -2002,20 +2402,20 @@ static int sun4i_codec_probe(struct platform_device *pdev)
&dummy_cpu_dai, 1);
if (ret) {
dev_err(&pdev->dev, "Failed to register our DAI\n");
- goto err_assert_reset;
+ return ret;
}
ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
if (ret) {
dev_err(&pdev->dev, "Failed to register against DMAEngine\n");
- goto err_assert_reset;
+ return ret;
}
card = quirks->create_card(&pdev->dev);
if (IS_ERR(card)) {
ret = PTR_ERR(card);
dev_err(&pdev->dev, "Failed to create our card\n");
- goto err_assert_reset;
+ return ret;
}
snd_soc_card_set_drvdata(card, scodec);
@@ -2023,28 +2423,17 @@ static int sun4i_codec_probe(struct platform_device *pdev)
ret = snd_soc_register_card(card);
if (ret) {
dev_err_probe(&pdev->dev, ret, "Failed to register our card\n");
- goto err_assert_reset;
+ return ret;
}
return 0;
-
-err_assert_reset:
- if (scodec->rst)
- reset_control_assert(scodec->rst);
-err_clk_disable:
- clk_disable_unprepare(scodec->clk_apb);
- return ret;
}
static void sun4i_codec_remove(struct platform_device *pdev)
{
struct snd_soc_card *card = platform_get_drvdata(pdev);
- struct sun4i_codec *scodec = snd_soc_card_get_drvdata(card);
snd_soc_unregister_card(card);
- if (scodec->rst)
- reset_control_assert(scodec->rst);
- clk_disable_unprepare(scodec->clk_apb);
}
static struct platform_driver sun4i_codec_driver = {
@@ -2063,4 +2452,5 @@ MODULE_AUTHOR("Jon Smirl <jonsmirl@gmail.com>");
MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
MODULE_AUTHOR("Chen-Yu Tsai <wens@csie.org>");
MODULE_AUTHOR("Ryan Walklin <ryan@testtoast.com");
+MODULE_AUTHOR("Mesih Kilinc <mesikilinc@gmail.com>");
MODULE_LICENSE("GPL");