brintos

brintos / linux-shallow public Read only

0
0
Text · 15.1 KiB · 662e458 Raw
570 lines · c
1// SPDX-License-Identifier: GPL-2.02//3// Socionext UniPhier EVEA ADC/DAC codec driver.4//5// Copyright (c) 2016-2017 Socionext Inc.6 7#include <linux/clk.h>8#include <linux/module.h>9#include <linux/of.h>10#include <linux/regmap.h>11#include <linux/reset.h>12#include <sound/pcm.h>13#include <sound/soc.h>14 15#define DRV_NAME        "evea"16#define EVEA_RATES      SNDRV_PCM_RATE_4800017#define EVEA_FORMATS    SNDRV_PCM_FMTBIT_S32_LE18 19#define AADCPOW(n)                           (0x0078 + 0x04 * (n))20#define   AADCPOW_AADC_POWD                   BIT(0)21#define ALINSW1                              0x008822#define   ALINSW1_SEL1_SHIFT                  323#define AHPOUTPOW                            0x009824#define   AHPOUTPOW_HP_ON                     BIT(4)25#define ALINEPOW                             0x009c26#define   ALINEPOW_LIN2_POWD                  BIT(3)27#define   ALINEPOW_LIN1_POWD                  BIT(4)28#define ALO1OUTPOW                           0x00a829#define   ALO1OUTPOW_LO1_ON                   BIT(4)30#define ALO2OUTPOW                           0x00ac31#define   ALO2OUTPOW_ADAC2_MUTE               BIT(0)32#define   ALO2OUTPOW_LO2_ON                   BIT(4)33#define AANAPOW                              0x00b834#define   AANAPOW_A_POWD                      BIT(4)35#define ADACSEQ1(n)                          (0x0144 + 0x40 * (n))36#define   ADACSEQ1_MMUTE                      BIT(1)37#define ADACSEQ2(n)                          (0x0160 + 0x40 * (n))38#define   ADACSEQ2_ADACIN_FIX                 BIT(0)39#define ADAC1ODC                             0x020040#define   ADAC1ODC_HP_DIS_RES_MASK            GENMASK(2, 1)41#define   ADAC1ODC_HP_DIS_RES_OFF             (0x0 << 1)42#define   ADAC1ODC_HP_DIS_RES_ON              (0x3 << 1)43#define   ADAC1ODC_ADAC_RAMPCLT_MASK          GENMASK(8, 7)44#define   ADAC1ODC_ADAC_RAMPCLT_NORMAL        (0x0 << 7)45#define   ADAC1ODC_ADAC_RAMPCLT_REDUCE        (0x1 << 7)46 47struct evea_priv {48	struct clk *clk, *clk_exiv;49	struct reset_control *rst, *rst_exiv, *rst_adamv;50	struct regmap *regmap;51 52	int switch_lin;53	int switch_lo;54	int switch_hp;55};56 57static const char * const linsw1_sel1_text[] = {58	"LIN1", "LIN2", "LIN3"59};60 61static SOC_ENUM_SINGLE_DECL(linsw1_sel1_enum,62	ALINSW1, ALINSW1_SEL1_SHIFT,63	linsw1_sel1_text);64 65static const struct snd_kcontrol_new linesw1_mux[] = {66	SOC_DAPM_ENUM("Line In 1 Source", linsw1_sel1_enum),67};68 69static const struct snd_soc_dapm_widget evea_widgets[] = {70	SND_SOC_DAPM_ADC("ADC", NULL, SND_SOC_NOPM, 0, 0),71	SND_SOC_DAPM_MUX("Line In 1 Mux", SND_SOC_NOPM, 0, 0, linesw1_mux),72	SND_SOC_DAPM_INPUT("LIN1_LP"),73	SND_SOC_DAPM_INPUT("LIN1_RP"),74	SND_SOC_DAPM_INPUT("LIN2_LP"),75	SND_SOC_DAPM_INPUT("LIN2_RP"),76	SND_SOC_DAPM_INPUT("LIN3_LP"),77	SND_SOC_DAPM_INPUT("LIN3_RP"),78 79	SND_SOC_DAPM_DAC("DAC HP", NULL, SND_SOC_NOPM, 0, 0),80	SND_SOC_DAPM_DAC("DAC LO1", NULL, SND_SOC_NOPM, 0, 0),81	SND_SOC_DAPM_DAC("DAC LO2", NULL, SND_SOC_NOPM, 0, 0),82	SND_SOC_DAPM_OUTPUT("HP1_L"),83	SND_SOC_DAPM_OUTPUT("HP1_R"),84	SND_SOC_DAPM_OUTPUT("LO2_L"),85	SND_SOC_DAPM_OUTPUT("LO2_R"),86};87 88static const struct snd_soc_dapm_route evea_routes[] = {89	{ "Line In 1", NULL, "ADC" },90	{ "ADC", NULL, "Line In 1 Mux" },91	{ "Line In 1 Mux", "LIN1", "LIN1_LP" },92	{ "Line In 1 Mux", "LIN1", "LIN1_RP" },93	{ "Line In 1 Mux", "LIN2", "LIN2_LP" },94	{ "Line In 1 Mux", "LIN2", "LIN2_RP" },95	{ "Line In 1 Mux", "LIN3", "LIN3_LP" },96	{ "Line In 1 Mux", "LIN3", "LIN3_RP" },97 98	{ "DAC HP", NULL, "Headphone 1" },99	{ "DAC LO1", NULL, "Line Out 1" },100	{ "DAC LO2", NULL, "Line Out 2" },101	{ "HP1_L", NULL, "DAC HP" },102	{ "HP1_R", NULL, "DAC HP" },103	{ "LO2_L", NULL, "DAC LO2" },104	{ "LO2_R", NULL, "DAC LO2" },105};106 107static void evea_set_power_state_on(struct evea_priv *evea)108{109	struct regmap *map = evea->regmap;110 111	regmap_update_bits(map, AANAPOW, AANAPOW_A_POWD,112			   AANAPOW_A_POWD);113 114	regmap_update_bits(map, ADAC1ODC, ADAC1ODC_HP_DIS_RES_MASK,115			   ADAC1ODC_HP_DIS_RES_ON);116 117	regmap_update_bits(map, ADAC1ODC, ADAC1ODC_ADAC_RAMPCLT_MASK,118			   ADAC1ODC_ADAC_RAMPCLT_REDUCE);119 120	regmap_update_bits(map, ADACSEQ2(0), ADACSEQ2_ADACIN_FIX, 0);121	regmap_update_bits(map, ADACSEQ2(1), ADACSEQ2_ADACIN_FIX, 0);122	regmap_update_bits(map, ADACSEQ2(2), ADACSEQ2_ADACIN_FIX, 0);123}124 125static void evea_set_power_state_off(struct evea_priv *evea)126{127	struct regmap *map = evea->regmap;128 129	regmap_update_bits(map, ADAC1ODC, ADAC1ODC_HP_DIS_RES_MASK,130			   ADAC1ODC_HP_DIS_RES_ON);131 132	regmap_update_bits(map, ADACSEQ1(0), ADACSEQ1_MMUTE,133			   ADACSEQ1_MMUTE);134	regmap_update_bits(map, ADACSEQ1(1), ADACSEQ1_MMUTE,135			   ADACSEQ1_MMUTE);136	regmap_update_bits(map, ADACSEQ1(2), ADACSEQ1_MMUTE,137			   ADACSEQ1_MMUTE);138 139	regmap_update_bits(map, ALO1OUTPOW, ALO1OUTPOW_LO1_ON, 0);140	regmap_update_bits(map, ALO2OUTPOW, ALO2OUTPOW_LO2_ON, 0);141	regmap_update_bits(map, AHPOUTPOW, AHPOUTPOW_HP_ON, 0);142}143 144static int evea_update_switch_lin(struct evea_priv *evea)145{146	struct regmap *map = evea->regmap;147 148	if (evea->switch_lin) {149		regmap_update_bits(map, ALINEPOW,150				   ALINEPOW_LIN2_POWD | ALINEPOW_LIN1_POWD,151				   ALINEPOW_LIN2_POWD | ALINEPOW_LIN1_POWD);152 153		regmap_update_bits(map, AADCPOW(0), AADCPOW_AADC_POWD,154				   AADCPOW_AADC_POWD);155		regmap_update_bits(map, AADCPOW(1), AADCPOW_AADC_POWD,156				   AADCPOW_AADC_POWD);157	} else {158		regmap_update_bits(map, AADCPOW(0), AADCPOW_AADC_POWD, 0);159		regmap_update_bits(map, AADCPOW(1), AADCPOW_AADC_POWD, 0);160 161		regmap_update_bits(map, ALINEPOW,162				   ALINEPOW_LIN2_POWD | ALINEPOW_LIN1_POWD, 0);163	}164 165	return 0;166}167 168static int evea_update_switch_lo(struct evea_priv *evea)169{170	struct regmap *map = evea->regmap;171 172	if (evea->switch_lo) {173		regmap_update_bits(map, ADACSEQ1(0), ADACSEQ1_MMUTE, 0);174		regmap_update_bits(map, ADACSEQ1(2), ADACSEQ1_MMUTE, 0);175 176		regmap_update_bits(map, ALO1OUTPOW, ALO1OUTPOW_LO1_ON,177				   ALO1OUTPOW_LO1_ON);178		regmap_update_bits(map, ALO2OUTPOW,179				   ALO2OUTPOW_ADAC2_MUTE | ALO2OUTPOW_LO2_ON,180				   ALO2OUTPOW_ADAC2_MUTE | ALO2OUTPOW_LO2_ON);181	} else {182		regmap_update_bits(map, ADACSEQ1(0), ADACSEQ1_MMUTE,183				   ADACSEQ1_MMUTE);184		regmap_update_bits(map, ADACSEQ1(2), ADACSEQ1_MMUTE,185				   ADACSEQ1_MMUTE);186 187		regmap_update_bits(map, ALO1OUTPOW, ALO1OUTPOW_LO1_ON, 0);188		regmap_update_bits(map, ALO2OUTPOW,189				   ALO2OUTPOW_ADAC2_MUTE | ALO2OUTPOW_LO2_ON,190				   0);191	}192 193	return 0;194}195 196static int evea_update_switch_hp(struct evea_priv *evea)197{198	struct regmap *map = evea->regmap;199 200	if (evea->switch_hp) {201		regmap_update_bits(map, ADACSEQ1(1), ADACSEQ1_MMUTE, 0);202 203		regmap_update_bits(map, AHPOUTPOW, AHPOUTPOW_HP_ON,204				   AHPOUTPOW_HP_ON);205 206		regmap_update_bits(map, ADAC1ODC, ADAC1ODC_HP_DIS_RES_MASK,207				   ADAC1ODC_HP_DIS_RES_OFF);208	} else {209		regmap_update_bits(map, ADAC1ODC, ADAC1ODC_HP_DIS_RES_MASK,210				   ADAC1ODC_HP_DIS_RES_ON);211 212		regmap_update_bits(map, ADACSEQ1(1), ADACSEQ1_MMUTE,213				   ADACSEQ1_MMUTE);214 215		regmap_update_bits(map, AHPOUTPOW, AHPOUTPOW_HP_ON, 0);216	}217 218	return 0;219}220 221static void evea_update_switch_all(struct evea_priv *evea)222{223	evea_update_switch_lin(evea);224	evea_update_switch_lo(evea);225	evea_update_switch_hp(evea);226}227 228static int evea_get_switch_lin(struct snd_kcontrol *kcontrol,229			       struct snd_ctl_elem_value *ucontrol)230{231	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);232	struct evea_priv *evea = snd_soc_component_get_drvdata(component);233 234	ucontrol->value.integer.value[0] = evea->switch_lin;235 236	return 0;237}238 239static int evea_set_switch_lin(struct snd_kcontrol *kcontrol,240			       struct snd_ctl_elem_value *ucontrol)241{242	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);243	struct evea_priv *evea = snd_soc_component_get_drvdata(component);244 245	if (evea->switch_lin == ucontrol->value.integer.value[0])246		return 0;247 248	evea->switch_lin = ucontrol->value.integer.value[0];249 250	return evea_update_switch_lin(evea);251}252 253static int evea_get_switch_lo(struct snd_kcontrol *kcontrol,254			      struct snd_ctl_elem_value *ucontrol)255{256	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);257	struct evea_priv *evea = snd_soc_component_get_drvdata(component);258 259	ucontrol->value.integer.value[0] = evea->switch_lo;260 261	return 0;262}263 264static int evea_set_switch_lo(struct snd_kcontrol *kcontrol,265			      struct snd_ctl_elem_value *ucontrol)266{267	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);268	struct evea_priv *evea = snd_soc_component_get_drvdata(component);269 270	if (evea->switch_lo == ucontrol->value.integer.value[0])271		return 0;272 273	evea->switch_lo = ucontrol->value.integer.value[0];274 275	return evea_update_switch_lo(evea);276}277 278static int evea_get_switch_hp(struct snd_kcontrol *kcontrol,279			      struct snd_ctl_elem_value *ucontrol)280{281	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);282	struct evea_priv *evea = snd_soc_component_get_drvdata(component);283 284	ucontrol->value.integer.value[0] = evea->switch_hp;285 286	return 0;287}288 289static int evea_set_switch_hp(struct snd_kcontrol *kcontrol,290			      struct snd_ctl_elem_value *ucontrol)291{292	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);293	struct evea_priv *evea = snd_soc_component_get_drvdata(component);294 295	if (evea->switch_hp == ucontrol->value.integer.value[0])296		return 0;297 298	evea->switch_hp = ucontrol->value.integer.value[0];299 300	return evea_update_switch_hp(evea);301}302 303static const struct snd_kcontrol_new evea_controls[] = {304	SOC_SINGLE_BOOL_EXT("Line Capture Switch", 0,305			    evea_get_switch_lin, evea_set_switch_lin),306	SOC_SINGLE_BOOL_EXT("Line Playback Switch", 0,307			    evea_get_switch_lo, evea_set_switch_lo),308	SOC_SINGLE_BOOL_EXT("Headphone Playback Switch", 0,309			    evea_get_switch_hp, evea_set_switch_hp),310};311 312static int evea_codec_probe(struct snd_soc_component *component)313{314	struct evea_priv *evea = snd_soc_component_get_drvdata(component);315 316	evea->switch_lin = 1;317	evea->switch_lo = 1;318	evea->switch_hp = 1;319 320	evea_set_power_state_on(evea);321	evea_update_switch_all(evea);322 323	return 0;324}325 326static int evea_codec_suspend(struct snd_soc_component *component)327{328	struct evea_priv *evea = snd_soc_component_get_drvdata(component);329 330	evea_set_power_state_off(evea);331 332	reset_control_assert(evea->rst_adamv);333	reset_control_assert(evea->rst_exiv);334	reset_control_assert(evea->rst);335 336	clk_disable_unprepare(evea->clk_exiv);337	clk_disable_unprepare(evea->clk);338 339	return 0;340}341 342static int evea_codec_resume(struct snd_soc_component *component)343{344	struct evea_priv *evea = snd_soc_component_get_drvdata(component);345	int ret;346 347	ret = clk_prepare_enable(evea->clk);348	if (ret)349		return ret;350 351	ret = clk_prepare_enable(evea->clk_exiv);352	if (ret)353		goto err_out_clock;354 355	ret = reset_control_deassert(evea->rst);356	if (ret)357		goto err_out_clock_exiv;358 359	ret = reset_control_deassert(evea->rst_exiv);360	if (ret)361		goto err_out_reset;362 363	ret = reset_control_deassert(evea->rst_adamv);364	if (ret)365		goto err_out_reset_exiv;366 367	evea_set_power_state_on(evea);368	evea_update_switch_all(evea);369 370	return 0;371 372err_out_reset_exiv:373	reset_control_assert(evea->rst_exiv);374 375err_out_reset:376	reset_control_assert(evea->rst);377 378err_out_clock_exiv:379	clk_disable_unprepare(evea->clk_exiv);380 381err_out_clock:382	clk_disable_unprepare(evea->clk);383 384	return ret;385}386 387static struct snd_soc_component_driver soc_codec_evea = {388	.probe			= evea_codec_probe,389	.suspend		= evea_codec_suspend,390	.resume			= evea_codec_resume,391	.dapm_widgets		= evea_widgets,392	.num_dapm_widgets	= ARRAY_SIZE(evea_widgets),393	.dapm_routes		= evea_routes,394	.num_dapm_routes	= ARRAY_SIZE(evea_routes),395	.controls		= evea_controls,396	.num_controls		= ARRAY_SIZE(evea_controls),397	.idle_bias_on		= 1,398	.use_pmdown_time	= 1,399	.endianness		= 1,400};401 402static struct snd_soc_dai_driver soc_dai_evea[] = {403	{404		.name     = DRV_NAME "-line1",405		.playback = {406			.stream_name  = "Line Out 1",407			.formats      = EVEA_FORMATS,408			.rates        = EVEA_RATES,409			.channels_min = 2,410			.channels_max = 2,411		},412		.capture = {413			.stream_name  = "Line In 1",414			.formats      = EVEA_FORMATS,415			.rates        = EVEA_RATES,416			.channels_min = 2,417			.channels_max = 2,418		},419	},420	{421		.name     = DRV_NAME "-hp1",422		.playback = {423			.stream_name  = "Headphone 1",424			.formats      = EVEA_FORMATS,425			.rates        = EVEA_RATES,426			.channels_min = 2,427			.channels_max = 2,428		},429	},430	{431		.name     = DRV_NAME "-lo2",432		.playback = {433			.stream_name  = "Line Out 2",434			.formats      = EVEA_FORMATS,435			.rates        = EVEA_RATES,436			.channels_min = 2,437			.channels_max = 2,438		},439	},440};441 442static const struct regmap_config evea_regmap_config = {443	.reg_bits      = 32,444	.reg_stride    = 4,445	.val_bits      = 32,446	.max_register  = 0xffc,447	.cache_type    = REGCACHE_NONE,448};449 450static int evea_probe(struct platform_device *pdev)451{452	struct evea_priv *evea;453	void __iomem *preg;454	int ret;455 456	evea = devm_kzalloc(&pdev->dev, sizeof(struct evea_priv), GFP_KERNEL);457	if (!evea)458		return -ENOMEM;459 460	evea->clk = devm_clk_get(&pdev->dev, "evea");461	if (IS_ERR(evea->clk))462		return PTR_ERR(evea->clk);463 464	evea->clk_exiv = devm_clk_get(&pdev->dev, "exiv");465	if (IS_ERR(evea->clk_exiv))466		return PTR_ERR(evea->clk_exiv);467 468	evea->rst = devm_reset_control_get_shared(&pdev->dev, "evea");469	if (IS_ERR(evea->rst))470		return PTR_ERR(evea->rst);471 472	evea->rst_exiv = devm_reset_control_get_shared(&pdev->dev, "exiv");473	if (IS_ERR(evea->rst_exiv))474		return PTR_ERR(evea->rst_exiv);475 476	preg = devm_platform_ioremap_resource(pdev, 0);477	if (IS_ERR(preg))478		return PTR_ERR(preg);479 480	evea->regmap = devm_regmap_init_mmio(&pdev->dev, preg,481					     &evea_regmap_config);482	if (IS_ERR(evea->regmap))483		return PTR_ERR(evea->regmap);484 485	ret = clk_prepare_enable(evea->clk);486	if (ret)487		return ret;488 489	ret = clk_prepare_enable(evea->clk_exiv);490	if (ret)491		goto err_out_clock;492 493	ret = reset_control_deassert(evea->rst);494	if (ret)495		goto err_out_clock_exiv;496 497	ret = reset_control_deassert(evea->rst_exiv);498	if (ret)499		goto err_out_reset;500 501	/* ADAMV will hangup if EXIV reset is asserted */502	evea->rst_adamv = devm_reset_control_get_shared(&pdev->dev, "adamv");503	if (IS_ERR(evea->rst_adamv)) {504		ret = PTR_ERR(evea->rst_adamv);505		goto err_out_reset_exiv;506	}507 508	ret = reset_control_deassert(evea->rst_adamv);509	if (ret)510		goto err_out_reset_exiv;511 512	platform_set_drvdata(pdev, evea);513 514	ret = devm_snd_soc_register_component(&pdev->dev, &soc_codec_evea,515				     soc_dai_evea, ARRAY_SIZE(soc_dai_evea));516	if (ret)517		goto err_out_reset_adamv;518 519	return 0;520 521err_out_reset_adamv:522	reset_control_assert(evea->rst_adamv);523 524err_out_reset_exiv:525	reset_control_assert(evea->rst_exiv);526 527err_out_reset:528	reset_control_assert(evea->rst);529 530err_out_clock_exiv:531	clk_disable_unprepare(evea->clk_exiv);532 533err_out_clock:534	clk_disable_unprepare(evea->clk);535 536	return ret;537}538 539static void evea_remove(struct platform_device *pdev)540{541	struct evea_priv *evea = platform_get_drvdata(pdev);542 543	reset_control_assert(evea->rst_adamv);544	reset_control_assert(evea->rst_exiv);545	reset_control_assert(evea->rst);546 547	clk_disable_unprepare(evea->clk_exiv);548	clk_disable_unprepare(evea->clk);549}550 551static const struct of_device_id evea_of_match[] __maybe_unused = {552	{ .compatible = "socionext,uniphier-evea", },553	{}554};555MODULE_DEVICE_TABLE(of, evea_of_match);556 557static struct platform_driver evea_codec_driver = {558	.driver = {559		.name = DRV_NAME,560		.of_match_table = of_match_ptr(evea_of_match),561	},562	.probe  = evea_probe,563	.remove = evea_remove,564};565module_platform_driver(evea_codec_driver);566 567MODULE_AUTHOR("Katsuhiro Suzuki <suzuki.katsuhiro@socionext.com>");568MODULE_DESCRIPTION("UniPhier EVEA codec driver");569MODULE_LICENSE("GPL v2");570