// SPDX-License-Identifier: GPL-2.0
//
// Driver for Microchip Pulse Density Microphone Controller (PDMC) interfaces
//
// Copyright (C) 2019-2022 Microchip Technology Inc. and its subsidiaries
//
// Author: Codrin Ciubotariu <codrin.ciubotariu@microchip.com>
#include <dt-bindings/sound/microchip,pdmc.h>
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <sound/core.h>
#include <sound/dmaengine_pcm.h>
#include <sound/pcm_params.h>
#include <sound/tlv.h>
/*
* ---- PDMC Register map ----
*/
#define MCHP_PDMC_CR 0x00 /* Control Register */
#define MCHP_PDMC_MR 0x04 /* Mode Register */
#define MCHP_PDMC_CFGR 0x08 /* Configuration Register */
#define MCHP_PDMC_RHR 0x0C /* Receive Holding Register */
#define MCHP_PDMC_IER 0x14 /* Interrupt Enable Register */
#define MCHP_PDMC_IDR 0x18 /* Interrupt Disable Register */
#define MCHP_PDMC_IMR 0x1C /* Interrupt Mask Register */
#define MCHP_PDMC_ISR 0x20 /* Interrupt Status Register */
#define MCHP_PDMC_VER 0x50 /* Version Register */
/*
* ---- Control Register (Write-only) ----
*/
#define MCHP_PDMC_CR_SWRST BIT(0) /* Software Reset */
/*
* ---- Mode Register (Read/Write) ----
*/
#define MCHP_PDMC_MR_PDMCEN_MASK GENMASK(3, 0)
#define MCHP_PDMC_MR_PDMCEN(ch) (BIT(ch) & MCHP_PDMC_MR_PDMCEN_MASK)
#define MCHP_PDMC_MR_OSR_MASK GENMASK(17, 16)
#define MCHP_PDMC_MR_OSR64 (1 << 16)
#define MCHP_PDMC_MR_OSR128 (2 << 16)
#define MCHP_PDMC_MR_OSR256 (3 << 16)
#define MCHP_PDMC_MR_SINCORDER_MASK GENMASK(23, 20)
#define MCHP_PDMC_MR_SINC_OSR_MASK GENMASK(27, 24)
#define MCHP_PDMC_MR_SINC_OSR_DIS (0 << 24)
#define MCHP_PDMC_MR_SINC_OSR_8 (1 << 24)
#define MCHP_PDMC_MR_SINC_OSR_16 (2 << 24)
#define MCHP_PDMC_MR_SINC_OSR_32 (3 << 24)
#define MCHP_PDMC_MR_SINC_OSR_64 (4 << 24)
#define MCHP_PDMC_MR_SINC_OSR_128 (5 << 24)
#define MCHP_PDMC_MR_SINC_OSR_256 (6 << 24)
#define MCHP_PDMC_MR_CHUNK_MASK GENMASK(31, 28)
/*
* ---- Configuration Register (Read/Write) ----
*/
#define MCHP_PDMC_CFGR_BSSEL_MASK (BIT(0) | BIT(2) | BIT(4) | BIT(6))
#define MCHP_PDMC_CFGR_BSSEL(ch) BIT((ch) * 2)
#define MCHP_PDMC_CFGR_PDMSEL_MASK (BIT(16) | BIT(18) | BIT(20) | BIT(22))
#define MCHP_PDMC_CFGR_PDMSEL(ch) BIT((ch) * 2 + 16)
/*
* ---- Interrupt Enable/Disable/Mask/Status Registers ----
*/
#define MCHP_PDMC_IR_RXRDY BIT(0)
#define MCHP_PDMC_IR_RXEMPTY BIT(1)
#define MCHP_PDMC_IR_RXFULL BIT(2)
#define MCHP_PDMC_IR_RXCHUNK BIT(3)
#define MCHP_PDMC_IR_RXUDR BIT(4)
#define MCHP_PDMC_IR_RXOVR BIT(5)
/*
* ---- Version Register (Read-only) ----
*/
#define MCHP_PDMC_VER_VERSION GENMASK(11, 0)
#define MCHP_PDMC_MAX_CHANNELS 4
#define MCHP_PDMC_DS_NO 2
#define MCHP_PDMC_EDGE_NO 2
/*
* ---- DMA chunk size allowed ----
*/
#define MCHP_PDMC_DMA_8_WORD_CHUNK 8
#define MCHP_PDMC_DMA_4_WORD_CHUNK 4
#define MCHP_PDMC_DMA_2_WORD_CHUNK 2
#define MCHP_PDMC_DMA_1_WORD_CHUNK 1
#define DMA_BURST_ALIGNED(_p, _s, _w) !(_p % (_s * _w))
struct mic_map {
int ds_pos;
int clk_edge;
};
struct mchp_pdmc_chmap {
struct snd_pcm_chmap_elem *chmap;
struct mchp_pdmc *dd;
struct snd_pcm *pcm;
struct snd_kcontrol *kctl;
};
struct mchp_pdmc {
struct mic_map channel_mic_map[MCHP_PDMC_MAX_CHANNELS];
struct device *dev;
struct snd_dmaengine_dai_dma_data addr;
struct regmap *regmap;
struct clk *pclk;
struct clk *gclk;
u32 pdmcen;
u32 suspend_irq;
u32 startup_delay_us;
int mic_no;
int sinc_order;
bool audio_filter_en;
atomic_t busy_stream;
};
static const char *const mchp_pdmc_sinc_filter_order_text[] = {
"1", "2", "3", "4", "5"
};
static const unsigned int mchp_pdmc_sinc_filter_order_values[] = {
1, 2, 3, 4, 5,
};
static const struct soc_enum mchp_pdmc_sinc_filter_order_enum = {
.items = ARRAY_SIZE(mchp_pdmc_sinc_filter_order_text),
.texts = mchp_pdmc_sinc_filter_order_text,
.values = mchp_pdmc_sinc_filter_order_values,
};
static int mchp_pdmc_sinc_order_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *uvalue)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct mchp_pdmc *dd = snd_soc_component_get_drvdata(component);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned int item;
item = snd_soc_enum_val_to_item(e, dd->sinc_order);
uvalue->value.enumerated.item[0] = item;
return 0;
}
static int mchp_pdmc_sinc_order_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *uvalue)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct mchp_pdmc *dd = snd_soc_component_get_drvdata(component);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned int *item = uvalue->value.enumerated.item;
unsigned int val;
if (item[0] >= e->items)
return -EINVAL;
val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l;
if (atomic_read(&dd->busy_stream))
return -EBUSY;
if (val == dd->sinc_order)
return 0;
dd->sinc_order = val;
return 1;
}
static int mchp_pdmc_af_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *uvalue)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct mchp_pd