162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * ALSA SoC Texas Instruments TPA6130A2 headset stereo amplifier driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) Nokia Corporation
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <linux/errno.h>
1262306a36Sopenharmony_ci#include <linux/device.h>
1362306a36Sopenharmony_ci#include <linux/i2c.h>
1462306a36Sopenharmony_ci#include <linux/gpio.h>
1562306a36Sopenharmony_ci#include <linux/regulator/consumer.h>
1662306a36Sopenharmony_ci#include <linux/slab.h>
1762306a36Sopenharmony_ci#include <sound/tpa6130a2-plat.h>
1862306a36Sopenharmony_ci#include <sound/soc.h>
1962306a36Sopenharmony_ci#include <sound/tlv.h>
2062306a36Sopenharmony_ci#include <linux/of.h>
2162306a36Sopenharmony_ci#include <linux/of_gpio.h>
2262306a36Sopenharmony_ci#include <linux/regmap.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#include "tpa6130a2.h"
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_cienum tpa_model {
2762306a36Sopenharmony_ci	TPA6130A2,
2862306a36Sopenharmony_ci	TPA6140A2,
2962306a36Sopenharmony_ci};
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci/* This struct is used to save the context */
3262306a36Sopenharmony_cistruct tpa6130a2_data {
3362306a36Sopenharmony_ci	struct device *dev;
3462306a36Sopenharmony_ci	struct regmap *regmap;
3562306a36Sopenharmony_ci	struct regulator *supply;
3662306a36Sopenharmony_ci	int power_gpio;
3762306a36Sopenharmony_ci	enum tpa_model id;
3862306a36Sopenharmony_ci};
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cistatic int tpa6130a2_power(struct tpa6130a2_data *data, bool enable)
4162306a36Sopenharmony_ci{
4262306a36Sopenharmony_ci	int ret = 0, ret2;
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	if (enable) {
4562306a36Sopenharmony_ci		ret = regulator_enable(data->supply);
4662306a36Sopenharmony_ci		if (ret != 0) {
4762306a36Sopenharmony_ci			dev_err(data->dev,
4862306a36Sopenharmony_ci				"Failed to enable supply: %d\n", ret);
4962306a36Sopenharmony_ci			return ret;
5062306a36Sopenharmony_ci		}
5162306a36Sopenharmony_ci		/* Power on */
5262306a36Sopenharmony_ci		if (data->power_gpio >= 0)
5362306a36Sopenharmony_ci			gpio_set_value(data->power_gpio, 1);
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci		/* Sync registers */
5662306a36Sopenharmony_ci		regcache_cache_only(data->regmap, false);
5762306a36Sopenharmony_ci		ret = regcache_sync(data->regmap);
5862306a36Sopenharmony_ci		if (ret != 0) {
5962306a36Sopenharmony_ci			dev_err(data->dev,
6062306a36Sopenharmony_ci				"Failed to sync registers: %d\n", ret);
6162306a36Sopenharmony_ci			regcache_cache_only(data->regmap, true);
6262306a36Sopenharmony_ci			if (data->power_gpio >= 0)
6362306a36Sopenharmony_ci				gpio_set_value(data->power_gpio, 0);
6462306a36Sopenharmony_ci			ret2 = regulator_disable(data->supply);
6562306a36Sopenharmony_ci			if (ret2 != 0)
6662306a36Sopenharmony_ci				dev_err(data->dev,
6762306a36Sopenharmony_ci					"Failed to disable supply: %d\n", ret2);
6862306a36Sopenharmony_ci			return ret;
6962306a36Sopenharmony_ci		}
7062306a36Sopenharmony_ci	} else {
7162306a36Sopenharmony_ci		/* Powered off device does not retain registers. While device
7262306a36Sopenharmony_ci		 * is off, any register updates (i.e. volume changes) should
7362306a36Sopenharmony_ci		 * happen in cache only.
7462306a36Sopenharmony_ci		 */
7562306a36Sopenharmony_ci		regcache_mark_dirty(data->regmap);
7662306a36Sopenharmony_ci		regcache_cache_only(data->regmap, true);
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci		/* Power off */
7962306a36Sopenharmony_ci		if (data->power_gpio >= 0)
8062306a36Sopenharmony_ci			gpio_set_value(data->power_gpio, 0);
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci		ret = regulator_disable(data->supply);
8362306a36Sopenharmony_ci		if (ret != 0) {
8462306a36Sopenharmony_ci			dev_err(data->dev,
8562306a36Sopenharmony_ci				"Failed to disable supply: %d\n", ret);
8662306a36Sopenharmony_ci			return ret;
8762306a36Sopenharmony_ci		}
8862306a36Sopenharmony_ci	}
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	return ret;
9162306a36Sopenharmony_ci}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_cistatic int tpa6130a2_power_event(struct snd_soc_dapm_widget *w,
9462306a36Sopenharmony_ci				 struct snd_kcontrol *kctrl, int event)
9562306a36Sopenharmony_ci{
9662306a36Sopenharmony_ci	struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);
9762306a36Sopenharmony_ci	struct tpa6130a2_data *data = snd_soc_component_get_drvdata(c);
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	if (SND_SOC_DAPM_EVENT_ON(event)) {
10062306a36Sopenharmony_ci		/* Before widget power up: turn chip on, sync registers */
10162306a36Sopenharmony_ci		return tpa6130a2_power(data, true);
10262306a36Sopenharmony_ci	} else {
10362306a36Sopenharmony_ci		/* After widget power down: turn chip off */
10462306a36Sopenharmony_ci		return tpa6130a2_power(data, false);
10562306a36Sopenharmony_ci	}
10662306a36Sopenharmony_ci}
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci/*
10962306a36Sopenharmony_ci * TPA6130 volume. From -59.5 to 4 dB with increasing step size when going
11062306a36Sopenharmony_ci * down in gain.
11162306a36Sopenharmony_ci */
11262306a36Sopenharmony_cistatic const DECLARE_TLV_DB_RANGE(tpa6130_tlv,
11362306a36Sopenharmony_ci	0, 1, TLV_DB_SCALE_ITEM(-5950, 600, 0),
11462306a36Sopenharmony_ci	2, 3, TLV_DB_SCALE_ITEM(-5000, 250, 0),
11562306a36Sopenharmony_ci	4, 5, TLV_DB_SCALE_ITEM(-4550, 160, 0),
11662306a36Sopenharmony_ci	6, 7, TLV_DB_SCALE_ITEM(-4140, 190, 0),
11762306a36Sopenharmony_ci	8, 9, TLV_DB_SCALE_ITEM(-3650, 120, 0),
11862306a36Sopenharmony_ci	10, 11, TLV_DB_SCALE_ITEM(-3330, 160, 0),
11962306a36Sopenharmony_ci	12, 13, TLV_DB_SCALE_ITEM(-3040, 180, 0),
12062306a36Sopenharmony_ci	14, 20, TLV_DB_SCALE_ITEM(-2710, 110, 0),
12162306a36Sopenharmony_ci	21, 37, TLV_DB_SCALE_ITEM(-1960, 74, 0),
12262306a36Sopenharmony_ci	38, 63, TLV_DB_SCALE_ITEM(-720, 45, 0)
12362306a36Sopenharmony_ci);
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_cistatic const struct snd_kcontrol_new tpa6130a2_controls[] = {
12662306a36Sopenharmony_ci	SOC_SINGLE_TLV("Headphone Playback Volume",
12762306a36Sopenharmony_ci		       TPA6130A2_REG_VOL_MUTE, 0, 0x3f, 0,
12862306a36Sopenharmony_ci		       tpa6130_tlv),
12962306a36Sopenharmony_ci};
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_cistatic const DECLARE_TLV_DB_RANGE(tpa6140_tlv,
13262306a36Sopenharmony_ci	0, 8, TLV_DB_SCALE_ITEM(-5900, 400, 0),
13362306a36Sopenharmony_ci	9, 16, TLV_DB_SCALE_ITEM(-2500, 200, 0),
13462306a36Sopenharmony_ci	17, 31, TLV_DB_SCALE_ITEM(-1000, 100, 0)
13562306a36Sopenharmony_ci);
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_cistatic const struct snd_kcontrol_new tpa6140a2_controls[] = {
13862306a36Sopenharmony_ci	SOC_SINGLE_TLV("Headphone Playback Volume",
13962306a36Sopenharmony_ci		       TPA6130A2_REG_VOL_MUTE, 1, 0x1f, 0,
14062306a36Sopenharmony_ci		       tpa6140_tlv),
14162306a36Sopenharmony_ci};
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_cistatic int tpa6130a2_component_probe(struct snd_soc_component *component)
14462306a36Sopenharmony_ci{
14562306a36Sopenharmony_ci	struct tpa6130a2_data *data = snd_soc_component_get_drvdata(component);
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	if (data->id == TPA6140A2)
14862306a36Sopenharmony_ci		return snd_soc_add_component_controls(component,
14962306a36Sopenharmony_ci			tpa6140a2_controls, ARRAY_SIZE(tpa6140a2_controls));
15062306a36Sopenharmony_ci	else
15162306a36Sopenharmony_ci		return snd_soc_add_component_controls(component,
15262306a36Sopenharmony_ci			tpa6130a2_controls, ARRAY_SIZE(tpa6130a2_controls));
15362306a36Sopenharmony_ci}
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_cistatic const struct snd_soc_dapm_widget tpa6130a2_dapm_widgets[] = {
15662306a36Sopenharmony_ci	SND_SOC_DAPM_INPUT("LEFTIN"),
15762306a36Sopenharmony_ci	SND_SOC_DAPM_INPUT("RIGHTIN"),
15862306a36Sopenharmony_ci	SND_SOC_DAPM_OUTPUT("HPLEFT"),
15962306a36Sopenharmony_ci	SND_SOC_DAPM_OUTPUT("HPRIGHT"),
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	SND_SOC_DAPM_PGA("Left Mute", TPA6130A2_REG_VOL_MUTE,
16262306a36Sopenharmony_ci			 TPA6130A2_HP_EN_L_SHIFT, 1, NULL, 0),
16362306a36Sopenharmony_ci	SND_SOC_DAPM_PGA("Right Mute", TPA6130A2_REG_VOL_MUTE,
16462306a36Sopenharmony_ci			 TPA6130A2_HP_EN_R_SHIFT, 1, NULL, 0),
16562306a36Sopenharmony_ci	SND_SOC_DAPM_PGA("Left PGA", TPA6130A2_REG_CONTROL,
16662306a36Sopenharmony_ci			 TPA6130A2_HP_EN_L_SHIFT, 0, NULL, 0),
16762306a36Sopenharmony_ci	SND_SOC_DAPM_PGA("Right PGA", TPA6130A2_REG_CONTROL,
16862306a36Sopenharmony_ci			 TPA6130A2_HP_EN_R_SHIFT, 0, NULL, 0),
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	SND_SOC_DAPM_SUPPLY("Power", TPA6130A2_REG_CONTROL,
17162306a36Sopenharmony_ci			    TPA6130A2_SWS_SHIFT, 1, tpa6130a2_power_event,
17262306a36Sopenharmony_ci			    SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
17362306a36Sopenharmony_ci};
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_cistatic const struct snd_soc_dapm_route tpa6130a2_dapm_routes[] = {
17662306a36Sopenharmony_ci	{ "Left PGA", NULL, "LEFTIN" },
17762306a36Sopenharmony_ci	{ "Right PGA", NULL, "RIGHTIN" },
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	{ "Left Mute", NULL, "Left PGA" },
18062306a36Sopenharmony_ci	{ "Right Mute", NULL, "Right PGA" },
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	{ "HPLEFT", NULL, "Left Mute" },
18362306a36Sopenharmony_ci	{ "HPRIGHT", NULL, "Right Mute" },
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	{ "Left PGA", NULL, "Power" },
18662306a36Sopenharmony_ci	{ "Right PGA", NULL, "Power" },
18762306a36Sopenharmony_ci};
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_cistatic const struct snd_soc_component_driver tpa6130a2_component_driver = {
19062306a36Sopenharmony_ci	.name = "tpa6130a2",
19162306a36Sopenharmony_ci	.probe = tpa6130a2_component_probe,
19262306a36Sopenharmony_ci	.dapm_widgets = tpa6130a2_dapm_widgets,
19362306a36Sopenharmony_ci	.num_dapm_widgets = ARRAY_SIZE(tpa6130a2_dapm_widgets),
19462306a36Sopenharmony_ci	.dapm_routes = tpa6130a2_dapm_routes,
19562306a36Sopenharmony_ci	.num_dapm_routes = ARRAY_SIZE(tpa6130a2_dapm_routes),
19662306a36Sopenharmony_ci};
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_cistatic const struct reg_default tpa6130a2_reg_defaults[] = {
19962306a36Sopenharmony_ci	{ TPA6130A2_REG_CONTROL, TPA6130A2_SWS },
20062306a36Sopenharmony_ci	{ TPA6130A2_REG_VOL_MUTE, TPA6130A2_MUTE_R | TPA6130A2_MUTE_L },
20162306a36Sopenharmony_ci};
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_cistatic const struct regmap_config tpa6130a2_regmap_config = {
20462306a36Sopenharmony_ci	.reg_bits = 8,
20562306a36Sopenharmony_ci	.val_bits = 8,
20662306a36Sopenharmony_ci	.max_register = TPA6130A2_REG_VERSION,
20762306a36Sopenharmony_ci	.reg_defaults = tpa6130a2_reg_defaults,
20862306a36Sopenharmony_ci	.num_reg_defaults = ARRAY_SIZE(tpa6130a2_reg_defaults),
20962306a36Sopenharmony_ci	.cache_type = REGCACHE_RBTREE,
21062306a36Sopenharmony_ci};
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_cistatic const struct i2c_device_id tpa6130a2_id[] = {
21362306a36Sopenharmony_ci	{ "tpa6130a2", TPA6130A2 },
21462306a36Sopenharmony_ci	{ "tpa6140a2", TPA6140A2 },
21562306a36Sopenharmony_ci	{ }
21662306a36Sopenharmony_ci};
21762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, tpa6130a2_id);
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_cistatic int tpa6130a2_probe(struct i2c_client *client)
22062306a36Sopenharmony_ci{
22162306a36Sopenharmony_ci	struct device *dev;
22262306a36Sopenharmony_ci	struct tpa6130a2_data *data;
22362306a36Sopenharmony_ci	struct tpa6130a2_platform_data *pdata = client->dev.platform_data;
22462306a36Sopenharmony_ci	struct device_node *np = client->dev.of_node;
22562306a36Sopenharmony_ci	const struct i2c_device_id *id;
22662306a36Sopenharmony_ci	const char *regulator;
22762306a36Sopenharmony_ci	unsigned int version;
22862306a36Sopenharmony_ci	int ret;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	dev = &client->dev;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
23362306a36Sopenharmony_ci	if (!data)
23462306a36Sopenharmony_ci		return -ENOMEM;
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	data->dev = dev;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	data->regmap = devm_regmap_init_i2c(client, &tpa6130a2_regmap_config);
23962306a36Sopenharmony_ci	if (IS_ERR(data->regmap))
24062306a36Sopenharmony_ci		return PTR_ERR(data->regmap);
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	if (pdata) {
24362306a36Sopenharmony_ci		data->power_gpio = pdata->power_gpio;
24462306a36Sopenharmony_ci	} else if (np) {
24562306a36Sopenharmony_ci		data->power_gpio = of_get_named_gpio(np, "power-gpio", 0);
24662306a36Sopenharmony_ci	} else {
24762306a36Sopenharmony_ci		dev_err(dev, "Platform data not set\n");
24862306a36Sopenharmony_ci		dump_stack();
24962306a36Sopenharmony_ci		return -ENODEV;
25062306a36Sopenharmony_ci	}
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	i2c_set_clientdata(client, data);
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	id = i2c_match_id(tpa6130a2_id, client);
25562306a36Sopenharmony_ci	data->id = id->driver_data;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	if (data->power_gpio >= 0) {
25862306a36Sopenharmony_ci		ret = devm_gpio_request(dev, data->power_gpio,
25962306a36Sopenharmony_ci					"tpa6130a2 enable");
26062306a36Sopenharmony_ci		if (ret < 0) {
26162306a36Sopenharmony_ci			dev_err(dev, "Failed to request power GPIO (%d)\n",
26262306a36Sopenharmony_ci				data->power_gpio);
26362306a36Sopenharmony_ci			return ret;
26462306a36Sopenharmony_ci		}
26562306a36Sopenharmony_ci		gpio_direction_output(data->power_gpio, 0);
26662306a36Sopenharmony_ci	}
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	switch (data->id) {
26962306a36Sopenharmony_ci	default:
27062306a36Sopenharmony_ci		dev_warn(dev, "Unknown TPA model (%d). Assuming 6130A2\n",
27162306a36Sopenharmony_ci			 data->id);
27262306a36Sopenharmony_ci		fallthrough;
27362306a36Sopenharmony_ci	case TPA6130A2:
27462306a36Sopenharmony_ci		regulator = "Vdd";
27562306a36Sopenharmony_ci		break;
27662306a36Sopenharmony_ci	case TPA6140A2:
27762306a36Sopenharmony_ci		regulator = "AVdd";
27862306a36Sopenharmony_ci		break;
27962306a36Sopenharmony_ci	}
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	data->supply = devm_regulator_get(dev, regulator);
28262306a36Sopenharmony_ci	if (IS_ERR(data->supply)) {
28362306a36Sopenharmony_ci		ret = PTR_ERR(data->supply);
28462306a36Sopenharmony_ci		dev_err(dev, "Failed to request supply: %d\n", ret);
28562306a36Sopenharmony_ci		return ret;
28662306a36Sopenharmony_ci	}
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	ret = tpa6130a2_power(data, true);
28962306a36Sopenharmony_ci	if (ret != 0)
29062306a36Sopenharmony_ci		return ret;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	/* Read version */
29462306a36Sopenharmony_ci	regmap_read(data->regmap, TPA6130A2_REG_VERSION, &version);
29562306a36Sopenharmony_ci	version &= TPA6130A2_VERSION_MASK;
29662306a36Sopenharmony_ci	if ((version != 1) && (version != 2))
29762306a36Sopenharmony_ci		dev_warn(dev, "UNTESTED version detected (%d)\n", version);
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	/* Disable the chip */
30062306a36Sopenharmony_ci	ret = tpa6130a2_power(data, false);
30162306a36Sopenharmony_ci	if (ret != 0)
30262306a36Sopenharmony_ci		return ret;
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	return devm_snd_soc_register_component(&client->dev,
30562306a36Sopenharmony_ci			&tpa6130a2_component_driver, NULL, 0);
30662306a36Sopenharmony_ci}
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_OF)
30962306a36Sopenharmony_cistatic const struct of_device_id tpa6130a2_of_match[] = {
31062306a36Sopenharmony_ci	{ .compatible = "ti,tpa6130a2", },
31162306a36Sopenharmony_ci	{ .compatible = "ti,tpa6140a2" },
31262306a36Sopenharmony_ci	{},
31362306a36Sopenharmony_ci};
31462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, tpa6130a2_of_match);
31562306a36Sopenharmony_ci#endif
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_cistatic struct i2c_driver tpa6130a2_i2c_driver = {
31862306a36Sopenharmony_ci	.driver = {
31962306a36Sopenharmony_ci		.name = "tpa6130a2",
32062306a36Sopenharmony_ci		.of_match_table = of_match_ptr(tpa6130a2_of_match),
32162306a36Sopenharmony_ci	},
32262306a36Sopenharmony_ci	.probe = tpa6130a2_probe,
32362306a36Sopenharmony_ci	.id_table = tpa6130a2_id,
32462306a36Sopenharmony_ci};
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_cimodule_i2c_driver(tpa6130a2_i2c_driver);
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ciMODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");
32962306a36Sopenharmony_ciMODULE_DESCRIPTION("TPA6130A2 Headphone amplifier driver");
33062306a36Sopenharmony_ciMODULE_LICENSE("GPL");
331