brintos

brintos / linux-shallow public Read only

0
0
Text · 26.8 KiB · 445b341 Raw
854 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * This driver supports the analog controls for the internal codec4 * found in Allwinner's A31s, A23, A33 and H3 SoCs.5 *6 * Copyright 2016 Chen-Yu Tsai <wens@csie.org>7 */8 9#include <linux/io.h>10#include <linux/kernel.h>11#include <linux/module.h>12#include <linux/of.h>13#include <linux/platform_device.h>14#include <linux/regmap.h>15 16#include <sound/soc.h>17#include <sound/soc-dapm.h>18#include <sound/tlv.h>19 20#include "sun8i-adda-pr-regmap.h"21 22/* Codec analog control register offsets and bit fields */23#define SUN8I_ADDA_HP_VOLC		0x0024#define SUN8I_ADDA_HP_VOLC_PA_CLK_GATE		725#define SUN8I_ADDA_HP_VOLC_HP_VOL		026#define SUN8I_ADDA_LOMIXSC		0x0127#define SUN8I_ADDA_LOMIXSC_MIC1			628#define SUN8I_ADDA_LOMIXSC_MIC2			529#define SUN8I_ADDA_LOMIXSC_PHONE		430#define SUN8I_ADDA_LOMIXSC_PHONEN		331#define SUN8I_ADDA_LOMIXSC_LINEINL		232#define SUN8I_ADDA_LOMIXSC_DACL			133#define SUN8I_ADDA_LOMIXSC_DACR			034#define SUN8I_ADDA_ROMIXSC		0x0235#define SUN8I_ADDA_ROMIXSC_MIC1			636#define SUN8I_ADDA_ROMIXSC_MIC2			537#define SUN8I_ADDA_ROMIXSC_PHONE		438#define SUN8I_ADDA_ROMIXSC_PHONEP		339#define SUN8I_ADDA_ROMIXSC_LINEINR		240#define SUN8I_ADDA_ROMIXSC_DACR			141#define SUN8I_ADDA_ROMIXSC_DACL			042#define SUN8I_ADDA_DAC_PA_SRC		0x0343#define SUN8I_ADDA_DAC_PA_SRC_DACAREN		744#define SUN8I_ADDA_DAC_PA_SRC_DACALEN		645#define SUN8I_ADDA_DAC_PA_SRC_RMIXEN		546#define SUN8I_ADDA_DAC_PA_SRC_LMIXEN		447#define SUN8I_ADDA_DAC_PA_SRC_RHPPAMUTE		348#define SUN8I_ADDA_DAC_PA_SRC_LHPPAMUTE		249#define SUN8I_ADDA_DAC_PA_SRC_RHPIS		150#define SUN8I_ADDA_DAC_PA_SRC_LHPIS		051#define SUN8I_ADDA_PHONEIN_GCTRL	0x0452#define SUN8I_ADDA_PHONEIN_GCTRL_PHONEPG	453#define SUN8I_ADDA_PHONEIN_GCTRL_PHONENG	054#define SUN8I_ADDA_LINEIN_GCTRL		0x0555#define SUN8I_ADDA_LINEIN_GCTRL_LINEING		456#define SUN8I_ADDA_LINEIN_GCTRL_PHONEG		057#define SUN8I_ADDA_MICIN_GCTRL		0x0658#define SUN8I_ADDA_MICIN_GCTRL_MIC1G		459#define SUN8I_ADDA_MICIN_GCTRL_MIC2G		060#define SUN8I_ADDA_PAEN_HP_CTRL		0x0761#define SUN8I_ADDA_PAEN_HP_CTRL_HPPAEN		762#define SUN8I_ADDA_PAEN_HP_CTRL_LINEOUTEN	7	/* H3 specific */63#define SUN8I_ADDA_PAEN_HP_CTRL_HPCOM_FC	564#define SUN8I_ADDA_PAEN_HP_CTRL_COMPTEN		465#define SUN8I_ADDA_PAEN_HP_CTRL_PA_ANTI_POP_CTRL	266#define SUN8I_ADDA_PAEN_HP_CTRL_LTRNMUTE	167#define SUN8I_ADDA_PAEN_HP_CTRL_RTLNMUTE	068#define SUN8I_ADDA_PHONEOUT_CTRL	0x0869#define SUN8I_ADDA_PHONEOUT_CTRL_PHONEOUTG	570#define SUN8I_ADDA_PHONEOUT_CTRL_PHONEOUTEN	471#define SUN8I_ADDA_PHONEOUT_CTRL_PHONEOUT_MIC1	372#define SUN8I_ADDA_PHONEOUT_CTRL_PHONEOUT_MIC2	273#define SUN8I_ADDA_PHONEOUT_CTRL_PHONEOUT_RMIX	174#define SUN8I_ADDA_PHONEOUT_CTRL_PHONEOUT_LMIX	075#define SUN8I_ADDA_PHONE_GAIN_CTRL	0x0976#define SUN8I_ADDA_PHONE_GAIN_CTRL_LINEOUT_VOL	377#define SUN8I_ADDA_PHONE_GAIN_CTRL_PHONEPREG	078#define SUN8I_ADDA_MIC2G_CTRL		0x0a79#define SUN8I_ADDA_MIC2G_CTRL_MIC2AMPEN		780#define SUN8I_ADDA_MIC2G_CTRL_MIC2BOOST		481#define SUN8I_ADDA_MIC2G_CTRL_LINEOUTLEN	382#define SUN8I_ADDA_MIC2G_CTRL_LINEOUTREN	283#define SUN8I_ADDA_MIC2G_CTRL_LINEOUTLSRC	184#define SUN8I_ADDA_MIC2G_CTRL_LINEOUTRSRC	085#define SUN8I_ADDA_MIC1G_MICBIAS_CTRL	0x0b86#define SUN8I_ADDA_MIC1G_MICBIAS_CTRL_HMICBIASEN	787#define SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MMICBIASEN	688#define SUN8I_ADDA_MIC1G_MICBIAS_CTRL_HMICBIAS_MODE	589#define SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MIC1AMPEN		390#define SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MIC1BOOST		091#define SUN8I_ADDA_LADCMIXSC		0x0c92#define SUN8I_ADDA_LADCMIXSC_MIC1		693#define SUN8I_ADDA_LADCMIXSC_MIC2		594#define SUN8I_ADDA_LADCMIXSC_PHONE		495#define SUN8I_ADDA_LADCMIXSC_PHONEN		396#define SUN8I_ADDA_LADCMIXSC_LINEINL		297#define SUN8I_ADDA_LADCMIXSC_OMIXRL		198#define SUN8I_ADDA_LADCMIXSC_OMIXRR		099#define SUN8I_ADDA_RADCMIXSC		0x0d100#define SUN8I_ADDA_RADCMIXSC_MIC1		6101#define SUN8I_ADDA_RADCMIXSC_MIC2		5102#define SUN8I_ADDA_RADCMIXSC_PHONE		4103#define SUN8I_ADDA_RADCMIXSC_PHONEP		3104#define SUN8I_ADDA_RADCMIXSC_LINEINR		2105#define SUN8I_ADDA_RADCMIXSC_OMIXR		1106#define SUN8I_ADDA_RADCMIXSC_OMIXL		0107#define SUN8I_ADDA_RES			0x0e108#define SUN8I_ADDA_RES_MMICBIAS_SEL		4109#define SUN8I_ADDA_RES_PA_ANTI_POP_CTRL		0110#define SUN8I_ADDA_ADC_AP_EN		0x0f111#define SUN8I_ADDA_ADC_AP_EN_ADCREN		7112#define SUN8I_ADDA_ADC_AP_EN_ADCLEN		6113#define SUN8I_ADDA_ADC_AP_EN_ADCG		0114 115/* mixer controls */116static const struct snd_kcontrol_new sun8i_codec_mixer_controls[] = {117	SOC_DAPM_DOUBLE_R("DAC Playback Switch",118			  SUN8I_ADDA_LOMIXSC,119			  SUN8I_ADDA_ROMIXSC,120			  SUN8I_ADDA_LOMIXSC_DACL, 1, 0),121	SOC_DAPM_DOUBLE_R("DAC Reversed Playback Switch",122			  SUN8I_ADDA_LOMIXSC,123			  SUN8I_ADDA_ROMIXSC,124			  SUN8I_ADDA_LOMIXSC_DACR, 1, 0),125	SOC_DAPM_DOUBLE_R("Line In Playback Switch",126			  SUN8I_ADDA_LOMIXSC,127			  SUN8I_ADDA_ROMIXSC,128			  SUN8I_ADDA_LOMIXSC_LINEINL, 1, 0),129	SOC_DAPM_DOUBLE_R("Mic1 Playback Switch",130			  SUN8I_ADDA_LOMIXSC,131			  SUN8I_ADDA_ROMIXSC,132			  SUN8I_ADDA_LOMIXSC_MIC1, 1, 0),133	SOC_DAPM_DOUBLE_R("Mic2 Playback Switch",134			  SUN8I_ADDA_LOMIXSC,135			  SUN8I_ADDA_ROMIXSC,136			  SUN8I_ADDA_LOMIXSC_MIC2, 1, 0),137};138 139/* mixer controls */140static const struct snd_kcontrol_new sun8i_v3s_codec_mixer_controls[] = {141	SOC_DAPM_DOUBLE_R("DAC Playback Switch",142			  SUN8I_ADDA_LOMIXSC,143			  SUN8I_ADDA_ROMIXSC,144			  SUN8I_ADDA_LOMIXSC_DACL, 1, 0),145	SOC_DAPM_DOUBLE_R("DAC Reversed Playback Switch",146			  SUN8I_ADDA_LOMIXSC,147			  SUN8I_ADDA_ROMIXSC,148			  SUN8I_ADDA_LOMIXSC_DACR, 1, 0),149	SOC_DAPM_DOUBLE_R("Mic1 Playback Switch",150			  SUN8I_ADDA_LOMIXSC,151			  SUN8I_ADDA_ROMIXSC,152			  SUN8I_ADDA_LOMIXSC_MIC1, 1, 0),153};154 155/* ADC mixer controls */156static const struct snd_kcontrol_new sun8i_codec_adc_mixer_controls[] = {157	SOC_DAPM_DOUBLE_R("Mixer Capture Switch",158			  SUN8I_ADDA_LADCMIXSC,159			  SUN8I_ADDA_RADCMIXSC,160			  SUN8I_ADDA_LADCMIXSC_OMIXRL, 1, 0),161	SOC_DAPM_DOUBLE_R("Mixer Reversed Capture Switch",162			  SUN8I_ADDA_LADCMIXSC,163			  SUN8I_ADDA_RADCMIXSC,164			  SUN8I_ADDA_LADCMIXSC_OMIXRR, 1, 0),165	SOC_DAPM_DOUBLE_R("Line In Capture Switch",166			  SUN8I_ADDA_LADCMIXSC,167			  SUN8I_ADDA_RADCMIXSC,168			  SUN8I_ADDA_LADCMIXSC_LINEINL, 1, 0),169	SOC_DAPM_DOUBLE_R("Mic1 Capture Switch",170			  SUN8I_ADDA_LADCMIXSC,171			  SUN8I_ADDA_RADCMIXSC,172			  SUN8I_ADDA_LADCMIXSC_MIC1, 1, 0),173	SOC_DAPM_DOUBLE_R("Mic2 Capture Switch",174			  SUN8I_ADDA_LADCMIXSC,175			  SUN8I_ADDA_RADCMIXSC,176			  SUN8I_ADDA_LADCMIXSC_MIC2, 1, 0),177};178 179/* ADC mixer controls */180static const struct snd_kcontrol_new sun8i_v3s_codec_adc_mixer_controls[] = {181	SOC_DAPM_DOUBLE_R("Mixer Capture Switch",182			  SUN8I_ADDA_LADCMIXSC,183			  SUN8I_ADDA_RADCMIXSC,184			  SUN8I_ADDA_LADCMIXSC_OMIXRL, 1, 0),185	SOC_DAPM_DOUBLE_R("Mixer Reversed Capture Switch",186			  SUN8I_ADDA_LADCMIXSC,187			  SUN8I_ADDA_RADCMIXSC,188			  SUN8I_ADDA_LADCMIXSC_OMIXRR, 1, 0),189	SOC_DAPM_DOUBLE_R("Mic1 Capture Switch",190			  SUN8I_ADDA_LADCMIXSC,191			  SUN8I_ADDA_RADCMIXSC,192			  SUN8I_ADDA_LADCMIXSC_MIC1, 1, 0),193};194 195/* volume / mute controls */196static const DECLARE_TLV_DB_SCALE(sun8i_codec_out_mixer_pregain_scale,197				  -450, 150, 0);198static const DECLARE_TLV_DB_RANGE(sun8i_codec_mic_gain_scale,199	0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),200	1, 7, TLV_DB_SCALE_ITEM(2400, 300, 0),201);202 203static const struct snd_kcontrol_new sun8i_codec_common_controls[] = {204	/* Mixer pre-gain */205	SOC_SINGLE_TLV("Mic1 Playback Volume", SUN8I_ADDA_MICIN_GCTRL,206		       SUN8I_ADDA_MICIN_GCTRL_MIC1G,207		       0x7, 0, sun8i_codec_out_mixer_pregain_scale),208 209	/* Microphone Amp boost gain */210	SOC_SINGLE_TLV("Mic1 Boost Volume", SUN8I_ADDA_MIC1G_MICBIAS_CTRL,211		       SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MIC1BOOST, 0x7, 0,212		       sun8i_codec_mic_gain_scale),213 214	/* ADC */215	SOC_SINGLE_TLV("ADC Gain Capture Volume", SUN8I_ADDA_ADC_AP_EN,216		       SUN8I_ADDA_ADC_AP_EN_ADCG, 0x7, 0,217		       sun8i_codec_out_mixer_pregain_scale),218};219 220static const struct snd_soc_dapm_widget sun8i_codec_common_widgets[] = {221	/* ADC */222	SND_SOC_DAPM_ADC("Left ADC", NULL, SUN8I_ADDA_ADC_AP_EN,223			 SUN8I_ADDA_ADC_AP_EN_ADCLEN, 0),224	SND_SOC_DAPM_ADC("Right ADC", NULL, SUN8I_ADDA_ADC_AP_EN,225			 SUN8I_ADDA_ADC_AP_EN_ADCREN, 0),226 227	/* DAC */228	SND_SOC_DAPM_DAC("Left DAC", NULL, SUN8I_ADDA_DAC_PA_SRC,229			 SUN8I_ADDA_DAC_PA_SRC_DACALEN, 0),230	SND_SOC_DAPM_DAC("Right DAC", NULL, SUN8I_ADDA_DAC_PA_SRC,231			 SUN8I_ADDA_DAC_PA_SRC_DACAREN, 0),232	/*233	 * Due to this component and the codec belonging to separate DAPM234	 * contexts, we need to manually link the above widgets to their235	 * stream widgets at the card level.236	 */237 238	/* Microphone input */239	SND_SOC_DAPM_INPUT("MIC1"),240 241	/* Mic input path */242	SND_SOC_DAPM_PGA("Mic1 Amplifier", SUN8I_ADDA_MIC1G_MICBIAS_CTRL,243			 SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MIC1AMPEN, 0, NULL, 0),244};245 246static const struct snd_soc_dapm_widget sun8i_codec_mixer_widgets[] = {247	SND_SOC_DAPM_MIXER("Left Mixer", SUN8I_ADDA_DAC_PA_SRC,248			   SUN8I_ADDA_DAC_PA_SRC_LMIXEN, 0,249			   sun8i_codec_mixer_controls,250			   ARRAY_SIZE(sun8i_codec_mixer_controls)),251	SND_SOC_DAPM_MIXER("Right Mixer", SUN8I_ADDA_DAC_PA_SRC,252			   SUN8I_ADDA_DAC_PA_SRC_RMIXEN, 0,253			   sun8i_codec_mixer_controls,254			   ARRAY_SIZE(sun8i_codec_mixer_controls)),255	SND_SOC_DAPM_MIXER("Left ADC Mixer", SUN8I_ADDA_ADC_AP_EN,256			   SUN8I_ADDA_ADC_AP_EN_ADCLEN, 0,257			   sun8i_codec_adc_mixer_controls,258			   ARRAY_SIZE(sun8i_codec_adc_mixer_controls)),259	SND_SOC_DAPM_MIXER("Right ADC Mixer", SUN8I_ADDA_ADC_AP_EN,260			   SUN8I_ADDA_ADC_AP_EN_ADCREN, 0,261			   sun8i_codec_adc_mixer_controls,262			   ARRAY_SIZE(sun8i_codec_adc_mixer_controls)),263};264 265static const struct snd_soc_dapm_widget sun8i_v3s_codec_mixer_widgets[] = {266	SND_SOC_DAPM_MIXER("Left Mixer", SUN8I_ADDA_DAC_PA_SRC,267			   SUN8I_ADDA_DAC_PA_SRC_LMIXEN, 0,268			   sun8i_v3s_codec_mixer_controls,269			   ARRAY_SIZE(sun8i_v3s_codec_mixer_controls)),270	SND_SOC_DAPM_MIXER("Right Mixer", SUN8I_ADDA_DAC_PA_SRC,271			   SUN8I_ADDA_DAC_PA_SRC_RMIXEN, 0,272			   sun8i_v3s_codec_mixer_controls,273			   ARRAY_SIZE(sun8i_v3s_codec_mixer_controls)),274	SND_SOC_DAPM_MIXER("Left ADC Mixer", SUN8I_ADDA_ADC_AP_EN,275			   SUN8I_ADDA_ADC_AP_EN_ADCLEN, 0,276			   sun8i_v3s_codec_adc_mixer_controls,277			   ARRAY_SIZE(sun8i_v3s_codec_adc_mixer_controls)),278	SND_SOC_DAPM_MIXER("Right ADC Mixer", SUN8I_ADDA_ADC_AP_EN,279			   SUN8I_ADDA_ADC_AP_EN_ADCREN, 0,280			   sun8i_v3s_codec_adc_mixer_controls,281			   ARRAY_SIZE(sun8i_v3s_codec_adc_mixer_controls)),282};283 284static const struct snd_soc_dapm_route sun8i_codec_common_routes[] = {285	/* Microphone Routes */286	{ "Mic1 Amplifier", NULL, "MIC1"},287};288 289static const struct snd_soc_dapm_route sun8i_codec_mixer_routes[] = {290	/* Left Mixer Routes */291	{ "Left Mixer", "DAC Playback Switch", "Left DAC" },292	{ "Left Mixer", "DAC Reversed Playback Switch", "Right DAC" },293	{ "Left Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" },294 295	/* Right Mixer Routes */296	{ "Right Mixer", "DAC Playback Switch", "Right DAC" },297	{ "Right Mixer", "DAC Reversed Playback Switch", "Left DAC" },298	{ "Right Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" },299 300	/* Left ADC Mixer Routes */301	{ "Left ADC Mixer", "Mixer Capture Switch", "Left Mixer" },302	{ "Left ADC Mixer", "Mixer Reversed Capture Switch", "Right Mixer" },303	{ "Left ADC Mixer", "Mic1 Capture Switch", "Mic1 Amplifier" },304 305	/* Right ADC Mixer Routes */306	{ "Right ADC Mixer", "Mixer Capture Switch", "Right Mixer" },307	{ "Right ADC Mixer", "Mixer Reversed Capture Switch", "Left Mixer" },308	{ "Right ADC Mixer", "Mic1 Capture Switch", "Mic1 Amplifier" },309 310	/* ADC Routes */311	{ "Left ADC", NULL, "Left ADC Mixer" },312	{ "Right ADC", NULL, "Right ADC Mixer" },313};314 315/* headphone specific controls, widgets, and routes */316static const DECLARE_TLV_DB_SCALE(sun8i_codec_hp_vol_scale, -6300, 100, 1);317static const struct snd_kcontrol_new sun8i_codec_headphone_controls[] = {318	SOC_SINGLE_TLV("Headphone Playback Volume",319		       SUN8I_ADDA_HP_VOLC,320		       SUN8I_ADDA_HP_VOLC_HP_VOL, 0x3f, 0,321		       sun8i_codec_hp_vol_scale),322	SOC_DOUBLE("Headphone Playback Switch",323		   SUN8I_ADDA_DAC_PA_SRC,324		   SUN8I_ADDA_DAC_PA_SRC_LHPPAMUTE,325		   SUN8I_ADDA_DAC_PA_SRC_RHPPAMUTE, 1, 0),326};327 328static const char * const sun8i_codec_hp_src_enum_text[] = {329	"DAC", "Mixer",330};331 332static SOC_ENUM_DOUBLE_DECL(sun8i_codec_hp_src_enum,333			    SUN8I_ADDA_DAC_PA_SRC,334			    SUN8I_ADDA_DAC_PA_SRC_LHPIS,335			    SUN8I_ADDA_DAC_PA_SRC_RHPIS,336			    sun8i_codec_hp_src_enum_text);337 338static const struct snd_kcontrol_new sun8i_codec_hp_src[] = {339	SOC_DAPM_ENUM("Headphone Source Playback Route",340		      sun8i_codec_hp_src_enum),341};342 343static int sun8i_headphone_amp_event(struct snd_soc_dapm_widget *w,344				     struct snd_kcontrol *k, int event)345{346	struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);347 348	if (SND_SOC_DAPM_EVENT_ON(event)) {349		snd_soc_component_update_bits(component, SUN8I_ADDA_PAEN_HP_CTRL,350					      BIT(SUN8I_ADDA_PAEN_HP_CTRL_HPPAEN),351					      BIT(SUN8I_ADDA_PAEN_HP_CTRL_HPPAEN));352		/*353		 * Need a delay to have the amplifier up. 700ms seems the best354		 * compromise between the time to let the amplifier up and the355		 * time not to feel this delay while playing a sound.356		 */357		msleep(700);358	} else if (SND_SOC_DAPM_EVENT_OFF(event)) {359		snd_soc_component_update_bits(component, SUN8I_ADDA_PAEN_HP_CTRL,360					      BIT(SUN8I_ADDA_PAEN_HP_CTRL_HPPAEN),361					      0x0);362	}363 364	return 0;365}366 367static const struct snd_soc_dapm_widget sun8i_codec_headphone_widgets[] = {368	SND_SOC_DAPM_MUX("Headphone Source Playback Route",369			 SND_SOC_NOPM, 0, 0, sun8i_codec_hp_src),370	SND_SOC_DAPM_OUT_DRV_E("Headphone Amp", SUN8I_ADDA_PAEN_HP_CTRL,371			       SUN8I_ADDA_PAEN_HP_CTRL_HPPAEN, 0, NULL, 0,372			       sun8i_headphone_amp_event,373			       SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),374	SND_SOC_DAPM_SUPPLY("HPCOM Protection", SUN8I_ADDA_PAEN_HP_CTRL,375			    SUN8I_ADDA_PAEN_HP_CTRL_COMPTEN, 0, NULL, 0),376	SND_SOC_DAPM_REG(snd_soc_dapm_supply, "HPCOM", SUN8I_ADDA_PAEN_HP_CTRL,377			 SUN8I_ADDA_PAEN_HP_CTRL_HPCOM_FC, 0x3, 0x3, 0),378	SND_SOC_DAPM_OUTPUT("HP"),379};380 381static const struct snd_soc_dapm_route sun8i_codec_headphone_routes[] = {382	{ "Headphone Source Playback Route", "DAC", "Left DAC" },383	{ "Headphone Source Playback Route", "DAC", "Right DAC" },384	{ "Headphone Source Playback Route", "Mixer", "Left Mixer" },385	{ "Headphone Source Playback Route", "Mixer", "Right Mixer" },386	{ "Headphone Amp", NULL, "Headphone Source Playback Route" },387	{ "HPCOM", NULL, "HPCOM Protection" },388	{ "HP", NULL, "Headphone Amp" },389};390 391static int sun8i_codec_add_headphone(struct snd_soc_component *cmpnt)392{393	struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt);394	struct device *dev = cmpnt->dev;395	int ret;396 397	ret = snd_soc_add_component_controls(cmpnt,398					     sun8i_codec_headphone_controls,399					     ARRAY_SIZE(sun8i_codec_headphone_controls));400	if (ret) {401		dev_err(dev, "Failed to add Headphone controls: %d\n", ret);402		return ret;403	}404 405	ret = snd_soc_dapm_new_controls(dapm, sun8i_codec_headphone_widgets,406					ARRAY_SIZE(sun8i_codec_headphone_widgets));407	if (ret) {408		dev_err(dev, "Failed to add Headphone DAPM widgets: %d\n", ret);409		return ret;410	}411 412	ret = snd_soc_dapm_add_routes(dapm, sun8i_codec_headphone_routes,413				      ARRAY_SIZE(sun8i_codec_headphone_routes));414	if (ret) {415		dev_err(dev, "Failed to add Headphone DAPM routes: %d\n", ret);416		return ret;417	}418 419	return 0;420}421 422/* mbias specific widget */423static const struct snd_soc_dapm_widget sun8i_codec_mbias_widgets[] = {424	SND_SOC_DAPM_SUPPLY("MBIAS", SUN8I_ADDA_MIC1G_MICBIAS_CTRL,425			    SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MMICBIASEN,426			    0, NULL, 0),427};428 429static int sun8i_codec_add_mbias(struct snd_soc_component *cmpnt)430{431	struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt);432	struct device *dev = cmpnt->dev;433	int ret;434 435	ret = snd_soc_dapm_new_controls(dapm, sun8i_codec_mbias_widgets,436					ARRAY_SIZE(sun8i_codec_mbias_widgets));437	if (ret)438		dev_err(dev, "Failed to add MBIAS DAPM widgets: %d\n", ret);439 440	return ret;441}442 443/* hmic specific widget */444static const struct snd_soc_dapm_widget sun8i_codec_hmic_widgets[] = {445	SND_SOC_DAPM_SUPPLY("HBIAS", SUN8I_ADDA_MIC1G_MICBIAS_CTRL,446			    SUN8I_ADDA_MIC1G_MICBIAS_CTRL_HMICBIASEN,447			    0, NULL, 0),448};449 450static int sun8i_codec_add_hmic(struct snd_soc_component *cmpnt)451{452	struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt);453	struct device *dev = cmpnt->dev;454	int ret;455 456	ret = snd_soc_dapm_new_controls(dapm, sun8i_codec_hmic_widgets,457					ARRAY_SIZE(sun8i_codec_hmic_widgets));458	if (ret)459		dev_err(dev, "Failed to add Mic3 DAPM widgets: %d\n", ret);460 461	return ret;462}463 464/* line in specific controls, widgets and rines */465static const struct snd_kcontrol_new sun8i_codec_linein_controls[] = {466	/* Mixer pre-gain */467	SOC_SINGLE_TLV("Line In Playback Volume", SUN8I_ADDA_LINEIN_GCTRL,468		       SUN8I_ADDA_LINEIN_GCTRL_LINEING,469		       0x7, 0, sun8i_codec_out_mixer_pregain_scale),470};471 472static const struct snd_soc_dapm_widget sun8i_codec_linein_widgets[] = {473	/* Line input */474	SND_SOC_DAPM_INPUT("LINEIN"),475};476 477static const struct snd_soc_dapm_route sun8i_codec_linein_routes[] = {478	{ "Left Mixer", "Line In Playback Switch", "LINEIN" },479 480	{ "Right Mixer", "Line In Playback Switch", "LINEIN" },481 482	{ "Left ADC Mixer", "Line In Capture Switch", "LINEIN" },483 484	{ "Right ADC Mixer", "Line In Capture Switch", "LINEIN" },485};486 487static int sun8i_codec_add_linein(struct snd_soc_component *cmpnt)488{489	struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt);490	struct device *dev = cmpnt->dev;491	int ret;492 493	ret = snd_soc_add_component_controls(cmpnt,494					     sun8i_codec_linein_controls,495					     ARRAY_SIZE(sun8i_codec_linein_controls));496	if (ret) {497		dev_err(dev, "Failed to add Line In controls: %d\n", ret);498		return ret;499	}500 501	ret = snd_soc_dapm_new_controls(dapm, sun8i_codec_linein_widgets,502					ARRAY_SIZE(sun8i_codec_linein_widgets));503	if (ret) {504		dev_err(dev, "Failed to add Line In DAPM widgets: %d\n", ret);505		return ret;506	}507 508	ret = snd_soc_dapm_add_routes(dapm, sun8i_codec_linein_routes,509				      ARRAY_SIZE(sun8i_codec_linein_routes));510	if (ret) {511		dev_err(dev, "Failed to add Line In DAPM routes: %d\n", ret);512		return ret;513	}514 515	return 0;516}517 518 519/* line out specific controls, widgets and routes */520static const DECLARE_TLV_DB_RANGE(sun8i_codec_lineout_vol_scale,521	0, 1, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1),522	2, 31, TLV_DB_SCALE_ITEM(-4350, 150, 0),523);524static const struct snd_kcontrol_new sun8i_codec_lineout_controls[] = {525	SOC_SINGLE_TLV("Line Out Playback Volume",526		       SUN8I_ADDA_PHONE_GAIN_CTRL,527		       SUN8I_ADDA_PHONE_GAIN_CTRL_LINEOUT_VOL, 0x1f, 0,528		       sun8i_codec_lineout_vol_scale),529	SOC_DOUBLE("Line Out Playback Switch",530		   SUN8I_ADDA_MIC2G_CTRL,531		   SUN8I_ADDA_MIC2G_CTRL_LINEOUTLEN,532		   SUN8I_ADDA_MIC2G_CTRL_LINEOUTREN, 1, 0),533};534 535static const char * const sun8i_codec_lineout_src_enum_text[] = {536	"Stereo", "Mono Differential",537};538 539static SOC_ENUM_DOUBLE_DECL(sun8i_codec_lineout_src_enum,540			    SUN8I_ADDA_MIC2G_CTRL,541			    SUN8I_ADDA_MIC2G_CTRL_LINEOUTLSRC,542			    SUN8I_ADDA_MIC2G_CTRL_LINEOUTRSRC,543			    sun8i_codec_lineout_src_enum_text);544 545static const struct snd_kcontrol_new sun8i_codec_lineout_src[] = {546	SOC_DAPM_ENUM("Line Out Source Playback Route",547		      sun8i_codec_lineout_src_enum),548};549 550static const struct snd_soc_dapm_widget sun8i_codec_lineout_widgets[] = {551	SND_SOC_DAPM_MUX("Line Out Source Playback Route",552			 SND_SOC_NOPM, 0, 0, sun8i_codec_lineout_src),553	/* It is unclear if this is a buffer or gate, model it as a supply */554	SND_SOC_DAPM_SUPPLY("Line Out Enable", SUN8I_ADDA_PAEN_HP_CTRL,555			    SUN8I_ADDA_PAEN_HP_CTRL_LINEOUTEN, 0, NULL, 0),556	SND_SOC_DAPM_OUTPUT("LINEOUT"),557};558 559static const struct snd_soc_dapm_route sun8i_codec_lineout_routes[] = {560	{ "Line Out Source Playback Route", "Stereo", "Left Mixer" },561	{ "Line Out Source Playback Route", "Stereo", "Right Mixer" },562	{ "Line Out Source Playback Route", "Mono Differential", "Left Mixer" },563	{ "Line Out Source Playback Route", "Mono Differential", "Right Mixer" },564	{ "LINEOUT", NULL, "Line Out Source Playback Route" },565	{ "LINEOUT", NULL, "Line Out Enable", },566};567 568static int sun8i_codec_add_lineout(struct snd_soc_component *cmpnt)569{570	struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt);571	struct device *dev = cmpnt->dev;572	int ret;573 574	ret = snd_soc_add_component_controls(cmpnt,575					     sun8i_codec_lineout_controls,576					     ARRAY_SIZE(sun8i_codec_lineout_controls));577	if (ret) {578		dev_err(dev, "Failed to add Line Out controls: %d\n", ret);579		return ret;580	}581 582	ret = snd_soc_dapm_new_controls(dapm, sun8i_codec_lineout_widgets,583					ARRAY_SIZE(sun8i_codec_lineout_widgets));584	if (ret) {585		dev_err(dev, "Failed to add Line Out DAPM widgets: %d\n", ret);586		return ret;587	}588 589	ret = snd_soc_dapm_add_routes(dapm, sun8i_codec_lineout_routes,590				      ARRAY_SIZE(sun8i_codec_lineout_routes));591	if (ret) {592		dev_err(dev, "Failed to add Line Out DAPM routes: %d\n", ret);593		return ret;594	}595 596	return 0;597}598 599/* mic2 specific controls, widgets and routes */600static const struct snd_kcontrol_new sun8i_codec_mic2_controls[] = {601	/* Mixer pre-gain */602	SOC_SINGLE_TLV("Mic2 Playback Volume",603		       SUN8I_ADDA_MICIN_GCTRL, SUN8I_ADDA_MICIN_GCTRL_MIC2G,604		       0x7, 0, sun8i_codec_out_mixer_pregain_scale),605 606	/* Microphone Amp boost gain */607	SOC_SINGLE_TLV("Mic2 Boost Volume", SUN8I_ADDA_MIC2G_CTRL,608		       SUN8I_ADDA_MIC2G_CTRL_MIC2BOOST, 0x7, 0,609		       sun8i_codec_mic_gain_scale),610};611 612static const struct snd_soc_dapm_widget sun8i_codec_mic2_widgets[] = {613	/* Microphone input */614	SND_SOC_DAPM_INPUT("MIC2"),615 616	/* Mic input path */617	SND_SOC_DAPM_PGA("Mic2 Amplifier", SUN8I_ADDA_MIC2G_CTRL,618			 SUN8I_ADDA_MIC2G_CTRL_MIC2AMPEN, 0, NULL, 0),619};620 621static const struct snd_soc_dapm_route sun8i_codec_mic2_routes[] = {622	{ "Mic2 Amplifier", NULL, "MIC2"},623 624	{ "Left Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" },625 626	{ "Right Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" },627 628	{ "Left ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" },629 630	{ "Right ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" },631};632 633static int sun8i_codec_add_mic2(struct snd_soc_component *cmpnt)634{635	struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt);636	struct device *dev = cmpnt->dev;637	int ret;638 639	ret = snd_soc_add_component_controls(cmpnt,640					     sun8i_codec_mic2_controls,641					     ARRAY_SIZE(sun8i_codec_mic2_controls));642	if (ret) {643		dev_err(dev, "Failed to add MIC2 controls: %d\n", ret);644		return ret;645	}646 647	ret = snd_soc_dapm_new_controls(dapm, sun8i_codec_mic2_widgets,648					ARRAY_SIZE(sun8i_codec_mic2_widgets));649	if (ret) {650		dev_err(dev, "Failed to add MIC2 DAPM widgets: %d\n", ret);651		return ret;652	}653 654	ret = snd_soc_dapm_add_routes(dapm, sun8i_codec_mic2_routes,655				      ARRAY_SIZE(sun8i_codec_mic2_routes));656	if (ret) {657		dev_err(dev, "Failed to add MIC2 DAPM routes: %d\n", ret);658		return ret;659	}660 661	return 0;662}663 664struct sun8i_codec_analog_quirks {665	bool has_headphone;666	bool has_hmic;667	bool has_linein;668	bool has_lineout;669	bool has_mbias;670	bool has_mic2;671};672 673static const struct sun8i_codec_analog_quirks sun8i_a23_quirks = {674	.has_headphone	= true,675	.has_hmic	= true,676	.has_linein	= true,677	.has_mbias	= true,678	.has_mic2	= true,679};680 681static const struct sun8i_codec_analog_quirks sun8i_h3_quirks = {682	.has_linein	= true,683	.has_lineout	= true,684	.has_mbias	= true,685	.has_mic2	= true,686};687 688static int sun8i_codec_analog_add_mixer(struct snd_soc_component *cmpnt,689					const struct sun8i_codec_analog_quirks *quirks)690{691	struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt);692	struct device *dev = cmpnt->dev;693	int ret;694 695	if (!quirks->has_mic2 && !quirks->has_linein) {696		/*697		 * Apply the special widget set which has uses a control698		 * without MIC2 and Line In, for SoCs without these.699		 * TODO: not all special cases are supported now, this case700		 * is present because it's the case of V3s.701		 */702		ret = snd_soc_dapm_new_controls(dapm,703						sun8i_v3s_codec_mixer_widgets,704						ARRAY_SIZE(sun8i_v3s_codec_mixer_widgets));705		if (ret) {706			dev_err(dev, "Failed to add V3s Mixer DAPM widgets: %d\n", ret);707			return ret;708		}709	} else {710		/* Apply the generic mixer widget set. */711		ret = snd_soc_dapm_new_controls(dapm,712						sun8i_codec_mixer_widgets,713						ARRAY_SIZE(sun8i_codec_mixer_widgets));714		if (ret) {715			dev_err(dev, "Failed to add Mixer DAPM widgets: %d\n", ret);716			return ret;717		}718	}719 720	ret = snd_soc_dapm_add_routes(dapm, sun8i_codec_mixer_routes,721				      ARRAY_SIZE(sun8i_codec_mixer_routes));722	if (ret) {723		dev_err(dev, "Failed to add Mixer DAPM routes: %d\n", ret);724		return ret;725	}726 727	return 0;728}729 730static const struct sun8i_codec_analog_quirks sun8i_v3s_quirks = {731	.has_headphone	= true,732	.has_hmic	= true,733};734 735static int sun8i_codec_analog_cmpnt_probe(struct snd_soc_component *cmpnt)736{737	struct device *dev = cmpnt->dev;738	const struct sun8i_codec_analog_quirks *quirks;739	int ret;740 741	/*742	 * This would never return NULL unless someone directly registers a743	 * platform device matching this driver's name, without specifying a744	 * device tree node.745	 */746	quirks = of_device_get_match_data(dev);747 748	/* Add controls, widgets, and routes for individual features */749	ret = sun8i_codec_analog_add_mixer(cmpnt, quirks);750	if (ret)751		return ret;752 753	if (quirks->has_headphone) {754		ret = sun8i_codec_add_headphone(cmpnt);755		if (ret)756			return ret;757	}758 759	if (quirks->has_hmic) {760		ret = sun8i_codec_add_hmic(cmpnt);761		if (ret)762			return ret;763	}764 765	if (quirks->has_linein) {766		ret = sun8i_codec_add_linein(cmpnt);767		if (ret)768			return ret;769	}770 771	if (quirks->has_lineout) {772		ret = sun8i_codec_add_lineout(cmpnt);773		if (ret)774			return ret;775	}776 777	if (quirks->has_mbias) {778		ret = sun8i_codec_add_mbias(cmpnt);779		if (ret)780			return ret;781	}782 783	if (quirks->has_mic2) {784		ret = sun8i_codec_add_mic2(cmpnt);785		if (ret)786			return ret;787	}788 789	return 0;790}791 792static const struct snd_soc_component_driver sun8i_codec_analog_cmpnt_drv = {793	.controls		= sun8i_codec_common_controls,794	.num_controls		= ARRAY_SIZE(sun8i_codec_common_controls),795	.dapm_widgets		= sun8i_codec_common_widgets,796	.num_dapm_widgets	= ARRAY_SIZE(sun8i_codec_common_widgets),797	.dapm_routes		= sun8i_codec_common_routes,798	.num_dapm_routes	= ARRAY_SIZE(sun8i_codec_common_routes),799	.probe			= sun8i_codec_analog_cmpnt_probe,800};801 802static const struct of_device_id sun8i_codec_analog_of_match[] = {803	{804		.compatible = "allwinner,sun8i-a23-codec-analog",805		.data = &sun8i_a23_quirks,806	},807	{808		.compatible = "allwinner,sun8i-h3-codec-analog",809		.data = &sun8i_h3_quirks,810	},811	{812		.compatible = "allwinner,sun8i-v3s-codec-analog",813		.data = &sun8i_v3s_quirks,814	},815	{}816};817MODULE_DEVICE_TABLE(of, sun8i_codec_analog_of_match);818 819static int sun8i_codec_analog_probe(struct platform_device *pdev)820{821	struct regmap *regmap;822	void __iomem *base;823 824	base = devm_platform_ioremap_resource(pdev, 0);825	if (IS_ERR(base)) {826		dev_err(&pdev->dev, "Failed to map the registers\n");827		return PTR_ERR(base);828	}829 830	regmap = sun8i_adda_pr_regmap_init(&pdev->dev, base);831	if (IS_ERR(regmap)) {832		dev_err(&pdev->dev, "Failed to create regmap\n");833		return PTR_ERR(regmap);834	}835 836	return devm_snd_soc_register_component(&pdev->dev,837					       &sun8i_codec_analog_cmpnt_drv,838					       NULL, 0);839}840 841static struct platform_driver sun8i_codec_analog_driver = {842	.driver = {843		.name = "sun8i-codec-analog",844		.of_match_table = sun8i_codec_analog_of_match,845	},846	.probe = sun8i_codec_analog_probe,847};848module_platform_driver(sun8i_codec_analog_driver);849 850MODULE_DESCRIPTION("Allwinner internal codec analog controls driver");851MODULE_AUTHOR("Chen-Yu Tsai <wens@csie.org>");852MODULE_LICENSE("GPL");853MODULE_ALIAS("platform:sun8i-codec-analog");854