Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * Driver for the NTP8918 Audio Amplifier
  4 *
  5 * Copyright (c) 2024, SaluteDevices. All Rights Reserved.
  6 *
  7 * Author: Igor Prusov <ivprusov@salutedevices.com>
  8 */
  9
 10#include <linux/kernel.h>
 11#include <linux/clk.h>
 12#include <linux/reset.h>
 13#include <linux/i2c.h>
 14#include <linux/regmap.h>
 15#include <linux/clk.h>
 16#include <linux/clk-provider.h>
 17
 18#include <sound/initval.h>
 19#include <sound/core.h>
 20#include <sound/pcm.h>
 21#include <sound/pcm_params.h>
 22#include <sound/soc.h>
 23#include <sound/soc-component.h>
 24#include <sound/tlv.h>
 25
 26#include "ntpfw.h"
 27
 28#define NTP8918_RATES   (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
 29			 SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000)
 30
 31#define NTP8918_FORMATS     (SNDRV_PCM_FMTBIT_S16_LE | \
 32			     SNDRV_PCM_FMTBIT_S20_3LE | \
 33			     SNDRV_PCM_FMTBIT_S24_LE | \
 34			     SNDRV_PCM_FMTBIT_S32_LE)
 35
 36#define NTP8918_INPUT_FMT			0x0
 37#define  NTP8918_INPUT_FMT_MASTER_MODE		BIT(0)
 38#define  NTP8918_INPUT_FMT_GSA_MODE		BIT(1)
 39#define NTP8918_GSA_FMT				0x1
 40#define  NTP8918_GSA_BS_MASK			GENMASK(3, 2)
 41#define  NTP8918_GSA_BS(x)			((x) << 2)
 42#define  NTP8918_GSA_RIGHT_J			BIT(0)
 43#define  NTP8918_GSA_LSB			BIT(1)
 44#define NTP8918_MCLK_FREQ_CTRL			0x2
 45#define  NTP8918_MCLK_FREQ_MCF			GENMASK(1, 0)
 46#define NTP8918_MASTER_VOL			0x0C
 47#define NTP8918_CHNL_A_VOL			0x17
 48#define NTP8918_CHNL_B_VOL			0x18
 49#define NTP8918_SOFT_MUTE			0x33
 50#define  NTP8918_SOFT_MUTE_SM1			BIT(0)
 51#define  NTP8918_SOFT_MUTE_SM2			BIT(1)
 52#define NTP8918_PWM_SWITCH			0x34
 53#define NTP8918_PWM_MASK_CTRL0			0x35
 54#define REG_MAX					NTP8918_PWM_MASK_CTRL0
 55
 56#define NTP8918_FW_NAME		"eq_8918.bin"
 57#define NTP8918_FW_MAGIC	0x38393138	/* "8918" */
 58
 59struct ntp8918_priv {
 60	struct i2c_client *i2c;
 61	struct clk *bck;
 62	struct reset_control *reset;
 63	unsigned int format;
 64};
 65
 66static const DECLARE_TLV_DB_SCALE(ntp8918_master_vol_scale, -12550, 50, 0);
 67
 68static const struct snd_kcontrol_new ntp8918_vol_control[] = {
 69	SOC_SINGLE_RANGE_TLV("Playback Volume", NTP8918_MASTER_VOL, 0,
 70			     0x04, 0xff, 0, ntp8918_master_vol_scale),
 71	SOC_SINGLE("Playback Switch", NTP8918_PWM_MASK_CTRL0, 1, 1, 1),
 72};
 73
 74static void ntp8918_reset_gpio(struct ntp8918_priv *ntp8918)
 75{
 76	/*
 77	 * Proper initialization sequence for NTP8918 amplifier requires driving
 78	 * /RESET signal low during power up for at least 0.1us. The sequence is,
 79	 * according to NTP8918 datasheet, 6.2 Timing Sequence 1:
 80	 * Deassert for T2 >= 1ms...
 81	 */
 82	reset_control_deassert(ntp8918->reset);
 83	fsleep(1000);
 84
 85	/* ...Assert for T3 >= 0.1us... */
 86	reset_control_assert(ntp8918->reset);
 87	fsleep(1);
 88
 89	/* ...Deassert, and wait for T4 >= 0.5ms before sound on sequence. */
 90	reset_control_deassert(ntp8918->reset);
 91	fsleep(500);
 92}
 93
 94static const struct reg_sequence ntp8918_sound_off[] = {
 95	{ NTP8918_MASTER_VOL, 0 },
 96};
 97
 98static const struct reg_sequence ntp8918_sound_on[] = {
 99	{ NTP8918_MASTER_VOL, 0b11 },
100};
101
102static int ntp8918_load_firmware(struct ntp8918_priv *ntp8918)
103{
104	int ret;
105
106	ret = ntpfw_load(ntp8918->i2c, NTP8918_FW_NAME, NTP8918_FW_MAGIC);
107	if (ret == -ENOENT) {
108		dev_warn_once(&ntp8918->i2c->dev, "Could not find firmware %s\n",
109			      NTP8918_FW_NAME);
110		return 0;
111	}
112
113	return ret;
114}
115
116static int ntp8918_snd_suspend(struct snd_soc_component *component)
117{
118	struct ntp8918_priv *ntp8918 = snd_soc_component_get_drvdata(component);
119
120	regcache_cache_only(component->regmap, true);
121
122	regmap_multi_reg_write_bypassed(component->regmap,
123					ntp8918_sound_off,
124					ARRAY_SIZE(ntp8918_sound_off));
125
126	/*
127	 * According to NTP8918 datasheet, 6.2 Timing Sequence 1:
128	 * wait after sound off for T6 >= 0.5ms
129	 */
130	fsleep(500);
131	reset_control_assert(ntp8918->reset);
132
133	regcache_mark_dirty(component->regmap);
134	clk_disable_unprepare(ntp8918->bck);
135
136	return 0;
137}
138
139static int ntp8918_snd_resume(struct snd_soc_component *component)
140{
141	struct ntp8918_priv *ntp8918 = snd_soc_component_get_drvdata(component);
142	int ret;
143
144	ret = clk_prepare_enable(ntp8918->bck);
145	if (ret)
146		return ret;
147
148	ntp8918_reset_gpio(ntp8918);
149
150	regmap_multi_reg_write_bypassed(component->regmap,
151					ntp8918_sound_on,
152					ARRAY_SIZE(ntp8918_sound_on));
153
154	ret = ntp8918_load_firmware(ntp8918);
155	if (ret) {
156		dev_err(&ntp8918->i2c->dev, "Failed to load firmware\n");
157		return ret;
158	}
159
160	regcache_cache_only(component->regmap, false);
161	snd_soc_component_cache_sync(component);
162
163	return 0;
164}
165
166static int ntp8918_probe(struct snd_soc_component *component)
167{
168	int ret;
169	struct ntp8918_priv *ntp8918 = snd_soc_component_get_drvdata(component);
170	struct device *dev = component->dev;
171
172	ret = snd_soc_add_component_controls(component, ntp8918_vol_control,
173			ARRAY_SIZE(ntp8918_vol_control));
174	if (ret)
175		return dev_err_probe(dev, ret, "Failed to add controls\n");
176
177	ret = ntp8918_load_firmware(ntp8918);
178	if (ret)
179		return dev_err_probe(dev, ret, "Failed to load firmware\n");
180
181	return 0;
182}
183
184static const struct snd_soc_dapm_widget ntp8918_dapm_widgets[] = {
185	SND_SOC_DAPM_DAC("AIFIN", "Playback", SND_SOC_NOPM, 0, 0),
186
187	SND_SOC_DAPM_OUTPUT("OUT1"),
188	SND_SOC_DAPM_OUTPUT("OUT2"),
189};
190
191static const struct snd_soc_dapm_route ntp8918_dapm_routes[] = {
192	{ "OUT1", NULL, "AIFIN" },
193	{ "OUT2", NULL, "AIFIN" },
194};
195
196static const struct snd_soc_component_driver soc_component_ntp8918 = {
197	.probe = ntp8918_probe,
198	.suspend = ntp8918_snd_suspend,
199	.resume = ntp8918_snd_resume,
200	.dapm_widgets = ntp8918_dapm_widgets,
201	.num_dapm_widgets = ARRAY_SIZE(ntp8918_dapm_widgets),
202	.dapm_routes = ntp8918_dapm_routes,
203	.num_dapm_routes = ARRAY_SIZE(ntp8918_dapm_routes),
204};
205
206static int ntp8918_hw_params(struct snd_pcm_substream *substream,
207			    struct snd_pcm_hw_params *params,
208			    struct snd_soc_dai *dai)
209{
210	struct snd_soc_component *component = dai->component;
211	struct ntp8918_priv *ntp8918 = snd_soc_component_get_drvdata(component);
212	unsigned int input_fmt = 0;
213	unsigned int gsa_fmt = 0;
214	unsigned int gsa_fmt_mask;
215	unsigned int mcf;
216	int bclk;
217	int ret;
218
219	bclk = snd_soc_params_to_bclk(params);
220	switch (bclk) {
221	case 3072000:
222	case 2822400:
223		mcf = 0;
224		break;
225	case 6144000:
226		mcf = 1;
227		break;
228	case 2048000:
229		mcf = 2;
230		break;
231	default:
232		return -EINVAL;
233	}
234
235	ret = snd_soc_component_update_bits(component, NTP8918_MCLK_FREQ_CTRL,
236					     NTP8918_MCLK_FREQ_MCF, mcf);
237	if (ret)
238		return ret;
239
240	switch (ntp8918->format) {
241	case SND_SOC_DAIFMT_I2S:
242		break;
243	case SND_SOC_DAIFMT_RIGHT_J:
244		input_fmt |= NTP8918_INPUT_FMT_GSA_MODE;
245		gsa_fmt |= NTP8918_GSA_RIGHT_J;
246		break;
247	case SND_SOC_DAIFMT_LEFT_J:
248		input_fmt |= NTP8918_INPUT_FMT_GSA_MODE;
249		break;
250	}
251
252	ret = snd_soc_component_update_bits(component, NTP8918_INPUT_FMT,
253					    NTP8918_INPUT_FMT_MASTER_MODE |
254					    NTP8918_INPUT_FMT_GSA_MODE,
255					    input_fmt);
256
257	if (!(input_fmt & NTP8918_INPUT_FMT_GSA_MODE) || ret < 0)
258		return ret;
259
260	switch (params_width(params)) {
261	case 24:
262		gsa_fmt |= NTP8918_GSA_BS(0);
263		break;
264	case 20:
265		gsa_fmt |= NTP8918_GSA_BS(1);
266		break;
267	case 18:
268		gsa_fmt |= NTP8918_GSA_BS(2);
269		break;
270	case 16:
271		gsa_fmt |= NTP8918_GSA_BS(3);
272		break;
273	default:
274		return -EINVAL;
275	}
276
277	gsa_fmt_mask = NTP8918_GSA_BS_MASK |
278		       NTP8918_GSA_RIGHT_J |
279		       NTP8918_GSA_LSB;
280	return snd_soc_component_update_bits(component, NTP8918_GSA_FMT,
281					     gsa_fmt_mask, gsa_fmt);
282}
283
284static int ntp8918_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
285{
286	struct snd_soc_component *component = dai->component;
287	struct ntp8918_priv *ntp8918 = snd_soc_component_get_drvdata(component);
288
289	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
290	case SND_SOC_DAIFMT_I2S:
291	case SND_SOC_DAIFMT_RIGHT_J:
292	case SND_SOC_DAIFMT_LEFT_J:
293		ntp8918->format = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
294		break;
295	default:
296		return -EINVAL;
297	}
298	return 0;
299}
300
301static int ntp8918_digital_mute(struct snd_soc_dai *dai, int mute, int stream)
302{
303	unsigned int mute_mask = NTP8918_SOFT_MUTE_SM1 |
304				 NTP8918_SOFT_MUTE_SM2;
305
306	return snd_soc_component_update_bits(dai->component, NTP8918_SOFT_MUTE,
307					     mute_mask, mute ? mute_mask : 0);
308}
309
310static const struct snd_soc_dai_ops ntp8918_dai_ops = {
311	.hw_params = ntp8918_hw_params,
312	.set_fmt = ntp8918_set_fmt,
313	.mute_stream = ntp8918_digital_mute,
314};
315
316static struct snd_soc_dai_driver ntp8918_dai = {
317	.name = "ntp8918-amplifier",
318	.playback = {
319		.stream_name = "Playback",
320		.channels_min = 1,
321		.channels_max = 2,
322		.rates = NTP8918_RATES,
323		.formats = NTP8918_FORMATS,
324	},
325	.ops = &ntp8918_dai_ops,
326};
327
328static const struct regmap_config ntp8918_regmap = {
329	.reg_bits = 8,
330	.val_bits = 8,
331	.max_register = REG_MAX,
332	.cache_type = REGCACHE_MAPLE,
333};
334
335static int ntp8918_i2c_probe(struct i2c_client *i2c)
336{
337	struct ntp8918_priv *ntp8918;
338	int ret;
339	struct regmap *regmap;
340
341	ntp8918 = devm_kzalloc(&i2c->dev, sizeof(*ntp8918), GFP_KERNEL);
342	if (!ntp8918)
343		return -ENOMEM;
344
345	ntp8918->i2c = i2c;
346
347	ntp8918->reset = devm_reset_control_get_shared(&i2c->dev, NULL);
348	if (IS_ERR(ntp8918->reset))
349		return dev_err_probe(&i2c->dev, PTR_ERR(ntp8918->reset), "Failed to get reset\n");
350
351	dev_set_drvdata(&i2c->dev, ntp8918);
352
353	ntp8918_reset_gpio(ntp8918);
354
355	regmap = devm_regmap_init_i2c(i2c, &ntp8918_regmap);
356	if (IS_ERR(regmap))
357		return dev_err_probe(&i2c->dev, PTR_ERR(regmap),
358				     "Failed to allocate regmap\n");
359
360	ret = devm_snd_soc_register_component(&i2c->dev, &soc_component_ntp8918,
361					      &ntp8918_dai, 1);
362	if (ret)
363		return dev_err_probe(&i2c->dev, ret,
364				     "Failed to register component\n");
365
366	ntp8918->bck = devm_clk_get_enabled(&i2c->dev, "bck");
367	if (IS_ERR(ntp8918->bck))
368		return dev_err_probe(&i2c->dev, PTR_ERR(ntp8918->bck), "failed to get bck clock\n");
369
370	return 0;
371}
372
373static const struct i2c_device_id ntp8918_i2c_id[] = {
374	{ "ntp8918", 0 },
375	{}
376};
377MODULE_DEVICE_TABLE(i2c, ntp8918_i2c_id);
378
379static const struct of_device_id ntp8918_of_match[] = {
380	{.compatible = "neofidelity,ntp8918"},
381	{}
382};
383MODULE_DEVICE_TABLE(of, ntp8918_of_match);
384
385static struct i2c_driver ntp8918_i2c_driver = {
386	.probe = ntp8918_i2c_probe,
387	.id_table = ntp8918_i2c_id,
388	.driver = {
389		.name = "ntp8918",
390		.of_match_table = ntp8918_of_match,
391	},
392};
393module_i2c_driver(ntp8918_i2c_driver);
394
395MODULE_AUTHOR("Igor Prusov <ivprusov@salutedevices.com>");
396MODULE_DESCRIPTION("NTP8918 Audio Amplifier Driver");
397MODULE_LICENSE("GPL");