brintos

brintos / linux-shallow public Read only

0
0
Text · 11.1 KiB · 8c66656 Raw
449 lines · c
1// SPDX-License-Identifier: GPL-2.0-only OR MIT2//3// Analog Devices' SSM3515 audio amp driver4//5// Copyright (C) The Asahi Linux Contributors6 7#include <linux/bits.h>8#include <linux/bitfield.h>9#include <linux/device.h>10#include <linux/i2c.h>11#include <linux/module.h>12#include <linux/of.h>13#include <linux/regmap.h>14 15#include <sound/pcm.h>16#include <sound/pcm_params.h>17#include <sound/soc.h>18#include <sound/tlv.h>19 20 21#define SSM3515_PWR		0x0022#define SSM3515_PWR_APWDN_EN	BIT(7)23#define SSM3515_PWR_BSNS_PWDN	BIT(6)24#define SSM3515_PWR_S_RST	BIT(1)25#define SSM3515_PWR_SPWDN	BIT(0)26 27#define SSM3515_GEC		0x0128#define SSM3515_GEC_EDGE	BIT(4)29#define SSM3515_GEC_EDGE_SHIFT	430#define SSM3515_GEC_ANA_GAIN	GENMASK(1, 0)31 32#define SSM3515_DAC		0x0233#define SSM3515_DAC_HV		BIT(7)34#define SSM3515_DAC_MUTE	BIT(6)35#define SSM3515_DAC_HPF		BIT(5)36#define SSM3515_DAC_LPM		BIT(4)37#define SSM3515_DAC_FS		GENMASK(2, 0)38 39#define SSM3515_DAC_VOL		0x0340 41#define SSM3515_SAI1		0x0442#define SSM3515_SAI1_DAC_POL	BIT(7)43#define SSM3515_SAI1_BCLK_POL	BIT(6)44#define SSM3515_SAI1_TDM_BCLKS	GENMASK(5, 3)45#define SSM3515_SAI1_FSYNC_MODE	BIT(2)46#define SSM3515_SAI1_SDATA_FMT	BIT(1)47#define SSM3515_SAI1_SAI_MODE	BIT(0)48 49#define SSM3515_SAI2		0x0550#define SSM3515_SAI2_DATA_WIDTH	BIT(7)51#define SSM3515_SAI2_AUTO_SLOT	BIT(4)52#define SSM3515_SAI2_TDM_SLOT	GENMASK(3, 0)53 54#define SSM3515_VBAT_OUT	0x0655 56#define SSM3515_STATUS		0x0a57#define SSM3515_STATUS_UVLO_REG	BIT(6)58#define SSM3515_STATUS_LIM_EG	BIT(5)59#define SSM3515_STATUS_CLIP	BIT(4)60#define SSM3515_STATUS_AMP_OC	BIT(3)61#define SSM3515_STATUS_OTF	BIT(2)62#define SSM3515_STATUS_OTW	BIT(1)63#define SSM3515_STATUS_BAT_WARN	BIT(0)64 65static bool ssm3515_volatile_reg(struct device *dev, unsigned int reg)66{67	switch (reg) {68	case SSM3515_STATUS:69	case SSM3515_VBAT_OUT:70		return true;71 72	default:73		return false;74	}75}76 77static const struct reg_default ssm3515_reg_defaults[] = {78	{ SSM3515_PWR, 0x81 },79	{ SSM3515_GEC, 0x01 },80	{ SSM3515_DAC, 0x32 },81	{ SSM3515_DAC_VOL, 0x40 },82	{ SSM3515_SAI1, 0x11 },83	{ SSM3515_SAI2, 0x00 },84};85 86static const struct regmap_config ssm3515_i2c_regmap = {87	.reg_bits = 8,88	.val_bits = 8,89	.volatile_reg = ssm3515_volatile_reg,90	.max_register = 0xb,91	.reg_defaults = ssm3515_reg_defaults,92	.num_reg_defaults = ARRAY_SIZE(ssm3515_reg_defaults),93	.cache_type = REGCACHE_FLAT,94};95 96struct ssm3515_data {97	struct device *dev;98	struct regmap *regmap;99};100 101// The specced range is -71.25...24.00 dB with step size of 0.375 dB,102// and a mute item below that. This is represented by -71.62...24.00 dB103// with the mute item mapped onto the low end.104static DECLARE_TLV_DB_MINMAX_MUTE(ssm3515_dac_volume, -7162, 2400);105 106static const char * const ssm3515_ana_gain_text[] = {107	"8.4 V Span", "12.6 V Span", "14 V Span", "15 V Span",108};109 110static SOC_ENUM_SINGLE_DECL(ssm3515_ana_gain_enum, SSM3515_GEC,111			    __bf_shf(SSM3515_GEC_ANA_GAIN),112			    ssm3515_ana_gain_text);113 114static const struct snd_kcontrol_new ssm3515_snd_controls[] = {115	SOC_SINGLE_TLV("DAC Playback Volume", SSM3515_DAC_VOL,116		       0, 255, 1, ssm3515_dac_volume),117	SOC_SINGLE("Low EMI Mode Switch", SSM3515_GEC,118		   __bf_shf(SSM3515_GEC_EDGE), 1, 0),119	SOC_SINGLE("Soft Volume Ramping Switch", SSM3515_DAC,120		   __bf_shf(SSM3515_DAC_HV), 1, 1),121	SOC_SINGLE("HPF Switch", SSM3515_DAC,122		   __bf_shf(SSM3515_DAC_HPF), 1, 0),123	SOC_SINGLE("DAC Invert Switch", SSM3515_SAI1,124		   __bf_shf(SSM3515_SAI1_DAC_POL), 1, 0),125	SOC_ENUM("DAC Analog Gain Select", ssm3515_ana_gain_enum),126};127 128static void ssm3515_read_faults(struct snd_soc_component *component)129{130	int ret;131 132	ret = snd_soc_component_read(component, SSM3515_STATUS);133	if (ret <= 0) {134		/*135		 * If the read was erroneous, ASoC core has printed a message,136		 * and that's all that's appropriate in handling the error here.137		 */138		return;139	}140 141	dev_err(component->dev, "device reports:%s%s%s%s%s%s%s\n",142		FIELD_GET(SSM3515_STATUS_UVLO_REG, ret) ? " voltage regulator fault" : "",143		FIELD_GET(SSM3515_STATUS_LIM_EG, ret)   ? " limiter engaged" : "",144		FIELD_GET(SSM3515_STATUS_CLIP, ret)     ? " clipping detected" : "",145		FIELD_GET(SSM3515_STATUS_AMP_OC, ret)   ? " amp over-current fault" : "",146		FIELD_GET(SSM3515_STATUS_OTF, ret)      ? " overtemperature fault" : "",147		FIELD_GET(SSM3515_STATUS_OTW, ret)      ? " overtemperature warning" : "",148		FIELD_GET(SSM3515_STATUS_BAT_WARN, ret) ? " bat voltage low warning" : "");149}150 151static int ssm3515_probe(struct snd_soc_component *component)152{153	int ret;154 155	/* Start out muted */156	ret = snd_soc_component_update_bits(component, SSM3515_DAC,157			SSM3515_DAC_MUTE, SSM3515_DAC_MUTE);158	if (ret < 0)159		return ret;160 161	/* Disable the 'master power-down' */162	ret = snd_soc_component_update_bits(component, SSM3515_PWR,163			SSM3515_PWR_SPWDN, 0);164	if (ret < 0)165		return ret;166 167	return 0;168}169 170static int ssm3515_mute(struct snd_soc_dai *dai, int mute, int direction)171{172	int ret;173 174	ret = snd_soc_component_update_bits(dai->component,175					    SSM3515_DAC,176					    SSM3515_DAC_MUTE,177					    FIELD_PREP(SSM3515_DAC_MUTE, mute));178	if (ret < 0)179		return ret;180	return 0;181}182 183static int ssm3515_hw_params(struct snd_pcm_substream *substream,184			     struct snd_pcm_hw_params *params,185			     struct snd_soc_dai *dai)186{187	struct snd_soc_component *component = dai->component;188	int ret, rateval;189 190	switch (params_format(params)) {191	case SNDRV_PCM_FORMAT_S16:192	case SNDRV_PCM_FORMAT_S24:193		ret = snd_soc_component_update_bits(component,194				SSM3515_SAI2, SSM3515_SAI2_DATA_WIDTH,195				FIELD_PREP(SSM3515_SAI2_DATA_WIDTH,196					   params_width(params) == 16));197		if (ret < 0)198			return ret;199		break;200 201	default:202		return -EINVAL;203	}204 205	switch (params_rate(params)) {206	case 8000 ... 12000:207		rateval = 0;208		break;209	case 16000 ... 24000:210		rateval = 1;211		break;212	case 32000 ... 48000:213		rateval = 2;214		break;215	case 64000 ... 96000:216		rateval = 3;217		break;218	case 128000 ... 192000:219		rateval = 4;220		break;221	case 48001 ... 63999: /* this is ...72000 but overlaps */222		rateval = 5;223		break;224	default:225		return -EINVAL;226	}227 228	ret = snd_soc_component_update_bits(component,229			SSM3515_DAC, SSM3515_DAC_FS,230			FIELD_PREP(SSM3515_DAC_FS, rateval));231	if (ret < 0)232		return ret;233 234	return 0;235}236 237static int ssm3515_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)238{239	struct snd_soc_component *component = dai->component;240	bool fpol_inv = false; /* non-inverted: frame starts with low-to-high FSYNC */241	int ret;242	u8 sai1 = 0;243 244	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {245	case SND_SOC_DAIFMT_IB_NF:246	case SND_SOC_DAIFMT_IB_IF:247		sai1 |= SSM3515_SAI1_BCLK_POL;248		break;249	}250 251	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {252	case SND_SOC_DAIFMT_I2S:253		fpol_inv = 1;254		sai1 &= ~SSM3515_SAI1_SDATA_FMT; /* 1 bit start delay */255		break;256	case SND_SOC_DAIFMT_LEFT_J:257		fpol_inv = 0;258		sai1 |= SSM3515_SAI1_SDATA_FMT; /* no start delay */259		break;260	default:261		return -EINVAL;262	}263 264	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {265	case SND_SOC_DAIFMT_NB_IF:266	case SND_SOC_DAIFMT_IB_IF:267		fpol_inv ^= 1;268		break;269	}270 271	/* Set the serial input to 'TDM mode' */272	sai1 |= SSM3515_SAI1_SAI_MODE;273 274	if (fpol_inv) {275		/*276		 * We configure the codec in a 'TDM mode', in which the277		 * FSYNC_MODE bit of SAI1 is supposed to select between278		 * what the datasheet calls 'Pulsed FSYNC mode' and '50%279		 * FSYNC mode'.280		 *281		 * Experiments suggest that this bit in fact simply selects282		 * the FSYNC polarity, so go with that.283		 */284		sai1 |= SSM3515_SAI1_FSYNC_MODE;285	}286 287	ret = snd_soc_component_update_bits(component, SSM3515_SAI1,288			SSM3515_SAI1_BCLK_POL | SSM3515_SAI1_SDATA_FMT |289			SSM3515_SAI1_SAI_MODE | SSM3515_SAI1_FSYNC_MODE, sai1);290 291	if (ret < 0)292		return ret;293	return 0;294}295 296static int ssm3515_set_tdm_slot(struct snd_soc_dai *dai,297				unsigned int tx_mask,298				unsigned int rx_mask,299				int slots, int slot_width)300{301	struct snd_soc_component *component = dai->component;302	int slot, tdm_bclks_val, ret;303 304	if (tx_mask == 0 || rx_mask != 0)305		return -EINVAL;306 307	slot = __ffs(tx_mask);308 309	if (tx_mask & ~BIT(slot))310		return -EINVAL;311 312	switch (slot_width) {313	case 16:314		tdm_bclks_val = 0;315		break;316	case 24:317		tdm_bclks_val = 1;318		break;319	case 32:320		tdm_bclks_val = 2;321		break;322	case 48:323		tdm_bclks_val = 3;324		break;325	case 64:326		tdm_bclks_val = 4;327		break;328	default:329		return -EINVAL;330	}331 332	ret = snd_soc_component_update_bits(component, SSM3515_SAI1,333			SSM3515_SAI1_TDM_BCLKS,334			FIELD_PREP(SSM3515_SAI1_TDM_BCLKS, tdm_bclks_val));335	if (ret < 0)336		return ret;337 338	ret = snd_soc_component_update_bits(component, SSM3515_SAI2,339			SSM3515_SAI2_TDM_SLOT,340			FIELD_PREP(SSM3515_SAI2_TDM_SLOT, slot));341	if (ret < 0)342		return ret;343 344	return 0;345}346 347static int ssm3515_hw_free(struct snd_pcm_substream *substream,348			   struct snd_soc_dai *dai)349{350	/*351	 * We don't get live notification of faults, so at least at352	 * this time, when playback is over, check if we have tripped353	 * over anything and if so, log it.354	 */355	ssm3515_read_faults(dai->component);356	return 0;357}358 359static const struct snd_soc_dai_ops ssm3515_dai_ops = {360	.mute_stream	= ssm3515_mute,361	.hw_params	= ssm3515_hw_params,362	.set_fmt	= ssm3515_set_fmt,363	.set_tdm_slot	= ssm3515_set_tdm_slot,364	.hw_free	= ssm3515_hw_free,365};366 367static struct snd_soc_dai_driver ssm3515_dai_driver = {368	.name = "SSM3515 SAI",369	.id = 0,370	.playback = {371		.stream_name = "Playback",372		.channels_min = 1,373		.channels_max = 1,374		.rates = SNDRV_PCM_RATE_CONTINUOUS,375		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,376	},377	.ops = &ssm3515_dai_ops,378};379 380static const struct snd_soc_dapm_widget ssm3515_dapm_widgets[] = {381	SND_SOC_DAPM_DAC("DAC", NULL, SND_SOC_NOPM, 0, 0),382	SND_SOC_DAPM_OUTPUT("OUT"),383};384 385static const struct snd_soc_dapm_route ssm3515_dapm_routes[] = {386	{"OUT", NULL, "DAC"},387	{"DAC", NULL, "Playback"},388};389 390static const struct snd_soc_component_driver ssm3515_asoc_component = {391	.probe = ssm3515_probe,392	.controls = ssm3515_snd_controls,393	.num_controls = ARRAY_SIZE(ssm3515_snd_controls),394	.dapm_widgets = ssm3515_dapm_widgets,395	.num_dapm_widgets = ARRAY_SIZE(ssm3515_dapm_widgets),396	.dapm_routes = ssm3515_dapm_routes,397	.num_dapm_routes = ARRAY_SIZE(ssm3515_dapm_routes),398	.endianness = 1,399};400 401static int ssm3515_i2c_probe(struct i2c_client *client)402{403	struct ssm3515_data *data;404	int ret;405 406	data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);407	if (!data)408		return -ENOMEM;409 410	data->dev = &client->dev;411	i2c_set_clientdata(client, data);412 413	data->regmap = devm_regmap_init_i2c(client, &ssm3515_i2c_regmap);414	if (IS_ERR(data->regmap))415		return dev_err_probe(data->dev, PTR_ERR(data->regmap),416				     "initializing register map\n");417 418	/* Perform a reset */419	ret = regmap_update_bits(data->regmap, SSM3515_PWR,420			SSM3515_PWR_S_RST, SSM3515_PWR_S_RST);421	if (ret < 0)422		return dev_err_probe(data->dev, ret,423				     "performing software reset\n");424	regmap_reinit_cache(data->regmap, &ssm3515_i2c_regmap);425 426	return devm_snd_soc_register_component(data->dev,427			&ssm3515_asoc_component,428			&ssm3515_dai_driver, 1);429}430 431static const struct of_device_id ssm3515_of_match[] = {432	{ .compatible = "adi,ssm3515" },433	{}434};435MODULE_DEVICE_TABLE(of, ssm3515_of_match);436 437static struct i2c_driver ssm3515_i2c_driver = {438	.driver = {439		.name = "ssm3515",440		.of_match_table = ssm3515_of_match,441	},442	.probe = ssm3515_i2c_probe,443};444module_i2c_driver(ssm3515_i2c_driver);445 446MODULE_AUTHOR("Martin Povišer <povik+lin@cutebit.org>");447MODULE_DESCRIPTION("ASoC SSM3515 audio amp driver");448MODULE_LICENSE("Dual MIT/GPL");449