18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * LM4857 AMP driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright 2007 Wolfson Microelectronics PLC.
68c2ecf20Sopenharmony_ci * Author: Graeme Gregory
78c2ecf20Sopenharmony_ci *         graeme.gregory@wolfsonmicro.com
88c2ecf20Sopenharmony_ci * Copyright 2011 Lars-Peter Clausen <lars@metafoo.de>
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/init.h>
128c2ecf20Sopenharmony_ci#include <linux/module.h>
138c2ecf20Sopenharmony_ci#include <linux/i2c.h>
148c2ecf20Sopenharmony_ci#include <linux/regmap.h>
158c2ecf20Sopenharmony_ci#include <linux/slab.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include <sound/core.h>
188c2ecf20Sopenharmony_ci#include <sound/soc.h>
198c2ecf20Sopenharmony_ci#include <sound/tlv.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_cistatic const struct reg_default lm4857_default_regs[] = {
228c2ecf20Sopenharmony_ci	{ 0x0, 0x00 },
238c2ecf20Sopenharmony_ci	{ 0x1, 0x00 },
248c2ecf20Sopenharmony_ci	{ 0x2, 0x00 },
258c2ecf20Sopenharmony_ci	{ 0x3, 0x00 },
268c2ecf20Sopenharmony_ci};
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci/* The register offsets in the cache array */
298c2ecf20Sopenharmony_ci#define LM4857_MVOL 0
308c2ecf20Sopenharmony_ci#define LM4857_LVOL 1
318c2ecf20Sopenharmony_ci#define LM4857_RVOL 2
328c2ecf20Sopenharmony_ci#define LM4857_CTRL 3
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci/* the shifts required to set these bits */
358c2ecf20Sopenharmony_ci#define LM4857_3D 5
368c2ecf20Sopenharmony_ci#define LM4857_WAKEUP 5
378c2ecf20Sopenharmony_ci#define LM4857_EPGAIN 4
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_cistatic const unsigned int lm4857_mode_values[] = {
408c2ecf20Sopenharmony_ci	0,
418c2ecf20Sopenharmony_ci	6,
428c2ecf20Sopenharmony_ci	7,
438c2ecf20Sopenharmony_ci	8,
448c2ecf20Sopenharmony_ci	9,
458c2ecf20Sopenharmony_ci};
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_cistatic const char * const lm4857_mode_texts[] = {
488c2ecf20Sopenharmony_ci	"Off",
498c2ecf20Sopenharmony_ci	"Earpiece",
508c2ecf20Sopenharmony_ci	"Loudspeaker",
518c2ecf20Sopenharmony_ci	"Loudspeaker + Headphone",
528c2ecf20Sopenharmony_ci	"Headphone",
538c2ecf20Sopenharmony_ci};
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistatic SOC_VALUE_ENUM_SINGLE_AUTODISABLE_DECL(lm4857_mode_enum,
568c2ecf20Sopenharmony_ci	LM4857_CTRL, 0, 0xf, lm4857_mode_texts, lm4857_mode_values);
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new lm4857_mode_ctrl =
598c2ecf20Sopenharmony_ci	SOC_DAPM_ENUM("Mode", lm4857_mode_enum);
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_cistatic const struct snd_soc_dapm_widget lm4857_dapm_widgets[] = {
628c2ecf20Sopenharmony_ci	SND_SOC_DAPM_INPUT("IN"),
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	SND_SOC_DAPM_DEMUX("Mode", SND_SOC_NOPM, 0, 0, &lm4857_mode_ctrl),
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	SND_SOC_DAPM_OUTPUT("LS"),
678c2ecf20Sopenharmony_ci	SND_SOC_DAPM_OUTPUT("HP"),
688c2ecf20Sopenharmony_ci	SND_SOC_DAPM_OUTPUT("EP"),
698c2ecf20Sopenharmony_ci};
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(stereo_tlv, -4050, 150, 0);
728c2ecf20Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(mono_tlv, -3450, 150, 0);
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new lm4857_controls[] = {
758c2ecf20Sopenharmony_ci	SOC_SINGLE_TLV("Left Playback Volume", LM4857_LVOL, 0, 31, 0,
768c2ecf20Sopenharmony_ci		stereo_tlv),
778c2ecf20Sopenharmony_ci	SOC_SINGLE_TLV("Right Playback Volume", LM4857_RVOL, 0, 31, 0,
788c2ecf20Sopenharmony_ci		stereo_tlv),
798c2ecf20Sopenharmony_ci	SOC_SINGLE_TLV("Mono Playback Volume", LM4857_MVOL, 0, 31, 0,
808c2ecf20Sopenharmony_ci		mono_tlv),
818c2ecf20Sopenharmony_ci	SOC_SINGLE("Spk 3D Playback Switch", LM4857_LVOL, LM4857_3D, 1, 0),
828c2ecf20Sopenharmony_ci	SOC_SINGLE("HP 3D Playback Switch", LM4857_RVOL, LM4857_3D, 1, 0),
838c2ecf20Sopenharmony_ci	SOC_SINGLE("Fast Wakeup Playback Switch", LM4857_CTRL,
848c2ecf20Sopenharmony_ci		LM4857_WAKEUP, 1, 0),
858c2ecf20Sopenharmony_ci	SOC_SINGLE("Earpiece 6dB Playback Switch", LM4857_CTRL,
868c2ecf20Sopenharmony_ci		LM4857_EPGAIN, 1, 0),
878c2ecf20Sopenharmony_ci};
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_cistatic const struct snd_soc_dapm_route lm4857_routes[] = {
908c2ecf20Sopenharmony_ci	{ "Mode", NULL, "IN" },
918c2ecf20Sopenharmony_ci	{ "LS", "Loudspeaker", "Mode" },
928c2ecf20Sopenharmony_ci	{ "LS", "Loudspeaker + Headphone", "Mode" },
938c2ecf20Sopenharmony_ci	{ "HP", "Headphone", "Mode" },
948c2ecf20Sopenharmony_ci	{ "HP", "Loudspeaker + Headphone", "Mode" },
958c2ecf20Sopenharmony_ci	{ "EP", "Earpiece", "Mode" },
968c2ecf20Sopenharmony_ci};
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_cistatic const struct snd_soc_component_driver lm4857_component_driver = {
998c2ecf20Sopenharmony_ci	.controls = lm4857_controls,
1008c2ecf20Sopenharmony_ci	.num_controls = ARRAY_SIZE(lm4857_controls),
1018c2ecf20Sopenharmony_ci	.dapm_widgets = lm4857_dapm_widgets,
1028c2ecf20Sopenharmony_ci	.num_dapm_widgets = ARRAY_SIZE(lm4857_dapm_widgets),
1038c2ecf20Sopenharmony_ci	.dapm_routes = lm4857_routes,
1048c2ecf20Sopenharmony_ci	.num_dapm_routes = ARRAY_SIZE(lm4857_routes),
1058c2ecf20Sopenharmony_ci};
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_cistatic const struct regmap_config lm4857_regmap_config = {
1088c2ecf20Sopenharmony_ci	.val_bits = 6,
1098c2ecf20Sopenharmony_ci	.reg_bits = 2,
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	.max_register = LM4857_CTRL,
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	.cache_type = REGCACHE_FLAT,
1148c2ecf20Sopenharmony_ci	.reg_defaults = lm4857_default_regs,
1158c2ecf20Sopenharmony_ci	.num_reg_defaults = ARRAY_SIZE(lm4857_default_regs),
1168c2ecf20Sopenharmony_ci};
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_cistatic int lm4857_i2c_probe(struct i2c_client *i2c,
1198c2ecf20Sopenharmony_ci			    const struct i2c_device_id *id)
1208c2ecf20Sopenharmony_ci{
1218c2ecf20Sopenharmony_ci	struct regmap *regmap;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	regmap = devm_regmap_init_i2c(i2c, &lm4857_regmap_config);
1248c2ecf20Sopenharmony_ci	if (IS_ERR(regmap))
1258c2ecf20Sopenharmony_ci		return PTR_ERR(regmap);
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	return devm_snd_soc_register_component(&i2c->dev,
1288c2ecf20Sopenharmony_ci		&lm4857_component_driver, NULL, 0);
1298c2ecf20Sopenharmony_ci}
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_cistatic const struct i2c_device_id lm4857_i2c_id[] = {
1328c2ecf20Sopenharmony_ci	{ "lm4857", 0 },
1338c2ecf20Sopenharmony_ci	{ }
1348c2ecf20Sopenharmony_ci};
1358c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, lm4857_i2c_id);
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_cistatic struct i2c_driver lm4857_i2c_driver = {
1388c2ecf20Sopenharmony_ci	.driver = {
1398c2ecf20Sopenharmony_ci		.name = "lm4857",
1408c2ecf20Sopenharmony_ci	},
1418c2ecf20Sopenharmony_ci	.probe = lm4857_i2c_probe,
1428c2ecf20Sopenharmony_ci	.id_table = lm4857_i2c_id,
1438c2ecf20Sopenharmony_ci};
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_cimodule_i2c_driver(lm4857_i2c_driver);
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ciMODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
1488c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("LM4857 amplifier driver");
1498c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
150