162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci//
362306a36Sopenharmony_ci// Driver for Microchip Pulse Density Microphone Controller (PDMC) interfaces
462306a36Sopenharmony_ci//
562306a36Sopenharmony_ci// Copyright (C) 2019-2022 Microchip Technology Inc. and its subsidiaries
662306a36Sopenharmony_ci//
762306a36Sopenharmony_ci// Author: Codrin Ciubotariu <codrin.ciubotariu@microchip.com>
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <dt-bindings/sound/microchip,pdmc.h>
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/bitfield.h>
1262306a36Sopenharmony_ci#include <linux/clk.h>
1362306a36Sopenharmony_ci#include <linux/module.h>
1462306a36Sopenharmony_ci#include <linux/of.h>
1562306a36Sopenharmony_ci#include <linux/pm_runtime.h>
1662306a36Sopenharmony_ci#include <linux/regmap.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include <sound/core.h>
1962306a36Sopenharmony_ci#include <sound/dmaengine_pcm.h>
2062306a36Sopenharmony_ci#include <sound/pcm_params.h>
2162306a36Sopenharmony_ci#include <sound/tlv.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci/*
2462306a36Sopenharmony_ci * ---- PDMC Register map ----
2562306a36Sopenharmony_ci */
2662306a36Sopenharmony_ci#define MCHP_PDMC_CR			0x00	/* Control Register */
2762306a36Sopenharmony_ci#define MCHP_PDMC_MR			0x04	/* Mode Register */
2862306a36Sopenharmony_ci#define MCHP_PDMC_CFGR			0x08	/* Configuration Register */
2962306a36Sopenharmony_ci#define MCHP_PDMC_RHR			0x0C	/* Receive Holding Register */
3062306a36Sopenharmony_ci#define MCHP_PDMC_IER			0x14	/* Interrupt Enable Register */
3162306a36Sopenharmony_ci#define MCHP_PDMC_IDR			0x18	/* Interrupt Disable Register */
3262306a36Sopenharmony_ci#define MCHP_PDMC_IMR			0x1C	/* Interrupt Mask Register */
3362306a36Sopenharmony_ci#define MCHP_PDMC_ISR			0x20	/* Interrupt Status Register */
3462306a36Sopenharmony_ci#define MCHP_PDMC_VER			0x50	/* Version Register */
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci/*
3762306a36Sopenharmony_ci * ---- Control Register (Write-only) ----
3862306a36Sopenharmony_ci */
3962306a36Sopenharmony_ci#define MCHP_PDMC_CR_SWRST		BIT(0)	/* Software Reset */
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci/*
4262306a36Sopenharmony_ci * ---- Mode Register (Read/Write) ----
4362306a36Sopenharmony_ci */
4462306a36Sopenharmony_ci#define MCHP_PDMC_MR_PDMCEN_MASK	GENMASK(3, 0)
4562306a36Sopenharmony_ci#define MCHP_PDMC_MR_PDMCEN(ch)		(BIT(ch) & MCHP_PDMC_MR_PDMCEN_MASK)
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci#define MCHP_PDMC_MR_OSR_MASK		GENMASK(17, 16)
4862306a36Sopenharmony_ci#define MCHP_PDMC_MR_OSR64		(1 << 16)
4962306a36Sopenharmony_ci#define MCHP_PDMC_MR_OSR128		(2 << 16)
5062306a36Sopenharmony_ci#define MCHP_PDMC_MR_OSR256		(3 << 16)
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci#define MCHP_PDMC_MR_SINCORDER_MASK	GENMASK(23, 20)
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci#define MCHP_PDMC_MR_SINC_OSR_MASK	GENMASK(27, 24)
5562306a36Sopenharmony_ci#define MCHP_PDMC_MR_SINC_OSR_DIS	(0 << 24)
5662306a36Sopenharmony_ci#define MCHP_PDMC_MR_SINC_OSR_8		(1 << 24)
5762306a36Sopenharmony_ci#define MCHP_PDMC_MR_SINC_OSR_16	(2 << 24)
5862306a36Sopenharmony_ci#define MCHP_PDMC_MR_SINC_OSR_32	(3 << 24)
5962306a36Sopenharmony_ci#define MCHP_PDMC_MR_SINC_OSR_64	(4 << 24)
6062306a36Sopenharmony_ci#define MCHP_PDMC_MR_SINC_OSR_128	(5 << 24)
6162306a36Sopenharmony_ci#define MCHP_PDMC_MR_SINC_OSR_256	(6 << 24)
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci#define MCHP_PDMC_MR_CHUNK_MASK		GENMASK(31, 28)
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci/*
6662306a36Sopenharmony_ci * ---- Configuration Register (Read/Write) ----
6762306a36Sopenharmony_ci */
6862306a36Sopenharmony_ci#define MCHP_PDMC_CFGR_BSSEL_MASK	(BIT(0) | BIT(2) | BIT(4) | BIT(6))
6962306a36Sopenharmony_ci#define MCHP_PDMC_CFGR_BSSEL(ch)	BIT((ch) * 2)
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci#define MCHP_PDMC_CFGR_PDMSEL_MASK	(BIT(16) | BIT(18) | BIT(20) | BIT(22))
7262306a36Sopenharmony_ci#define MCHP_PDMC_CFGR_PDMSEL(ch)	BIT((ch) * 2 + 16)
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci/*
7562306a36Sopenharmony_ci * ---- Interrupt Enable/Disable/Mask/Status Registers ----
7662306a36Sopenharmony_ci */
7762306a36Sopenharmony_ci#define MCHP_PDMC_IR_RXRDY		BIT(0)
7862306a36Sopenharmony_ci#define MCHP_PDMC_IR_RXEMPTY		BIT(1)
7962306a36Sopenharmony_ci#define MCHP_PDMC_IR_RXFULL		BIT(2)
8062306a36Sopenharmony_ci#define MCHP_PDMC_IR_RXCHUNK		BIT(3)
8162306a36Sopenharmony_ci#define MCHP_PDMC_IR_RXUDR		BIT(4)
8262306a36Sopenharmony_ci#define MCHP_PDMC_IR_RXOVR		BIT(5)
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci/*
8562306a36Sopenharmony_ci * ---- Version Register (Read-only) ----
8662306a36Sopenharmony_ci */
8762306a36Sopenharmony_ci#define MCHP_PDMC_VER_VERSION		GENMASK(11, 0)
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci#define MCHP_PDMC_MAX_CHANNELS		4
9062306a36Sopenharmony_ci#define MCHP_PDMC_DS_NO			2
9162306a36Sopenharmony_ci#define MCHP_PDMC_EDGE_NO		2
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_cistruct mic_map {
9462306a36Sopenharmony_ci	int ds_pos;
9562306a36Sopenharmony_ci	int clk_edge;
9662306a36Sopenharmony_ci};
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cistruct mchp_pdmc_chmap {
9962306a36Sopenharmony_ci	struct snd_pcm_chmap_elem *chmap;
10062306a36Sopenharmony_ci	struct mchp_pdmc *dd;
10162306a36Sopenharmony_ci	struct snd_pcm *pcm;
10262306a36Sopenharmony_ci	struct snd_kcontrol *kctl;
10362306a36Sopenharmony_ci};
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_cistruct mchp_pdmc {
10662306a36Sopenharmony_ci	struct mic_map channel_mic_map[MCHP_PDMC_MAX_CHANNELS];
10762306a36Sopenharmony_ci	struct device *dev;
10862306a36Sopenharmony_ci	struct snd_dmaengine_dai_dma_data addr;
10962306a36Sopenharmony_ci	struct regmap *regmap;
11062306a36Sopenharmony_ci	struct clk *pclk;
11162306a36Sopenharmony_ci	struct clk *gclk;
11262306a36Sopenharmony_ci	u32 pdmcen;
11362306a36Sopenharmony_ci	u32 suspend_irq;
11462306a36Sopenharmony_ci	u32 startup_delay_us;
11562306a36Sopenharmony_ci	int mic_no;
11662306a36Sopenharmony_ci	int sinc_order;
11762306a36Sopenharmony_ci	bool audio_filter_en;
11862306a36Sopenharmony_ci};
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_cistatic const char *const mchp_pdmc_sinc_filter_order_text[] = {
12162306a36Sopenharmony_ci	"1", "2", "3", "4", "5"
12262306a36Sopenharmony_ci};
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_cistatic const unsigned int mchp_pdmc_sinc_filter_order_values[] = {
12562306a36Sopenharmony_ci	1, 2, 3, 4, 5,
12662306a36Sopenharmony_ci};
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_cistatic const struct soc_enum mchp_pdmc_sinc_filter_order_enum = {
12962306a36Sopenharmony_ci	.items = ARRAY_SIZE(mchp_pdmc_sinc_filter_order_text),
13062306a36Sopenharmony_ci	.texts = mchp_pdmc_sinc_filter_order_text,
13162306a36Sopenharmony_ci	.values = mchp_pdmc_sinc_filter_order_values,
13262306a36Sopenharmony_ci};
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_cistatic int mchp_pdmc_sinc_order_get(struct snd_kcontrol *kcontrol,
13562306a36Sopenharmony_ci				    struct snd_ctl_elem_value *uvalue)
13662306a36Sopenharmony_ci{
13762306a36Sopenharmony_ci	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
13862306a36Sopenharmony_ci	struct mchp_pdmc *dd = snd_soc_component_get_drvdata(component);
13962306a36Sopenharmony_ci	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
14062306a36Sopenharmony_ci	unsigned int item;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	item = snd_soc_enum_val_to_item(e, dd->sinc_order);
14362306a36Sopenharmony_ci	uvalue->value.enumerated.item[0] = item;
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	return 0;
14662306a36Sopenharmony_ci}
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_cistatic int mchp_pdmc_sinc_order_put(struct snd_kcontrol *kcontrol,
14962306a36Sopenharmony_ci				    struct snd_ctl_elem_value *uvalue)
15062306a36Sopenharmony_ci{
15162306a36Sopenharmony_ci	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
15262306a36Sopenharmony_ci	struct mchp_pdmc *dd = snd_soc_component_get_drvdata(component);
15362306a36Sopenharmony_ci	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
15462306a36Sopenharmony_ci	unsigned int *item = uvalue->value.enumerated.item;
15562306a36Sopenharmony_ci	unsigned int val;
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	if (item[0] >= e->items)
15862306a36Sopenharmony_ci		return -EINVAL;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l;
16162306a36Sopenharmony_ci	if (val == dd->sinc_order)
16262306a36Sopenharmony_ci		return 0;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	dd->sinc_order = val;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	return 1;
16762306a36Sopenharmony_ci}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_cistatic int mchp_pdmc_af_get(struct snd_kcontrol *kcontrol,
17062306a36Sopenharmony_ci			    struct snd_ctl_elem_value *uvalue)
17162306a36Sopenharmony_ci{
17262306a36Sopenharmony_ci	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
17362306a36Sopenharmony_ci	struct mchp_pdmc *dd = snd_soc_component_get_drvdata(component);
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	uvalue->value.integer.value[0] = !!dd->audio_filter_en;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	return 0;
17862306a36Sopenharmony_ci}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_cistatic int mchp_pdmc_af_put(struct snd_kcontrol *kcontrol,
18162306a36Sopenharmony_ci			    struct snd_ctl_elem_value *uvalue)
18262306a36Sopenharmony_ci{
18362306a36Sopenharmony_ci	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
18462306a36Sopenharmony_ci	struct mchp_pdmc *dd = snd_soc_component_get_drvdata(component);
18562306a36Sopenharmony_ci	bool af = uvalue->value.integer.value[0] ? true : false;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	if (dd->audio_filter_en == af)
18862306a36Sopenharmony_ci		return 0;
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	dd->audio_filter_en = af;
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	return 1;
19362306a36Sopenharmony_ci}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_cistatic int mchp_pdmc_chmap_ctl_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
19662306a36Sopenharmony_ci{
19762306a36Sopenharmony_ci	struct mchp_pdmc_chmap *info = snd_kcontrol_chip(kcontrol);
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
20062306a36Sopenharmony_ci	uinfo->count = info->dd->mic_no;
20162306a36Sopenharmony_ci	uinfo->value.integer.min = 0;
20262306a36Sopenharmony_ci	uinfo->value.integer.max = SNDRV_CHMAP_RR; /* maxmimum 4 channels */
20362306a36Sopenharmony_ci	return 0;
20462306a36Sopenharmony_ci}
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_cistatic inline struct snd_pcm_substream *
20762306a36Sopenharmony_cimchp_pdmc_chmap_substream(struct mchp_pdmc_chmap *info, unsigned int idx)
20862306a36Sopenharmony_ci{
20962306a36Sopenharmony_ci	struct snd_pcm_substream *s;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	for (s = info->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; s; s = s->next)
21262306a36Sopenharmony_ci		if (s->number == idx)
21362306a36Sopenharmony_ci			return s;
21462306a36Sopenharmony_ci	return NULL;
21562306a36Sopenharmony_ci}
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_cistatic struct snd_pcm_chmap_elem *mchp_pdmc_chmap_get(struct snd_pcm_substream *substream,
21862306a36Sopenharmony_ci						      struct mchp_pdmc_chmap *ch_info)
21962306a36Sopenharmony_ci{
22062306a36Sopenharmony_ci	struct snd_pcm_chmap_elem *map;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	for (map = ch_info->chmap; map->channels; map++) {
22362306a36Sopenharmony_ci		if (map->channels == substream->runtime->channels)
22462306a36Sopenharmony_ci			return map;
22562306a36Sopenharmony_ci	}
22662306a36Sopenharmony_ci	return NULL;
22762306a36Sopenharmony_ci}
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_cistatic int mchp_pdmc_chmap_ctl_get(struct snd_kcontrol *kcontrol,
23062306a36Sopenharmony_ci				   struct snd_ctl_elem_value *ucontrol)
23162306a36Sopenharmony_ci{
23262306a36Sopenharmony_ci	struct mchp_pdmc_chmap *info = snd_kcontrol_chip(kcontrol);
23362306a36Sopenharmony_ci	struct mchp_pdmc *dd = info->dd;
23462306a36Sopenharmony_ci	unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
23562306a36Sopenharmony_ci	struct snd_pcm_substream *substream;
23662306a36Sopenharmony_ci	const struct snd_pcm_chmap_elem *map;
23762306a36Sopenharmony_ci	int i;
23862306a36Sopenharmony_ci	u32 cfgr_val = 0;
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	if (!info->chmap)
24162306a36Sopenharmony_ci		return -EINVAL;
24262306a36Sopenharmony_ci	substream = mchp_pdmc_chmap_substream(info, idx);
24362306a36Sopenharmony_ci	if (!substream)
24462306a36Sopenharmony_ci		return -ENODEV;
24562306a36Sopenharmony_ci	memset(ucontrol->value.integer.value, 0, sizeof(long) * info->dd->mic_no);
24662306a36Sopenharmony_ci	if (!substream->runtime)
24762306a36Sopenharmony_ci		return 0; /* no channels set */
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	map = mchp_pdmc_chmap_get(substream, info);
25062306a36Sopenharmony_ci	if (!map)
25162306a36Sopenharmony_ci		return -EINVAL;
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	for (i = 0; i < map->channels; i++) {
25462306a36Sopenharmony_ci		int map_idx = map->channels == 1 ? map->map[i] - SNDRV_CHMAP_MONO :
25562306a36Sopenharmony_ci						   map->map[i] - SNDRV_CHMAP_FL;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci		/* make sure the reported channel map is the real one, so write the map */
25862306a36Sopenharmony_ci		if (dd->channel_mic_map[map_idx].ds_pos)
25962306a36Sopenharmony_ci			cfgr_val |= MCHP_PDMC_CFGR_PDMSEL(i);
26062306a36Sopenharmony_ci		if (dd->channel_mic_map[map_idx].clk_edge)
26162306a36Sopenharmony_ci			cfgr_val |= MCHP_PDMC_CFGR_BSSEL(i);
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci		ucontrol->value.integer.value[i] = map->map[i];
26462306a36Sopenharmony_ci	}
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	regmap_write(dd->regmap, MCHP_PDMC_CFGR, cfgr_val);
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	return 0;
26962306a36Sopenharmony_ci}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_cistatic int mchp_pdmc_chmap_ctl_put(struct snd_kcontrol *kcontrol,
27262306a36Sopenharmony_ci				   struct snd_ctl_elem_value *ucontrol)
27362306a36Sopenharmony_ci{
27462306a36Sopenharmony_ci	struct mchp_pdmc_chmap *info = snd_kcontrol_chip(kcontrol);
27562306a36Sopenharmony_ci	struct mchp_pdmc *dd = info->dd;
27662306a36Sopenharmony_ci	unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
27762306a36Sopenharmony_ci	struct snd_pcm_substream *substream;
27862306a36Sopenharmony_ci	struct snd_pcm_chmap_elem *map;
27962306a36Sopenharmony_ci	u32 cfgr_val = 0;
28062306a36Sopenharmony_ci	int i;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	if (!info->chmap)
28362306a36Sopenharmony_ci		return -EINVAL;
28462306a36Sopenharmony_ci	substream = mchp_pdmc_chmap_substream(info, idx);
28562306a36Sopenharmony_ci	if (!substream)
28662306a36Sopenharmony_ci		return -ENODEV;
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	map = mchp_pdmc_chmap_get(substream, info);
28962306a36Sopenharmony_ci	if (!map)
29062306a36Sopenharmony_ci		return -EINVAL;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	for (i = 0; i < map->channels; i++) {
29362306a36Sopenharmony_ci		int map_idx;
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci		map->map[i] = ucontrol->value.integer.value[i];
29662306a36Sopenharmony_ci		map_idx = map->channels == 1 ? map->map[i] - SNDRV_CHMAP_MONO :
29762306a36Sopenharmony_ci					       map->map[i] - SNDRV_CHMAP_FL;
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci		/* configure IP for the desired channel map */
30062306a36Sopenharmony_ci		if (dd->channel_mic_map[map_idx].ds_pos)
30162306a36Sopenharmony_ci			cfgr_val |= MCHP_PDMC_CFGR_PDMSEL(i);
30262306a36Sopenharmony_ci		if (dd->channel_mic_map[map_idx].clk_edge)
30362306a36Sopenharmony_ci			cfgr_val |= MCHP_PDMC_CFGR_BSSEL(i);
30462306a36Sopenharmony_ci	}
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	regmap_write(dd->regmap, MCHP_PDMC_CFGR, cfgr_val);
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	return 0;
30962306a36Sopenharmony_ci}
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_cistatic void mchp_pdmc_chmap_ctl_private_free(struct snd_kcontrol *kcontrol)
31262306a36Sopenharmony_ci{
31362306a36Sopenharmony_ci	struct mchp_pdmc_chmap *info = snd_kcontrol_chip(kcontrol);
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	info->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].chmap_kctl = NULL;
31662306a36Sopenharmony_ci	kfree(info);
31762306a36Sopenharmony_ci}
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_cistatic int mchp_pdmc_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag,
32062306a36Sopenharmony_ci				   unsigned int size, unsigned int __user *tlv)
32162306a36Sopenharmony_ci{
32262306a36Sopenharmony_ci	struct mchp_pdmc_chmap *info = snd_kcontrol_chip(kcontrol);
32362306a36Sopenharmony_ci	const struct snd_pcm_chmap_elem *map;
32462306a36Sopenharmony_ci	unsigned int __user *dst;
32562306a36Sopenharmony_ci	int c, count = 0;
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	if (!info->chmap)
32862306a36Sopenharmony_ci		return -EINVAL;
32962306a36Sopenharmony_ci	if (size < 8)
33062306a36Sopenharmony_ci		return -ENOMEM;
33162306a36Sopenharmony_ci	if (put_user(SNDRV_CTL_TLVT_CONTAINER, tlv))
33262306a36Sopenharmony_ci		return -EFAULT;
33362306a36Sopenharmony_ci	size -= 8;
33462306a36Sopenharmony_ci	dst = tlv + 2;
33562306a36Sopenharmony_ci	for (map = info->chmap; map->channels; map++) {
33662306a36Sopenharmony_ci		int chs_bytes = map->channels * 4;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci		if (size < 8)
33962306a36Sopenharmony_ci			return -ENOMEM;
34062306a36Sopenharmony_ci		if (put_user(SNDRV_CTL_TLVT_CHMAP_VAR, dst) ||
34162306a36Sopenharmony_ci		    put_user(chs_bytes, dst + 1))
34262306a36Sopenharmony_ci			return -EFAULT;
34362306a36Sopenharmony_ci		dst += 2;
34462306a36Sopenharmony_ci		size -= 8;
34562306a36Sopenharmony_ci		count += 8;
34662306a36Sopenharmony_ci		if (size < chs_bytes)
34762306a36Sopenharmony_ci			return -ENOMEM;
34862306a36Sopenharmony_ci		size -= chs_bytes;
34962306a36Sopenharmony_ci		count += chs_bytes;
35062306a36Sopenharmony_ci		for (c = 0; c < map->channels; c++) {
35162306a36Sopenharmony_ci			if (put_user(map->map[c], dst))
35262306a36Sopenharmony_ci				return -EFAULT;
35362306a36Sopenharmony_ci			dst++;
35462306a36Sopenharmony_ci		}
35562306a36Sopenharmony_ci	}
35662306a36Sopenharmony_ci	if (put_user(count, tlv + 1))
35762306a36Sopenharmony_ci		return -EFAULT;
35862306a36Sopenharmony_ci	return 0;
35962306a36Sopenharmony_ci}
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_cistatic const struct snd_kcontrol_new mchp_pdmc_snd_controls[] = {
36262306a36Sopenharmony_ci	SOC_SINGLE_BOOL_EXT("Audio Filter", 0, &mchp_pdmc_af_get, &mchp_pdmc_af_put),
36362306a36Sopenharmony_ci	{
36462306a36Sopenharmony_ci		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
36562306a36Sopenharmony_ci		.name = "SINC Filter Order",
36662306a36Sopenharmony_ci		.info = snd_soc_info_enum_double,
36762306a36Sopenharmony_ci		.get = mchp_pdmc_sinc_order_get,
36862306a36Sopenharmony_ci		.put = mchp_pdmc_sinc_order_put,
36962306a36Sopenharmony_ci		.private_value = (unsigned long)&mchp_pdmc_sinc_filter_order_enum,
37062306a36Sopenharmony_ci	},
37162306a36Sopenharmony_ci};
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_cistatic int mchp_pdmc_close(struct snd_soc_component *component,
37462306a36Sopenharmony_ci			   struct snd_pcm_substream *substream)
37562306a36Sopenharmony_ci{
37662306a36Sopenharmony_ci	return snd_soc_add_component_controls(component, mchp_pdmc_snd_controls,
37762306a36Sopenharmony_ci					      ARRAY_SIZE(mchp_pdmc_snd_controls));
37862306a36Sopenharmony_ci}
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_cistatic int mchp_pdmc_open(struct snd_soc_component *component,
38162306a36Sopenharmony_ci			  struct snd_pcm_substream *substream)
38262306a36Sopenharmony_ci{
38362306a36Sopenharmony_ci	int i;
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	/* remove controls that can't be changed at runtime */
38662306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(mchp_pdmc_snd_controls); i++) {
38762306a36Sopenharmony_ci		const struct snd_kcontrol_new *control = &mchp_pdmc_snd_controls[i];
38862306a36Sopenharmony_ci		struct snd_ctl_elem_id id;
38962306a36Sopenharmony_ci		int err;
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci		if (component->name_prefix)
39262306a36Sopenharmony_ci			snprintf(id.name, sizeof(id.name), "%s %s", component->name_prefix,
39362306a36Sopenharmony_ci				 control->name);
39462306a36Sopenharmony_ci		else
39562306a36Sopenharmony_ci			strscpy(id.name, control->name, sizeof(id.name));
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci		id.numid = 0;
39862306a36Sopenharmony_ci		id.iface = control->iface;
39962306a36Sopenharmony_ci		id.device = control->device;
40062306a36Sopenharmony_ci		id.subdevice = control->subdevice;
40162306a36Sopenharmony_ci		id.index = control->index;
40262306a36Sopenharmony_ci		err = snd_ctl_remove_id(component->card->snd_card, &id);
40362306a36Sopenharmony_ci		if (err < 0)
40462306a36Sopenharmony_ci			dev_err(component->dev, "%d: Failed to remove %s\n", err,
40562306a36Sopenharmony_ci				control->name);
40662306a36Sopenharmony_ci	}
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	return 0;
40962306a36Sopenharmony_ci}
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_cistatic const struct snd_soc_component_driver mchp_pdmc_dai_component = {
41262306a36Sopenharmony_ci	.name = "mchp-pdmc",
41362306a36Sopenharmony_ci	.controls = mchp_pdmc_snd_controls,
41462306a36Sopenharmony_ci	.num_controls = ARRAY_SIZE(mchp_pdmc_snd_controls),
41562306a36Sopenharmony_ci	.open = &mchp_pdmc_open,
41662306a36Sopenharmony_ci	.close = &mchp_pdmc_close,
41762306a36Sopenharmony_ci	.legacy_dai_naming = 1,
41862306a36Sopenharmony_ci	.trigger_start = SND_SOC_TRIGGER_ORDER_LDC,
41962306a36Sopenharmony_ci};
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_cistatic const unsigned int mchp_pdmc_1mic[] = {1};
42262306a36Sopenharmony_cistatic const unsigned int mchp_pdmc_2mic[] = {1, 2};
42362306a36Sopenharmony_cistatic const unsigned int mchp_pdmc_3mic[] = {1, 2, 3};
42462306a36Sopenharmony_cistatic const unsigned int mchp_pdmc_4mic[] = {1, 2, 3, 4};
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_cistatic const struct snd_pcm_hw_constraint_list mchp_pdmc_chan_constr[] = {
42762306a36Sopenharmony_ci	{
42862306a36Sopenharmony_ci		.list = mchp_pdmc_1mic,
42962306a36Sopenharmony_ci		.count = ARRAY_SIZE(mchp_pdmc_1mic),
43062306a36Sopenharmony_ci	},
43162306a36Sopenharmony_ci	{
43262306a36Sopenharmony_ci		.list = mchp_pdmc_2mic,
43362306a36Sopenharmony_ci		.count = ARRAY_SIZE(mchp_pdmc_2mic),
43462306a36Sopenharmony_ci	},
43562306a36Sopenharmony_ci	{
43662306a36Sopenharmony_ci		.list = mchp_pdmc_3mic,
43762306a36Sopenharmony_ci		.count = ARRAY_SIZE(mchp_pdmc_3mic),
43862306a36Sopenharmony_ci	},
43962306a36Sopenharmony_ci	{
44062306a36Sopenharmony_ci		.list = mchp_pdmc_4mic,
44162306a36Sopenharmony_ci		.count = ARRAY_SIZE(mchp_pdmc_4mic),
44262306a36Sopenharmony_ci	},
44362306a36Sopenharmony_ci};
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_cistatic int mchp_pdmc_startup(struct snd_pcm_substream *substream,
44662306a36Sopenharmony_ci			     struct snd_soc_dai *dai)
44762306a36Sopenharmony_ci{
44862306a36Sopenharmony_ci	struct mchp_pdmc *dd = snd_soc_dai_get_drvdata(dai);
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	regmap_write(dd->regmap, MCHP_PDMC_CR, MCHP_PDMC_CR_SWRST);
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	snd_pcm_hw_constraint_list(substream->runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
45362306a36Sopenharmony_ci				   &mchp_pdmc_chan_constr[dd->mic_no - 1]);
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	return 0;
45662306a36Sopenharmony_ci}
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_cistatic int mchp_pdmc_dai_probe(struct snd_soc_dai *dai)
45962306a36Sopenharmony_ci{
46062306a36Sopenharmony_ci	struct mchp_pdmc *dd = snd_soc_dai_get_drvdata(dai);
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	snd_soc_dai_init_dma_data(dai, NULL, &dd->addr);
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	return 0;
46562306a36Sopenharmony_ci}
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_cistatic int mchp_pdmc_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
46862306a36Sopenharmony_ci{
46962306a36Sopenharmony_ci	unsigned int fmt_master = fmt & SND_SOC_DAIFMT_MASTER_MASK;
47062306a36Sopenharmony_ci	unsigned int fmt_format = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	/* IP needs to be bitclock master */
47362306a36Sopenharmony_ci	if (fmt_master != SND_SOC_DAIFMT_BP_FP &&
47462306a36Sopenharmony_ci	    fmt_master != SND_SOC_DAIFMT_BP_FC)
47562306a36Sopenharmony_ci		return -EINVAL;
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	/* IP supports only PDM interface */
47862306a36Sopenharmony_ci	if (fmt_format != SND_SOC_DAIFMT_PDM)
47962306a36Sopenharmony_ci		return -EINVAL;
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	return 0;
48262306a36Sopenharmony_ci}
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_cistatic u32 mchp_pdmc_mr_set_osr(int audio_filter_en, unsigned int osr)
48562306a36Sopenharmony_ci{
48662306a36Sopenharmony_ci	if (audio_filter_en) {
48762306a36Sopenharmony_ci		switch (osr) {
48862306a36Sopenharmony_ci		case 64:
48962306a36Sopenharmony_ci			return MCHP_PDMC_MR_OSR64;
49062306a36Sopenharmony_ci		case 128:
49162306a36Sopenharmony_ci			return MCHP_PDMC_MR_OSR128;
49262306a36Sopenharmony_ci		case 256:
49362306a36Sopenharmony_ci			return MCHP_PDMC_MR_OSR256;
49462306a36Sopenharmony_ci		}
49562306a36Sopenharmony_ci	} else {
49662306a36Sopenharmony_ci		switch (osr) {
49762306a36Sopenharmony_ci		case 8:
49862306a36Sopenharmony_ci			return MCHP_PDMC_MR_SINC_OSR_8;
49962306a36Sopenharmony_ci		case 16:
50062306a36Sopenharmony_ci			return MCHP_PDMC_MR_SINC_OSR_16;
50162306a36Sopenharmony_ci		case 32:
50262306a36Sopenharmony_ci			return MCHP_PDMC_MR_SINC_OSR_32;
50362306a36Sopenharmony_ci		case 64:
50462306a36Sopenharmony_ci			return MCHP_PDMC_MR_SINC_OSR_64;
50562306a36Sopenharmony_ci		case 128:
50662306a36Sopenharmony_ci			return MCHP_PDMC_MR_SINC_OSR_128;
50762306a36Sopenharmony_ci		case 256:
50862306a36Sopenharmony_ci			return MCHP_PDMC_MR_SINC_OSR_256;
50962306a36Sopenharmony_ci		}
51062306a36Sopenharmony_ci	}
51162306a36Sopenharmony_ci	return 0;
51262306a36Sopenharmony_ci}
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_cistatic inline int mchp_pdmc_period_to_maxburst(int period_size)
51562306a36Sopenharmony_ci{
51662306a36Sopenharmony_ci	if (!(period_size % 8))
51762306a36Sopenharmony_ci		return 8;
51862306a36Sopenharmony_ci	if (!(period_size % 4))
51962306a36Sopenharmony_ci		return 4;
52062306a36Sopenharmony_ci	if (!(period_size % 2))
52162306a36Sopenharmony_ci		return 2;
52262306a36Sopenharmony_ci	return 1;
52362306a36Sopenharmony_ci}
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_cistatic struct snd_pcm_chmap_elem mchp_pdmc_std_chmaps[] = {
52662306a36Sopenharmony_ci	{ .channels = 1,
52762306a36Sopenharmony_ci	  .map = { SNDRV_CHMAP_MONO } },
52862306a36Sopenharmony_ci	{ .channels = 2,
52962306a36Sopenharmony_ci	  .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } },
53062306a36Sopenharmony_ci	{ .channels = 3,
53162306a36Sopenharmony_ci	  .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR,
53262306a36Sopenharmony_ci		   SNDRV_CHMAP_RL } },
53362306a36Sopenharmony_ci	{ .channels = 4,
53462306a36Sopenharmony_ci	  .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR,
53562306a36Sopenharmony_ci		   SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } },
53662306a36Sopenharmony_ci	{ }
53762306a36Sopenharmony_ci};
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_cistatic int mchp_pdmc_hw_params(struct snd_pcm_substream *substream,
54062306a36Sopenharmony_ci			       struct snd_pcm_hw_params *params,
54162306a36Sopenharmony_ci			       struct snd_soc_dai *dai)
54262306a36Sopenharmony_ci{
54362306a36Sopenharmony_ci	struct mchp_pdmc *dd = snd_soc_dai_get_drvdata(dai);
54462306a36Sopenharmony_ci	struct snd_soc_component *comp = dai->component;
54562306a36Sopenharmony_ci	unsigned long gclk_rate = 0;
54662306a36Sopenharmony_ci	unsigned long best_diff_rate = ~0UL;
54762306a36Sopenharmony_ci	unsigned int channels = params_channels(params);
54862306a36Sopenharmony_ci	unsigned int osr = 0, osr_start;
54962306a36Sopenharmony_ci	unsigned int fs = params_rate(params);
55062306a36Sopenharmony_ci	u32 mr_val = 0;
55162306a36Sopenharmony_ci	u32 cfgr_val = 0;
55262306a36Sopenharmony_ci	int i;
55362306a36Sopenharmony_ci	int ret;
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	dev_dbg(comp->dev, "%s() rate=%u format=%#x width=%u channels=%u\n",
55662306a36Sopenharmony_ci		__func__, params_rate(params), params_format(params),
55762306a36Sopenharmony_ci		params_width(params), params_channels(params));
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	if (channels > dd->mic_no) {
56062306a36Sopenharmony_ci		dev_err(comp->dev, "more channels %u than microphones %d\n",
56162306a36Sopenharmony_ci			channels, dd->mic_no);
56262306a36Sopenharmony_ci		return -EINVAL;
56362306a36Sopenharmony_ci	}
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	dd->pdmcen = 0;
56662306a36Sopenharmony_ci	for (i = 0; i < channels; i++) {
56762306a36Sopenharmony_ci		dd->pdmcen |= MCHP_PDMC_MR_PDMCEN(i);
56862306a36Sopenharmony_ci		if (dd->channel_mic_map[i].ds_pos)
56962306a36Sopenharmony_ci			cfgr_val |= MCHP_PDMC_CFGR_PDMSEL(i);
57062306a36Sopenharmony_ci		if (dd->channel_mic_map[i].clk_edge)
57162306a36Sopenharmony_ci			cfgr_val |= MCHP_PDMC_CFGR_BSSEL(i);
57262306a36Sopenharmony_ci	}
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	for (osr_start = dd->audio_filter_en ? 64 : 8;
57562306a36Sopenharmony_ci	     osr_start <= 256 && best_diff_rate; osr_start *= 2) {
57662306a36Sopenharmony_ci		long round_rate;
57762306a36Sopenharmony_ci		unsigned long diff_rate;
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci		round_rate = clk_round_rate(dd->gclk,
58062306a36Sopenharmony_ci					    (unsigned long)fs * 16 * osr_start);
58162306a36Sopenharmony_ci		if (round_rate < 0)
58262306a36Sopenharmony_ci			continue;
58362306a36Sopenharmony_ci		diff_rate = abs((fs * 16 * osr_start) - round_rate);
58462306a36Sopenharmony_ci		if (diff_rate < best_diff_rate) {
58562306a36Sopenharmony_ci			best_diff_rate = diff_rate;
58662306a36Sopenharmony_ci			osr = osr_start;
58762306a36Sopenharmony_ci			gclk_rate = fs * 16 * osr;
58862306a36Sopenharmony_ci		}
58962306a36Sopenharmony_ci	}
59062306a36Sopenharmony_ci	if (!gclk_rate) {
59162306a36Sopenharmony_ci		dev_err(comp->dev, "invalid sampling rate: %u\n", fs);
59262306a36Sopenharmony_ci		return -EINVAL;
59362306a36Sopenharmony_ci	}
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	/* CLK is enabled by runtime PM. */
59662306a36Sopenharmony_ci	clk_disable_unprepare(dd->gclk);
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci	/* set the rate */
59962306a36Sopenharmony_ci	ret = clk_set_rate(dd->gclk, gclk_rate);
60062306a36Sopenharmony_ci	clk_prepare_enable(dd->gclk);
60162306a36Sopenharmony_ci	if (ret) {
60262306a36Sopenharmony_ci		dev_err(comp->dev, "unable to set rate %lu to GCLK: %d\n",
60362306a36Sopenharmony_ci			gclk_rate, ret);
60462306a36Sopenharmony_ci		return ret;
60562306a36Sopenharmony_ci	}
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci	mr_val |= mchp_pdmc_mr_set_osr(dd->audio_filter_en, osr);
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci	mr_val |= FIELD_PREP(MCHP_PDMC_MR_SINCORDER_MASK, dd->sinc_order);
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci	dd->addr.maxburst = mchp_pdmc_period_to_maxburst(snd_pcm_lib_period_bytes(substream));
61262306a36Sopenharmony_ci	mr_val |= FIELD_PREP(MCHP_PDMC_MR_CHUNK_MASK, dd->addr.maxburst);
61362306a36Sopenharmony_ci	dev_dbg(comp->dev, "maxburst set to %d\n", dd->addr.maxburst);
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	snd_soc_component_update_bits(comp, MCHP_PDMC_MR,
61662306a36Sopenharmony_ci				      MCHP_PDMC_MR_OSR_MASK |
61762306a36Sopenharmony_ci				      MCHP_PDMC_MR_SINCORDER_MASK |
61862306a36Sopenharmony_ci				      MCHP_PDMC_MR_SINC_OSR_MASK |
61962306a36Sopenharmony_ci				      MCHP_PDMC_MR_CHUNK_MASK, mr_val);
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci	snd_soc_component_write(comp, MCHP_PDMC_CFGR, cfgr_val);
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci	return 0;
62462306a36Sopenharmony_ci}
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_cistatic void mchp_pdmc_noise_filter_workaround(struct mchp_pdmc *dd)
62762306a36Sopenharmony_ci{
62862306a36Sopenharmony_ci	u32 tmp, steps = 16;
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	/*
63162306a36Sopenharmony_ci	 * PDMC doesn't wait for microphones' startup time thus the acquisition
63262306a36Sopenharmony_ci	 * may start before the microphones are ready leading to poc noises at
63362306a36Sopenharmony_ci	 * the beginning of capture. To avoid this, we need to wait 50ms (in
63462306a36Sopenharmony_ci	 * normal startup procedure) or 150 ms (worst case after resume from sleep
63562306a36Sopenharmony_ci	 * states) after microphones are enabled and then clear the FIFOs (by
63662306a36Sopenharmony_ci	 * reading the RHR 16 times) and possible interrupts before continuing.
63762306a36Sopenharmony_ci	 * Also, for this to work the DMA needs to be started after interrupts
63862306a36Sopenharmony_ci	 * are enabled.
63962306a36Sopenharmony_ci	 */
64062306a36Sopenharmony_ci	usleep_range(dd->startup_delay_us, dd->startup_delay_us + 5);
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci	while (steps--)
64362306a36Sopenharmony_ci		regmap_read(dd->regmap, MCHP_PDMC_RHR, &tmp);
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci	/* Clear interrupts. */
64662306a36Sopenharmony_ci	regmap_read(dd->regmap, MCHP_PDMC_ISR, &tmp);
64762306a36Sopenharmony_ci}
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_cistatic int mchp_pdmc_trigger(struct snd_pcm_substream *substream,
65062306a36Sopenharmony_ci			     int cmd, struct snd_soc_dai *dai)
65162306a36Sopenharmony_ci{
65262306a36Sopenharmony_ci	struct mchp_pdmc *dd = snd_soc_dai_get_drvdata(dai);
65362306a36Sopenharmony_ci	struct snd_soc_component *cpu = dai->component;
65462306a36Sopenharmony_ci#ifdef DEBUG
65562306a36Sopenharmony_ci	u32 val;
65662306a36Sopenharmony_ci#endif
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci	switch (cmd) {
65962306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_RESUME:
66062306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
66162306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
66262306a36Sopenharmony_ci		snd_soc_component_update_bits(cpu, MCHP_PDMC_MR,
66362306a36Sopenharmony_ci					      MCHP_PDMC_MR_PDMCEN_MASK,
66462306a36Sopenharmony_ci					      dd->pdmcen);
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci		mchp_pdmc_noise_filter_workaround(dd);
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci		/* Enable interrupts. */
66962306a36Sopenharmony_ci		regmap_write(dd->regmap, MCHP_PDMC_IER, dd->suspend_irq |
67062306a36Sopenharmony_ci			     MCHP_PDMC_IR_RXOVR | MCHP_PDMC_IR_RXUDR);
67162306a36Sopenharmony_ci		dd->suspend_irq = 0;
67262306a36Sopenharmony_ci		break;
67362306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_SUSPEND:
67462306a36Sopenharmony_ci		regmap_read(dd->regmap, MCHP_PDMC_IMR, &dd->suspend_irq);
67562306a36Sopenharmony_ci		fallthrough;
67662306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
67762306a36Sopenharmony_ci		/* Disable overrun and underrun error interrupts */
67862306a36Sopenharmony_ci		regmap_write(dd->regmap, MCHP_PDMC_IDR, dd->suspend_irq |
67962306a36Sopenharmony_ci			     MCHP_PDMC_IR_RXOVR | MCHP_PDMC_IR_RXUDR);
68062306a36Sopenharmony_ci		fallthrough;
68162306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
68262306a36Sopenharmony_ci		snd_soc_component_update_bits(cpu, MCHP_PDMC_MR,
68362306a36Sopenharmony_ci					      MCHP_PDMC_MR_PDMCEN_MASK, 0);
68462306a36Sopenharmony_ci		break;
68562306a36Sopenharmony_ci	default:
68662306a36Sopenharmony_ci		return -EINVAL;
68762306a36Sopenharmony_ci	}
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci#ifdef DEBUG
69062306a36Sopenharmony_ci	regmap_read(dd->regmap, MCHP_PDMC_MR, &val);
69162306a36Sopenharmony_ci	dev_dbg(dd->dev, "MR (0x%02x): 0x%08x\n", MCHP_PDMC_MR, val);
69262306a36Sopenharmony_ci	regmap_read(dd->regmap, MCHP_PDMC_CFGR, &val);
69362306a36Sopenharmony_ci	dev_dbg(dd->dev, "CFGR (0x%02x): 0x%08x\n", MCHP_PDMC_CFGR, val);
69462306a36Sopenharmony_ci	regmap_read(dd->regmap, MCHP_PDMC_IMR, &val);
69562306a36Sopenharmony_ci	dev_dbg(dd->dev, "IMR (0x%02x): 0x%08x\n", MCHP_PDMC_IMR, val);
69662306a36Sopenharmony_ci#endif
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	return 0;
69962306a36Sopenharmony_ci}
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_cistatic int mchp_pdmc_add_chmap_ctls(struct snd_pcm *pcm, struct mchp_pdmc *dd)
70262306a36Sopenharmony_ci{
70362306a36Sopenharmony_ci	struct mchp_pdmc_chmap *info;
70462306a36Sopenharmony_ci	struct snd_kcontrol_new knew = {
70562306a36Sopenharmony_ci		.iface = SNDRV_CTL_ELEM_IFACE_PCM,
70662306a36Sopenharmony_ci		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
70762306a36Sopenharmony_ci			SNDRV_CTL_ELEM_ACCESS_TLV_READ |
70862306a36Sopenharmony_ci			SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK,
70962306a36Sopenharmony_ci		.info = mchp_pdmc_chmap_ctl_info,
71062306a36Sopenharmony_ci		.get = mchp_pdmc_chmap_ctl_get,
71162306a36Sopenharmony_ci		.put = mchp_pdmc_chmap_ctl_put,
71262306a36Sopenharmony_ci		.tlv.c = mchp_pdmc_chmap_ctl_tlv,
71362306a36Sopenharmony_ci	};
71462306a36Sopenharmony_ci	int err;
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci	if (WARN_ON(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].chmap_kctl))
71762306a36Sopenharmony_ci		return -EBUSY;
71862306a36Sopenharmony_ci	info = kzalloc(sizeof(*info), GFP_KERNEL);
71962306a36Sopenharmony_ci	if (!info)
72062306a36Sopenharmony_ci		return -ENOMEM;
72162306a36Sopenharmony_ci	info->pcm = pcm;
72262306a36Sopenharmony_ci	info->dd = dd;
72362306a36Sopenharmony_ci	info->chmap = mchp_pdmc_std_chmaps;
72462306a36Sopenharmony_ci	knew.name = "Capture Channel Map";
72562306a36Sopenharmony_ci	knew.device = pcm->device;
72662306a36Sopenharmony_ci	knew.count = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream_count;
72762306a36Sopenharmony_ci	info->kctl = snd_ctl_new1(&knew, info);
72862306a36Sopenharmony_ci	if (!info->kctl) {
72962306a36Sopenharmony_ci		kfree(info);
73062306a36Sopenharmony_ci		return -ENOMEM;
73162306a36Sopenharmony_ci	}
73262306a36Sopenharmony_ci	info->kctl->private_free = mchp_pdmc_chmap_ctl_private_free;
73362306a36Sopenharmony_ci	err = snd_ctl_add(pcm->card, info->kctl);
73462306a36Sopenharmony_ci	if (err < 0)
73562306a36Sopenharmony_ci		return err;
73662306a36Sopenharmony_ci	pcm->streams[SNDRV_PCM_STREAM_CAPTURE].chmap_kctl = info->kctl;
73762306a36Sopenharmony_ci	return 0;
73862306a36Sopenharmony_ci}
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_cistatic int mchp_pdmc_pcm_new(struct snd_soc_pcm_runtime *rtd,
74162306a36Sopenharmony_ci			     struct snd_soc_dai *dai)
74262306a36Sopenharmony_ci{
74362306a36Sopenharmony_ci	struct mchp_pdmc *dd = snd_soc_dai_get_drvdata(dai);
74462306a36Sopenharmony_ci	int ret;
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci	ret = mchp_pdmc_add_chmap_ctls(rtd->pcm, dd);
74762306a36Sopenharmony_ci	if (ret < 0)
74862306a36Sopenharmony_ci		dev_err(dd->dev, "failed to add channel map controls: %d\n", ret);
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci	return ret;
75162306a36Sopenharmony_ci}
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_cistatic const struct snd_soc_dai_ops mchp_pdmc_dai_ops = {
75462306a36Sopenharmony_ci	.probe		= mchp_pdmc_dai_probe,
75562306a36Sopenharmony_ci	.set_fmt	= mchp_pdmc_set_fmt,
75662306a36Sopenharmony_ci	.startup	= mchp_pdmc_startup,
75762306a36Sopenharmony_ci	.hw_params	= mchp_pdmc_hw_params,
75862306a36Sopenharmony_ci	.trigger	= mchp_pdmc_trigger,
75962306a36Sopenharmony_ci	.pcm_new	= &mchp_pdmc_pcm_new,
76062306a36Sopenharmony_ci};
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_cistatic struct snd_soc_dai_driver mchp_pdmc_dai = {
76362306a36Sopenharmony_ci	.capture = {
76462306a36Sopenharmony_ci		.stream_name	= "Capture",
76562306a36Sopenharmony_ci		.channels_min	= 1,
76662306a36Sopenharmony_ci		.channels_max	= 4,
76762306a36Sopenharmony_ci		.rate_min	= 8000,
76862306a36Sopenharmony_ci		.rate_max	= 192000,
76962306a36Sopenharmony_ci		.rates		= SNDRV_PCM_RATE_KNOT,
77062306a36Sopenharmony_ci		.formats	= SNDRV_PCM_FMTBIT_S24_LE,
77162306a36Sopenharmony_ci	},
77262306a36Sopenharmony_ci	.ops = &mchp_pdmc_dai_ops,
77362306a36Sopenharmony_ci};
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci/* PDMC interrupt handler */
77662306a36Sopenharmony_cistatic irqreturn_t mchp_pdmc_interrupt(int irq, void *dev_id)
77762306a36Sopenharmony_ci{
77862306a36Sopenharmony_ci	struct mchp_pdmc *dd = dev_id;
77962306a36Sopenharmony_ci	u32 isr, msr, pending;
78062306a36Sopenharmony_ci	irqreturn_t ret = IRQ_NONE;
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci	regmap_read(dd->regmap, MCHP_PDMC_ISR, &isr);
78362306a36Sopenharmony_ci	regmap_read(dd->regmap, MCHP_PDMC_IMR, &msr);
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci	pending = isr & msr;
78662306a36Sopenharmony_ci	dev_dbg(dd->dev, "ISR (0x%02x): 0x%08x, IMR (0x%02x): 0x%08x, pending: 0x%08x\n",
78762306a36Sopenharmony_ci		MCHP_PDMC_ISR, isr, MCHP_PDMC_IMR, msr, pending);
78862306a36Sopenharmony_ci	if (!pending)
78962306a36Sopenharmony_ci		return IRQ_NONE;
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci	if (pending & MCHP_PDMC_IR_RXUDR) {
79262306a36Sopenharmony_ci		dev_warn(dd->dev, "underrun detected\n");
79362306a36Sopenharmony_ci		regmap_write(dd->regmap, MCHP_PDMC_IDR, MCHP_PDMC_IR_RXUDR);
79462306a36Sopenharmony_ci		ret = IRQ_HANDLED;
79562306a36Sopenharmony_ci	}
79662306a36Sopenharmony_ci	if (pending & MCHP_PDMC_IR_RXOVR) {
79762306a36Sopenharmony_ci		dev_warn(dd->dev, "overrun detected\n");
79862306a36Sopenharmony_ci		regmap_write(dd->regmap, MCHP_PDMC_IDR, MCHP_PDMC_IR_RXOVR);
79962306a36Sopenharmony_ci		ret = IRQ_HANDLED;
80062306a36Sopenharmony_ci	}
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci	return ret;
80362306a36Sopenharmony_ci}
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_ci/* regmap configuration */
80662306a36Sopenharmony_cistatic bool mchp_pdmc_readable_reg(struct device *dev, unsigned int reg)
80762306a36Sopenharmony_ci{
80862306a36Sopenharmony_ci	switch (reg) {
80962306a36Sopenharmony_ci	case MCHP_PDMC_MR:
81062306a36Sopenharmony_ci	case MCHP_PDMC_CFGR:
81162306a36Sopenharmony_ci	case MCHP_PDMC_IMR:
81262306a36Sopenharmony_ci	case MCHP_PDMC_ISR:
81362306a36Sopenharmony_ci	case MCHP_PDMC_RHR:
81462306a36Sopenharmony_ci	case MCHP_PDMC_VER:
81562306a36Sopenharmony_ci		return true;
81662306a36Sopenharmony_ci	default:
81762306a36Sopenharmony_ci		return false;
81862306a36Sopenharmony_ci	}
81962306a36Sopenharmony_ci}
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_cistatic bool mchp_pdmc_writeable_reg(struct device *dev, unsigned int reg)
82262306a36Sopenharmony_ci{
82362306a36Sopenharmony_ci	switch (reg) {
82462306a36Sopenharmony_ci	case MCHP_PDMC_CR:
82562306a36Sopenharmony_ci	case MCHP_PDMC_MR:
82662306a36Sopenharmony_ci	case MCHP_PDMC_CFGR:
82762306a36Sopenharmony_ci	case MCHP_PDMC_IER:
82862306a36Sopenharmony_ci	case MCHP_PDMC_IDR:
82962306a36Sopenharmony_ci		return true;
83062306a36Sopenharmony_ci	default:
83162306a36Sopenharmony_ci		return false;
83262306a36Sopenharmony_ci	}
83362306a36Sopenharmony_ci}
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_cistatic bool mchp_pdmc_volatile_reg(struct device *dev, unsigned int reg)
83662306a36Sopenharmony_ci{
83762306a36Sopenharmony_ci	switch (reg) {
83862306a36Sopenharmony_ci	case MCHP_PDMC_ISR:
83962306a36Sopenharmony_ci	case MCHP_PDMC_RHR:
84062306a36Sopenharmony_ci		return true;
84162306a36Sopenharmony_ci	default:
84262306a36Sopenharmony_ci		return false;
84362306a36Sopenharmony_ci	}
84462306a36Sopenharmony_ci}
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_cistatic bool mchp_pdmc_precious_reg(struct device *dev, unsigned int reg)
84762306a36Sopenharmony_ci{
84862306a36Sopenharmony_ci	switch (reg) {
84962306a36Sopenharmony_ci	case MCHP_PDMC_RHR:
85062306a36Sopenharmony_ci	case MCHP_PDMC_ISR:
85162306a36Sopenharmony_ci		return true;
85262306a36Sopenharmony_ci	default:
85362306a36Sopenharmony_ci		return false;
85462306a36Sopenharmony_ci	}
85562306a36Sopenharmony_ci}
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_cistatic const struct regmap_config mchp_pdmc_regmap_config = {
85862306a36Sopenharmony_ci	.reg_bits	= 32,
85962306a36Sopenharmony_ci	.reg_stride	= 4,
86062306a36Sopenharmony_ci	.val_bits	= 32,
86162306a36Sopenharmony_ci	.max_register	= MCHP_PDMC_VER,
86262306a36Sopenharmony_ci	.readable_reg	= mchp_pdmc_readable_reg,
86362306a36Sopenharmony_ci	.writeable_reg	= mchp_pdmc_writeable_reg,
86462306a36Sopenharmony_ci	.precious_reg	= mchp_pdmc_precious_reg,
86562306a36Sopenharmony_ci	.volatile_reg	= mchp_pdmc_volatile_reg,
86662306a36Sopenharmony_ci	.cache_type	= REGCACHE_FLAT,
86762306a36Sopenharmony_ci};
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_cistatic int mchp_pdmc_dt_init(struct mchp_pdmc *dd)
87062306a36Sopenharmony_ci{
87162306a36Sopenharmony_ci	struct device_node *np = dd->dev->of_node;
87262306a36Sopenharmony_ci	bool mic_ch[MCHP_PDMC_DS_NO][MCHP_PDMC_EDGE_NO] = {0};
87362306a36Sopenharmony_ci	int i;
87462306a36Sopenharmony_ci	int ret;
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_ci	if (!np) {
87762306a36Sopenharmony_ci		dev_err(dd->dev, "device node not found\n");
87862306a36Sopenharmony_ci		return -EINVAL;
87962306a36Sopenharmony_ci	}
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_ci	dd->mic_no = of_property_count_u32_elems(np, "microchip,mic-pos");
88262306a36Sopenharmony_ci	if (dd->mic_no < 0) {
88362306a36Sopenharmony_ci		dev_err(dd->dev, "failed to get microchip,mic-pos: %d",
88462306a36Sopenharmony_ci			dd->mic_no);
88562306a36Sopenharmony_ci		return dd->mic_no;
88662306a36Sopenharmony_ci	}
88762306a36Sopenharmony_ci	if (!dd->mic_no || dd->mic_no % 2 ||
88862306a36Sopenharmony_ci	    dd->mic_no / 2 > MCHP_PDMC_MAX_CHANNELS) {
88962306a36Sopenharmony_ci		dev_err(dd->dev, "invalid array length for microchip,mic-pos: %d",
89062306a36Sopenharmony_ci			dd->mic_no);
89162306a36Sopenharmony_ci		return -EINVAL;
89262306a36Sopenharmony_ci	}
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ci	dd->mic_no /= 2;
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_ci	dev_info(dd->dev, "%d PDM microphones declared\n", dd->mic_no);
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci	/*
89962306a36Sopenharmony_ci	 * by default, we consider the order of microphones in
90062306a36Sopenharmony_ci	 * microchip,mic-pos to be the same with the channel mapping;
90162306a36Sopenharmony_ci	 * 1st microphone channel 0, 2nd microphone channel 1, etc.
90262306a36Sopenharmony_ci	 */
90362306a36Sopenharmony_ci	for (i = 0; i < dd->mic_no; i++) {
90462306a36Sopenharmony_ci		int ds;
90562306a36Sopenharmony_ci		int edge;
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci		ret = of_property_read_u32_index(np, "microchip,mic-pos", i * 2,
90862306a36Sopenharmony_ci						 &ds);
90962306a36Sopenharmony_ci		if (ret) {
91062306a36Sopenharmony_ci			dev_err(dd->dev,
91162306a36Sopenharmony_ci				"failed to get value no %d value from microchip,mic-pos: %d",
91262306a36Sopenharmony_ci				i * 2, ret);
91362306a36Sopenharmony_ci			return ret;
91462306a36Sopenharmony_ci		}
91562306a36Sopenharmony_ci		if (ds >= MCHP_PDMC_DS_NO) {
91662306a36Sopenharmony_ci			dev_err(dd->dev,
91762306a36Sopenharmony_ci				"invalid DS index in microchip,mic-pos array: %d",
91862306a36Sopenharmony_ci				ds);
91962306a36Sopenharmony_ci			return -EINVAL;
92062306a36Sopenharmony_ci		}
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_ci		ret = of_property_read_u32_index(np, "microchip,mic-pos", i * 2 + 1,
92362306a36Sopenharmony_ci						 &edge);
92462306a36Sopenharmony_ci		if (ret) {
92562306a36Sopenharmony_ci			dev_err(dd->dev,
92662306a36Sopenharmony_ci				"failed to get value no %d value from microchip,mic-pos: %d",
92762306a36Sopenharmony_ci				i * 2 + 1, ret);
92862306a36Sopenharmony_ci			return ret;
92962306a36Sopenharmony_ci		}
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci		if (edge != MCHP_PDMC_CLK_POSITIVE &&
93262306a36Sopenharmony_ci		    edge != MCHP_PDMC_CLK_NEGATIVE) {
93362306a36Sopenharmony_ci			dev_err(dd->dev,
93462306a36Sopenharmony_ci				"invalid edge in microchip,mic-pos array: %d", edge);
93562306a36Sopenharmony_ci			return -EINVAL;
93662306a36Sopenharmony_ci		}
93762306a36Sopenharmony_ci		if (mic_ch[ds][edge]) {
93862306a36Sopenharmony_ci			dev_err(dd->dev,
93962306a36Sopenharmony_ci				"duplicated mic (DS %d, edge %d) in microchip,mic-pos array",
94062306a36Sopenharmony_ci				ds, edge);
94162306a36Sopenharmony_ci			return -EINVAL;
94262306a36Sopenharmony_ci		}
94362306a36Sopenharmony_ci		mic_ch[ds][edge] = true;
94462306a36Sopenharmony_ci		dd->channel_mic_map[i].ds_pos = ds;
94562306a36Sopenharmony_ci		dd->channel_mic_map[i].clk_edge = edge;
94662306a36Sopenharmony_ci	}
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_ci	dd->startup_delay_us = 150000;
94962306a36Sopenharmony_ci	of_property_read_u32(np, "microchip,startup-delay-us", &dd->startup_delay_us);
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_ci	return 0;
95262306a36Sopenharmony_ci}
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_ci/* used to clean the channel index found on RHR's MSB */
95562306a36Sopenharmony_cistatic int mchp_pdmc_process(struct snd_pcm_substream *substream,
95662306a36Sopenharmony_ci			     int channel, unsigned long hwoff,
95762306a36Sopenharmony_ci			     unsigned long bytes)
95862306a36Sopenharmony_ci{
95962306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
96062306a36Sopenharmony_ci	u8 *dma_ptr = runtime->dma_area + hwoff +
96162306a36Sopenharmony_ci		      channel * (runtime->dma_bytes / runtime->channels);
96262306a36Sopenharmony_ci	u8 *dma_ptr_end = dma_ptr + bytes;
96362306a36Sopenharmony_ci	unsigned int sample_size = samples_to_bytes(runtime, 1);
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_ci	for (; dma_ptr < dma_ptr_end; dma_ptr += sample_size)
96662306a36Sopenharmony_ci		*dma_ptr = 0;
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_ci	return 0;
96962306a36Sopenharmony_ci}
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_cistatic struct snd_dmaengine_pcm_config mchp_pdmc_config = {
97262306a36Sopenharmony_ci	.process = mchp_pdmc_process,
97362306a36Sopenharmony_ci	.prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
97462306a36Sopenharmony_ci};
97562306a36Sopenharmony_ci
97662306a36Sopenharmony_cistatic int mchp_pdmc_runtime_suspend(struct device *dev)
97762306a36Sopenharmony_ci{
97862306a36Sopenharmony_ci	struct mchp_pdmc *dd = dev_get_drvdata(dev);
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci	regcache_cache_only(dd->regmap, true);
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci	clk_disable_unprepare(dd->gclk);
98362306a36Sopenharmony_ci	clk_disable_unprepare(dd->pclk);
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci	return 0;
98662306a36Sopenharmony_ci}
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_cistatic int mchp_pdmc_runtime_resume(struct device *dev)
98962306a36Sopenharmony_ci{
99062306a36Sopenharmony_ci	struct mchp_pdmc *dd = dev_get_drvdata(dev);
99162306a36Sopenharmony_ci	int ret;
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_ci	ret = clk_prepare_enable(dd->pclk);
99462306a36Sopenharmony_ci	if (ret) {
99562306a36Sopenharmony_ci		dev_err(dd->dev,
99662306a36Sopenharmony_ci			"failed to enable the peripheral clock: %d\n", ret);
99762306a36Sopenharmony_ci		return ret;
99862306a36Sopenharmony_ci	}
99962306a36Sopenharmony_ci	ret = clk_prepare_enable(dd->gclk);
100062306a36Sopenharmony_ci	if (ret) {
100162306a36Sopenharmony_ci		dev_err(dd->dev,
100262306a36Sopenharmony_ci			"failed to enable generic clock: %d\n", ret);
100362306a36Sopenharmony_ci		goto disable_pclk;
100462306a36Sopenharmony_ci	}
100562306a36Sopenharmony_ci
100662306a36Sopenharmony_ci	regcache_cache_only(dd->regmap, false);
100762306a36Sopenharmony_ci	regcache_mark_dirty(dd->regmap);
100862306a36Sopenharmony_ci	ret = regcache_sync(dd->regmap);
100962306a36Sopenharmony_ci	if (ret) {
101062306a36Sopenharmony_ci		regcache_cache_only(dd->regmap, true);
101162306a36Sopenharmony_ci		clk_disable_unprepare(dd->gclk);
101262306a36Sopenharmony_cidisable_pclk:
101362306a36Sopenharmony_ci		clk_disable_unprepare(dd->pclk);
101462306a36Sopenharmony_ci	}
101562306a36Sopenharmony_ci
101662306a36Sopenharmony_ci	return ret;
101762306a36Sopenharmony_ci}
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_cistatic int mchp_pdmc_probe(struct platform_device *pdev)
102062306a36Sopenharmony_ci{
102162306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
102262306a36Sopenharmony_ci	struct mchp_pdmc *dd;
102362306a36Sopenharmony_ci	struct resource *res;
102462306a36Sopenharmony_ci	void __iomem *io_base;
102562306a36Sopenharmony_ci	u32 version;
102662306a36Sopenharmony_ci	int irq;
102762306a36Sopenharmony_ci	int ret;
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_ci	dd = devm_kzalloc(dev, sizeof(*dd), GFP_KERNEL);
103062306a36Sopenharmony_ci	if (!dd)
103162306a36Sopenharmony_ci		return -ENOMEM;
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_ci	dd->dev = &pdev->dev;
103462306a36Sopenharmony_ci	ret = mchp_pdmc_dt_init(dd);
103562306a36Sopenharmony_ci	if (ret < 0)
103662306a36Sopenharmony_ci		return ret;
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ci	irq = platform_get_irq(pdev, 0);
103962306a36Sopenharmony_ci	if (irq < 0)
104062306a36Sopenharmony_ci		return irq;
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_ci	dd->pclk = devm_clk_get(dev, "pclk");
104362306a36Sopenharmony_ci	if (IS_ERR(dd->pclk)) {
104462306a36Sopenharmony_ci		ret = PTR_ERR(dd->pclk);
104562306a36Sopenharmony_ci		dev_err(dev, "failed to get peripheral clock: %d\n", ret);
104662306a36Sopenharmony_ci		return ret;
104762306a36Sopenharmony_ci	}
104862306a36Sopenharmony_ci
104962306a36Sopenharmony_ci	dd->gclk = devm_clk_get(dev, "gclk");
105062306a36Sopenharmony_ci	if (IS_ERR(dd->gclk)) {
105162306a36Sopenharmony_ci		ret = PTR_ERR(dd->gclk);
105262306a36Sopenharmony_ci		dev_err(dev, "failed to get GCK: %d\n", ret);
105362306a36Sopenharmony_ci		return ret;
105462306a36Sopenharmony_ci	}
105562306a36Sopenharmony_ci
105662306a36Sopenharmony_ci	io_base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
105762306a36Sopenharmony_ci	if (IS_ERR(io_base)) {
105862306a36Sopenharmony_ci		ret = PTR_ERR(io_base);
105962306a36Sopenharmony_ci		dev_err(dev, "failed to remap register memory: %d\n", ret);
106062306a36Sopenharmony_ci		return ret;
106162306a36Sopenharmony_ci	}
106262306a36Sopenharmony_ci
106362306a36Sopenharmony_ci	dd->regmap = devm_regmap_init_mmio(dev, io_base,
106462306a36Sopenharmony_ci					   &mchp_pdmc_regmap_config);
106562306a36Sopenharmony_ci	if (IS_ERR(dd->regmap)) {
106662306a36Sopenharmony_ci		ret = PTR_ERR(dd->regmap);
106762306a36Sopenharmony_ci		dev_err(dev, "failed to init register map: %d\n", ret);
106862306a36Sopenharmony_ci		return ret;
106962306a36Sopenharmony_ci	}
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ci	ret = devm_request_irq(dev, irq, mchp_pdmc_interrupt, 0,
107262306a36Sopenharmony_ci			       dev_name(&pdev->dev), dd);
107362306a36Sopenharmony_ci	if (ret < 0) {
107462306a36Sopenharmony_ci		dev_err(dev, "can't register ISR for IRQ %u (ret=%i)\n",
107562306a36Sopenharmony_ci			irq, ret);
107662306a36Sopenharmony_ci		return ret;
107762306a36Sopenharmony_ci	}
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_ci	/* by default audio filter is enabled and the SINC Filter order
108062306a36Sopenharmony_ci	 * will be set to the recommended value, 3
108162306a36Sopenharmony_ci	 */
108262306a36Sopenharmony_ci	dd->audio_filter_en = true;
108362306a36Sopenharmony_ci	dd->sinc_order = 3;
108462306a36Sopenharmony_ci
108562306a36Sopenharmony_ci	dd->addr.addr = (dma_addr_t)res->start + MCHP_PDMC_RHR;
108662306a36Sopenharmony_ci	platform_set_drvdata(pdev, dd);
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci	pm_runtime_enable(dd->dev);
108962306a36Sopenharmony_ci	if (!pm_runtime_enabled(dd->dev)) {
109062306a36Sopenharmony_ci		ret = mchp_pdmc_runtime_resume(dd->dev);
109162306a36Sopenharmony_ci		if (ret)
109262306a36Sopenharmony_ci			return ret;
109362306a36Sopenharmony_ci	}
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci	/* register platform */
109662306a36Sopenharmony_ci	ret = devm_snd_dmaengine_pcm_register(dev, &mchp_pdmc_config, 0);
109762306a36Sopenharmony_ci	if (ret) {
109862306a36Sopenharmony_ci		dev_err(dev, "could not register platform: %d\n", ret);
109962306a36Sopenharmony_ci		goto pm_runtime_suspend;
110062306a36Sopenharmony_ci	}
110162306a36Sopenharmony_ci
110262306a36Sopenharmony_ci	ret = devm_snd_soc_register_component(dev, &mchp_pdmc_dai_component,
110362306a36Sopenharmony_ci					      &mchp_pdmc_dai, 1);
110462306a36Sopenharmony_ci	if (ret) {
110562306a36Sopenharmony_ci		dev_err(dev, "could not register CPU DAI: %d\n", ret);
110662306a36Sopenharmony_ci		goto pm_runtime_suspend;
110762306a36Sopenharmony_ci	}
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_ci	/* print IP version */
111062306a36Sopenharmony_ci	regmap_read(dd->regmap, MCHP_PDMC_VER, &version);
111162306a36Sopenharmony_ci	dev_info(dd->dev, "hw version: %#lx\n",
111262306a36Sopenharmony_ci		 version & MCHP_PDMC_VER_VERSION);
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_ci	return 0;
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_cipm_runtime_suspend:
111762306a36Sopenharmony_ci	if (!pm_runtime_status_suspended(dd->dev))
111862306a36Sopenharmony_ci		mchp_pdmc_runtime_suspend(dd->dev);
111962306a36Sopenharmony_ci	pm_runtime_disable(dd->dev);
112062306a36Sopenharmony_ci
112162306a36Sopenharmony_ci	return ret;
112262306a36Sopenharmony_ci}
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_cistatic void mchp_pdmc_remove(struct platform_device *pdev)
112562306a36Sopenharmony_ci{
112662306a36Sopenharmony_ci	struct mchp_pdmc *dd = platform_get_drvdata(pdev);
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_ci	if (!pm_runtime_status_suspended(dd->dev))
112962306a36Sopenharmony_ci		mchp_pdmc_runtime_suspend(dd->dev);
113062306a36Sopenharmony_ci
113162306a36Sopenharmony_ci	pm_runtime_disable(dd->dev);
113262306a36Sopenharmony_ci}
113362306a36Sopenharmony_ci
113462306a36Sopenharmony_cistatic const struct of_device_id mchp_pdmc_of_match[] = {
113562306a36Sopenharmony_ci	{
113662306a36Sopenharmony_ci		.compatible = "microchip,sama7g5-pdmc",
113762306a36Sopenharmony_ci	}, {
113862306a36Sopenharmony_ci		/* sentinel */
113962306a36Sopenharmony_ci	}
114062306a36Sopenharmony_ci};
114162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, mchp_pdmc_of_match);
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_cistatic const struct dev_pm_ops mchp_pdmc_pm_ops = {
114462306a36Sopenharmony_ci	SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
114562306a36Sopenharmony_ci	RUNTIME_PM_OPS(mchp_pdmc_runtime_suspend, mchp_pdmc_runtime_resume,
114662306a36Sopenharmony_ci		       NULL)
114762306a36Sopenharmony_ci};
114862306a36Sopenharmony_ci
114962306a36Sopenharmony_cistatic struct platform_driver mchp_pdmc_driver = {
115062306a36Sopenharmony_ci	.driver	= {
115162306a36Sopenharmony_ci		.name		= "mchp-pdmc",
115262306a36Sopenharmony_ci		.of_match_table	= of_match_ptr(mchp_pdmc_of_match),
115362306a36Sopenharmony_ci		.pm		= pm_ptr(&mchp_pdmc_pm_ops),
115462306a36Sopenharmony_ci	},
115562306a36Sopenharmony_ci	.probe	= mchp_pdmc_probe,
115662306a36Sopenharmony_ci	.remove_new = mchp_pdmc_remove,
115762306a36Sopenharmony_ci};
115862306a36Sopenharmony_cimodule_platform_driver(mchp_pdmc_driver);
115962306a36Sopenharmony_ci
116062306a36Sopenharmony_ciMODULE_DESCRIPTION("Microchip PDMC driver under ALSA SoC architecture");
116162306a36Sopenharmony_ciMODULE_AUTHOR("Codrin Ciubotariu <codrin.ciubotariu@microchip.com>");
116262306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
1163