162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * max9877.c  --  amp driver for max9877
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2009 Samsung Electronics Co.Ltd
662306a36Sopenharmony_ci * Author: Joonyoung Shim <jy0922.shim@samsung.com>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/module.h>
1062306a36Sopenharmony_ci#include <linux/init.h>
1162306a36Sopenharmony_ci#include <linux/i2c.h>
1262306a36Sopenharmony_ci#include <linux/regmap.h>
1362306a36Sopenharmony_ci#include <sound/soc.h>
1462306a36Sopenharmony_ci#include <sound/tlv.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include "max9877.h"
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_cistatic const struct reg_default max9877_regs[] = {
1962306a36Sopenharmony_ci	{ 0, 0x40 },
2062306a36Sopenharmony_ci	{ 1, 0x00 },
2162306a36Sopenharmony_ci	{ 2, 0x00 },
2262306a36Sopenharmony_ci	{ 3, 0x00 },
2362306a36Sopenharmony_ci	{ 4, 0x49 },
2462306a36Sopenharmony_ci};
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_cistatic const DECLARE_TLV_DB_RANGE(max9877_pgain_tlv,
2762306a36Sopenharmony_ci	0, 1, TLV_DB_SCALE_ITEM(0, 900, 0),
2862306a36Sopenharmony_ci	2, 2, TLV_DB_SCALE_ITEM(2000, 0, 0)
2962306a36Sopenharmony_ci);
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistatic const DECLARE_TLV_DB_RANGE(max9877_output_tlv,
3262306a36Sopenharmony_ci	0, 7, TLV_DB_SCALE_ITEM(-7900, 400, 1),
3362306a36Sopenharmony_ci	8, 15, TLV_DB_SCALE_ITEM(-4700, 300, 0),
3462306a36Sopenharmony_ci	16, 23, TLV_DB_SCALE_ITEM(-2300, 200, 0),
3562306a36Sopenharmony_ci	24, 31, TLV_DB_SCALE_ITEM(-700, 100, 0)
3662306a36Sopenharmony_ci);
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistatic const char *max9877_out_mode[] = {
3962306a36Sopenharmony_ci	"INA -> SPK",
4062306a36Sopenharmony_ci	"INA -> HP",
4162306a36Sopenharmony_ci	"INA -> SPK and HP",
4262306a36Sopenharmony_ci	"INB -> SPK",
4362306a36Sopenharmony_ci	"INB -> HP",
4462306a36Sopenharmony_ci	"INB -> SPK and HP",
4562306a36Sopenharmony_ci	"INA + INB -> SPK",
4662306a36Sopenharmony_ci	"INA + INB -> HP",
4762306a36Sopenharmony_ci	"INA + INB -> SPK and HP",
4862306a36Sopenharmony_ci};
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_cistatic const char *max9877_osc_mode[] = {
5162306a36Sopenharmony_ci	"1176KHz",
5262306a36Sopenharmony_ci	"1100KHz",
5362306a36Sopenharmony_ci	"700KHz",
5462306a36Sopenharmony_ci};
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistatic const struct soc_enum max9877_enum[] = {
5762306a36Sopenharmony_ci	SOC_ENUM_SINGLE(MAX9877_OUTPUT_MODE, 0, ARRAY_SIZE(max9877_out_mode),
5862306a36Sopenharmony_ci			max9877_out_mode),
5962306a36Sopenharmony_ci	SOC_ENUM_SINGLE(MAX9877_OUTPUT_MODE, MAX9877_OSC_OFFSET,
6062306a36Sopenharmony_ci			ARRAY_SIZE(max9877_osc_mode), max9877_osc_mode),
6162306a36Sopenharmony_ci};
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_cistatic const struct snd_kcontrol_new max9877_controls[] = {
6462306a36Sopenharmony_ci	SOC_SINGLE_TLV("MAX9877 PGAINA Playback Volume",
6562306a36Sopenharmony_ci		       MAX9877_INPUT_MODE, 0, 2, 0, max9877_pgain_tlv),
6662306a36Sopenharmony_ci	SOC_SINGLE_TLV("MAX9877 PGAINB Playback Volume",
6762306a36Sopenharmony_ci		       MAX9877_INPUT_MODE, 2, 2, 0, max9877_pgain_tlv),
6862306a36Sopenharmony_ci	SOC_SINGLE_TLV("MAX9877 Amp Speaker Playback Volume",
6962306a36Sopenharmony_ci		       MAX9877_SPK_VOLUME, 0, 31, 0, max9877_output_tlv),
7062306a36Sopenharmony_ci	SOC_DOUBLE_R_TLV("MAX9877 Amp HP Playback Volume",
7162306a36Sopenharmony_ci			 MAX9877_HPL_VOLUME, MAX9877_HPR_VOLUME, 0, 31, 0,
7262306a36Sopenharmony_ci			 max9877_output_tlv),
7362306a36Sopenharmony_ci	SOC_SINGLE("MAX9877 INB Stereo Switch",
7462306a36Sopenharmony_ci		   MAX9877_INPUT_MODE, 4, 1, 1),
7562306a36Sopenharmony_ci	SOC_SINGLE("MAX9877 INA Stereo Switch",
7662306a36Sopenharmony_ci		   MAX9877_INPUT_MODE, 5, 1, 1),
7762306a36Sopenharmony_ci	SOC_SINGLE("MAX9877 Zero-crossing detection Switch",
7862306a36Sopenharmony_ci		   MAX9877_INPUT_MODE, 6, 1, 0),
7962306a36Sopenharmony_ci	SOC_SINGLE("MAX9877 Bypass Mode Switch",
8062306a36Sopenharmony_ci		   MAX9877_OUTPUT_MODE, 6, 1, 0),
8162306a36Sopenharmony_ci	SOC_ENUM("MAX9877 Output Mode", max9877_enum[0]),
8262306a36Sopenharmony_ci	SOC_ENUM("MAX9877 Oscillator Mode", max9877_enum[1]),
8362306a36Sopenharmony_ci};
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_cistatic const struct snd_soc_dapm_widget max9877_dapm_widgets[] = {
8662306a36Sopenharmony_ciSND_SOC_DAPM_INPUT("INA1"),
8762306a36Sopenharmony_ciSND_SOC_DAPM_INPUT("INA2"),
8862306a36Sopenharmony_ciSND_SOC_DAPM_INPUT("INB1"),
8962306a36Sopenharmony_ciSND_SOC_DAPM_INPUT("INB2"),
9062306a36Sopenharmony_ciSND_SOC_DAPM_INPUT("RXIN+"),
9162306a36Sopenharmony_ciSND_SOC_DAPM_INPUT("RXIN-"),
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ciSND_SOC_DAPM_PGA("SHDN", MAX9877_OUTPUT_MODE, 7, 1, NULL, 0),
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ciSND_SOC_DAPM_OUTPUT("OUT+"),
9662306a36Sopenharmony_ciSND_SOC_DAPM_OUTPUT("OUT-"),
9762306a36Sopenharmony_ciSND_SOC_DAPM_OUTPUT("HPL"),
9862306a36Sopenharmony_ciSND_SOC_DAPM_OUTPUT("HPR"),
9962306a36Sopenharmony_ci};
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_cistatic const struct snd_soc_dapm_route max9877_dapm_routes[] = {
10262306a36Sopenharmony_ci	{ "SHDN", NULL, "INA1" },
10362306a36Sopenharmony_ci	{ "SHDN", NULL, "INA2" },
10462306a36Sopenharmony_ci	{ "SHDN", NULL, "INB1" },
10562306a36Sopenharmony_ci	{ "SHDN", NULL, "INB2" },
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	{ "OUT+", NULL, "RXIN+" },
10862306a36Sopenharmony_ci	{ "OUT+", NULL, "SHDN" },
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	{ "OUT-", NULL, "SHDN" },
11162306a36Sopenharmony_ci	{ "OUT-", NULL, "RXIN-" },
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	{ "HPL", NULL, "SHDN" },
11462306a36Sopenharmony_ci	{ "HPR", NULL, "SHDN" },
11562306a36Sopenharmony_ci};
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_cistatic const struct snd_soc_component_driver max9877_component_driver = {
11862306a36Sopenharmony_ci	.controls = max9877_controls,
11962306a36Sopenharmony_ci	.num_controls = ARRAY_SIZE(max9877_controls),
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	.dapm_widgets = max9877_dapm_widgets,
12262306a36Sopenharmony_ci	.num_dapm_widgets = ARRAY_SIZE(max9877_dapm_widgets),
12362306a36Sopenharmony_ci	.dapm_routes = max9877_dapm_routes,
12462306a36Sopenharmony_ci	.num_dapm_routes = ARRAY_SIZE(max9877_dapm_routes),
12562306a36Sopenharmony_ci};
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_cistatic const struct regmap_config max9877_regmap = {
12862306a36Sopenharmony_ci	.reg_bits = 8,
12962306a36Sopenharmony_ci	.val_bits = 8,
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	.reg_defaults = max9877_regs,
13262306a36Sopenharmony_ci	.num_reg_defaults = ARRAY_SIZE(max9877_regs),
13362306a36Sopenharmony_ci	.cache_type = REGCACHE_RBTREE,
13462306a36Sopenharmony_ci};
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_cistatic int max9877_i2c_probe(struct i2c_client *client)
13762306a36Sopenharmony_ci{
13862306a36Sopenharmony_ci	struct regmap *regmap;
13962306a36Sopenharmony_ci	int i;
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	regmap = devm_regmap_init_i2c(client, &max9877_regmap);
14262306a36Sopenharmony_ci	if (IS_ERR(regmap))
14362306a36Sopenharmony_ci		return PTR_ERR(regmap);
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	/* Ensure the device is in reset state */
14662306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(max9877_regs); i++)
14762306a36Sopenharmony_ci		regmap_write(regmap, max9877_regs[i].reg, max9877_regs[i].def);
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	return devm_snd_soc_register_component(&client->dev,
15062306a36Sopenharmony_ci			&max9877_component_driver, NULL, 0);
15162306a36Sopenharmony_ci}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_cistatic const struct i2c_device_id max9877_i2c_id[] = {
15462306a36Sopenharmony_ci	{ "max9877", 0 },
15562306a36Sopenharmony_ci	{ }
15662306a36Sopenharmony_ci};
15762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, max9877_i2c_id);
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_cistatic struct i2c_driver max9877_i2c_driver = {
16062306a36Sopenharmony_ci	.driver = {
16162306a36Sopenharmony_ci		.name = "max9877",
16262306a36Sopenharmony_ci	},
16362306a36Sopenharmony_ci	.probe = max9877_i2c_probe,
16462306a36Sopenharmony_ci	.id_table = max9877_i2c_id,
16562306a36Sopenharmony_ci};
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_cimodule_i2c_driver(max9877_i2c_driver);
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ciMODULE_DESCRIPTION("ASoC MAX9877 amp driver");
17062306a36Sopenharmony_ciMODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
17162306a36Sopenharmony_ciMODULE_LICENSE("GPL");
172