150291652SCodrin Ciubotariu // SPDX-License-Identifier: GPL-2.0 250291652SCodrin Ciubotariu // 350291652SCodrin Ciubotariu // Driver for Microchip Pulse Density Microphone Controller (PDMC) interfaces 450291652SCodrin Ciubotariu // 550291652SCodrin Ciubotariu // Copyright (C) 2019-2022 Microchip Technology Inc. and its subsidiaries 650291652SCodrin Ciubotariu // 750291652SCodrin Ciubotariu // Author: Codrin Ciubotariu <codrin.ciubotariu@microchip.com> 850291652SCodrin Ciubotariu 950291652SCodrin Ciubotariu #include <dt-bindings/sound/microchip,pdmc.h> 1050291652SCodrin Ciubotariu 1112974257SClaudiu Beznea #include <linux/bitfield.h> 1250291652SCodrin Ciubotariu #include <linux/clk.h> 1350291652SCodrin Ciubotariu #include <linux/module.h> 1450291652SCodrin Ciubotariu #include <linux/of.h> 15e8c8e9deSClaudiu Beznea #include <linux/pm_runtime.h> 1650291652SCodrin Ciubotariu #include <linux/regmap.h> 1750291652SCodrin Ciubotariu 1850291652SCodrin Ciubotariu #include <sound/core.h> 1950291652SCodrin Ciubotariu #include <sound/dmaengine_pcm.h> 2050291652SCodrin Ciubotariu #include <sound/pcm_params.h> 2150291652SCodrin Ciubotariu #include <sound/tlv.h> 2250291652SCodrin Ciubotariu 2350291652SCodrin Ciubotariu /* 2450291652SCodrin Ciubotariu * ---- PDMC Register map ---- 2550291652SCodrin Ciubotariu */ 2650291652SCodrin Ciubotariu #define MCHP_PDMC_CR 0x00 /* Control Register */ 2750291652SCodrin Ciubotariu #define MCHP_PDMC_MR 0x04 /* Mode Register */ 2850291652SCodrin Ciubotariu #define MCHP_PDMC_CFGR 0x08 /* Configuration Register */ 2950291652SCodrin Ciubotariu #define MCHP_PDMC_RHR 0x0C /* Receive Holding Register */ 3050291652SCodrin Ciubotariu #define MCHP_PDMC_IER 0x14 /* Interrupt Enable Register */ 3150291652SCodrin Ciubotariu #define MCHP_PDMC_IDR 0x18 /* Interrupt Disable Register */ 3250291652SCodrin Ciubotariu #define MCHP_PDMC_IMR 0x1C /* Interrupt Mask Register */ 3350291652SCodrin Ciubotariu #define MCHP_PDMC_ISR 0x20 /* Interrupt Status Register */ 3450291652SCodrin Ciubotariu #define MCHP_PDMC_VER 0x50 /* Version Register */ 3550291652SCodrin Ciubotariu 3650291652SCodrin Ciubotariu /* 3750291652SCodrin Ciubotariu * ---- Control Register (Write-only) ---- 3850291652SCodrin Ciubotariu */ 3950291652SCodrin Ciubotariu #define MCHP_PDMC_CR_SWRST BIT(0) /* Software Reset */ 4050291652SCodrin Ciubotariu 4150291652SCodrin Ciubotariu /* 4250291652SCodrin Ciubotariu * ---- Mode Register (Read/Write) ---- 4350291652SCodrin Ciubotariu */ 4450291652SCodrin Ciubotariu #define MCHP_PDMC_MR_PDMCEN_MASK GENMASK(3, 0) 4550291652SCodrin Ciubotariu #define MCHP_PDMC_MR_PDMCEN(ch) (BIT(ch) & MCHP_PDMC_MR_PDMCEN_MASK) 4650291652SCodrin Ciubotariu 4750291652SCodrin Ciubotariu #define MCHP_PDMC_MR_OSR_MASK GENMASK(17, 16) 4850291652SCodrin Ciubotariu #define MCHP_PDMC_MR_OSR64 (1 << 16) 4950291652SCodrin Ciubotariu #define MCHP_PDMC_MR_OSR128 (2 << 16) 5050291652SCodrin Ciubotariu #define MCHP_PDMC_MR_OSR256 (3 << 16) 5150291652SCodrin Ciubotariu 5250291652SCodrin Ciubotariu #define MCHP_PDMC_MR_SINCORDER_MASK GENMASK(23, 20) 5350291652SCodrin Ciubotariu 5450291652SCodrin Ciubotariu #define MCHP_PDMC_MR_SINC_OSR_MASK GENMASK(27, 24) 5550291652SCodrin Ciubotariu #define MCHP_PDMC_MR_SINC_OSR_DIS (0 << 24) 5650291652SCodrin Ciubotariu #define MCHP_PDMC_MR_SINC_OSR_8 (1 << 24) 5750291652SCodrin Ciubotariu #define MCHP_PDMC_MR_SINC_OSR_16 (2 << 24) 5850291652SCodrin Ciubotariu #define MCHP_PDMC_MR_SINC_OSR_32 (3 << 24) 5950291652SCodrin Ciubotariu #define MCHP_PDMC_MR_SINC_OSR_64 (4 << 24) 6050291652SCodrin Ciubotariu #define MCHP_PDMC_MR_SINC_OSR_128 (5 << 24) 6150291652SCodrin Ciubotariu #define MCHP_PDMC_MR_SINC_OSR_256 (6 << 24) 6250291652SCodrin Ciubotariu 6350291652SCodrin Ciubotariu #define MCHP_PDMC_MR_CHUNK_MASK GENMASK(31, 28) 6450291652SCodrin Ciubotariu 6550291652SCodrin Ciubotariu /* 6650291652SCodrin Ciubotariu * ---- Configuration Register (Read/Write) ---- 6750291652SCodrin Ciubotariu */ 6850291652SCodrin Ciubotariu #define MCHP_PDMC_CFGR_BSSEL_MASK (BIT(0) | BIT(2) | BIT(4) | BIT(6)) 6950291652SCodrin Ciubotariu #define MCHP_PDMC_CFGR_BSSEL(ch) BIT((ch) * 2) 7050291652SCodrin Ciubotariu 7150291652SCodrin Ciubotariu #define MCHP_PDMC_CFGR_PDMSEL_MASK (BIT(16) | BIT(18) | BIT(20) | BIT(22)) 7250291652SCodrin Ciubotariu #define MCHP_PDMC_CFGR_PDMSEL(ch) BIT((ch) * 2 + 16) 7350291652SCodrin Ciubotariu 7450291652SCodrin Ciubotariu /* 7550291652SCodrin Ciubotariu * ---- Interrupt Enable/Disable/Mask/Status Registers ---- 7650291652SCodrin Ciubotariu */ 7750291652SCodrin Ciubotariu #define MCHP_PDMC_IR_RXRDY BIT(0) 7850291652SCodrin Ciubotariu #define MCHP_PDMC_IR_RXEMPTY BIT(1) 7950291652SCodrin Ciubotariu #define MCHP_PDMC_IR_RXFULL BIT(2) 8050291652SCodrin Ciubotariu #define MCHP_PDMC_IR_RXCHUNK BIT(3) 8150291652SCodrin Ciubotariu #define MCHP_PDMC_IR_RXUDR BIT(4) 8250291652SCodrin Ciubotariu #define MCHP_PDMC_IR_RXOVR BIT(5) 8350291652SCodrin Ciubotariu 8450291652SCodrin Ciubotariu /* 8550291652SCodrin Ciubotariu * ---- Version Register (Read-only) ---- 8650291652SCodrin Ciubotariu */ 8750291652SCodrin Ciubotariu #define MCHP_PDMC_VER_VERSION GENMASK(11, 0) 8850291652SCodrin Ciubotariu 8950291652SCodrin Ciubotariu #define MCHP_PDMC_MAX_CHANNELS 4 9050291652SCodrin Ciubotariu #define MCHP_PDMC_DS_NO 2 9150291652SCodrin Ciubotariu #define MCHP_PDMC_EDGE_NO 2 9250291652SCodrin Ciubotariu 93*8f0280c8SCodrin Ciubotariu /* 94*8f0280c8SCodrin Ciubotariu * ---- DMA chunk size allowed ---- 95*8f0280c8SCodrin Ciubotariu */ 96*8f0280c8SCodrin Ciubotariu #define MCHP_PDMC_DMA_8_WORD_CHUNK 8 97*8f0280c8SCodrin Ciubotariu #define MCHP_PDMC_DMA_4_WORD_CHUNK 4 98*8f0280c8SCodrin Ciubotariu #define MCHP_PDMC_DMA_2_WORD_CHUNK 2 99*8f0280c8SCodrin Ciubotariu #define MCHP_PDMC_DMA_1_WORD_CHUNK 1 100*8f0280c8SCodrin Ciubotariu #define DMA_BURST_ALIGNED(_p, _s, _w) !(_p % (_s * _w)) 101*8f0280c8SCodrin Ciubotariu 10250291652SCodrin Ciubotariu struct mic_map { 10350291652SCodrin Ciubotariu int ds_pos; 10450291652SCodrin Ciubotariu int clk_edge; 10550291652SCodrin Ciubotariu }; 10650291652SCodrin Ciubotariu 10750291652SCodrin Ciubotariu struct mchp_pdmc_chmap { 10850291652SCodrin Ciubotariu struct snd_pcm_chmap_elem *chmap; 10950291652SCodrin Ciubotariu struct mchp_pdmc *dd; 11050291652SCodrin Ciubotariu struct snd_pcm *pcm; 11150291652SCodrin Ciubotariu struct snd_kcontrol *kctl; 11250291652SCodrin Ciubotariu }; 11350291652SCodrin Ciubotariu 11450291652SCodrin Ciubotariu struct mchp_pdmc { 11550291652SCodrin Ciubotariu struct mic_map channel_mic_map[MCHP_PDMC_MAX_CHANNELS]; 11650291652SCodrin Ciubotariu struct device *dev; 11750291652SCodrin Ciubotariu struct snd_dmaengine_dai_dma_data addr; 11850291652SCodrin Ciubotariu struct regmap *regmap; 11950291652SCodrin Ciubotariu struct clk *pclk; 12050291652SCodrin Ciubotariu struct clk *gclk; 12150291652SCodrin Ciubotariu u32 pdmcen; 122404c61c4SClaudiu Beznea u32 suspend_irq; 123c5682e2bSClaudiu Beznea u32 startup_delay_us; 12450291652SCodrin Ciubotariu int mic_no; 12550291652SCodrin Ciubotariu int sinc_order; 12650291652SCodrin Ciubotariu bool audio_filter_en; 12750291652SCodrin Ciubotariu }; 12850291652SCodrin Ciubotariu 12950291652SCodrin Ciubotariu static const char *const mchp_pdmc_sinc_filter_order_text[] = { 13050291652SCodrin Ciubotariu "1", "2", "3", "4", "5" 13150291652SCodrin Ciubotariu }; 13250291652SCodrin Ciubotariu 13350291652SCodrin Ciubotariu static const unsigned int mchp_pdmc_sinc_filter_order_values[] = { 13450291652SCodrin Ciubotariu 1, 2, 3, 4, 5, 13550291652SCodrin Ciubotariu }; 13650291652SCodrin Ciubotariu 13750291652SCodrin Ciubotariu static const struct soc_enum mchp_pdmc_sinc_filter_order_enum = { 13850291652SCodrin Ciubotariu .items = ARRAY_SIZE(mchp_pdmc_sinc_filter_order_text), 13950291652SCodrin Ciubotariu .texts = mchp_pdmc_sinc_filter_order_text, 14050291652SCodrin Ciubotariu .values = mchp_pdmc_sinc_filter_order_values, 14150291652SCodrin Ciubotariu }; 14250291652SCodrin Ciubotariu 14350291652SCodrin Ciubotariu static int mchp_pdmc_sinc_order_get(struct snd_kcontrol *kcontrol, 14450291652SCodrin Ciubotariu struct snd_ctl_elem_value *uvalue) 14550291652SCodrin Ciubotariu { 14650291652SCodrin Ciubotariu struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); 14750291652SCodrin Ciubotariu struct mchp_pdmc *dd = snd_soc_component_get_drvdata(component); 14850291652SCodrin Ciubotariu struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; 14950291652SCodrin Ciubotariu unsigned int item; 15050291652SCodrin Ciubotariu 15150291652SCodrin Ciubotariu item = snd_soc_enum_val_to_item(e, dd->sinc_order); 15250291652SCodrin Ciubotariu uvalue->value.enumerated.item[0] = item; 15350291652SCodrin Ciubotariu 15450291652SCodrin Ciubotariu return 0; 15550291652SCodrin Ciubotariu } 15650291652SCodrin Ciubotariu 15750291652SCodrin Ciubotariu static int mchp_pdmc_sinc_order_put(struct snd_kcontrol *kcontrol, 15850291652SCodrin Ciubotariu struct snd_ctl_elem_value *uvalue) 15950291652SCodrin Ciubotariu { 16050291652SCodrin Ciubotariu struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); 16150291652SCodrin Ciubotariu struct mchp_pdmc *dd = snd_soc_component_get_drvdata(component); 16250291652SCodrin Ciubotariu struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; 16350291652SCodrin Ciubotariu unsigned int *item = uvalue->value.enumerated.item; 16450291652SCodrin Ciubotariu unsigned int val; 16550291652SCodrin Ciubotariu 16650291652SCodrin Ciubotariu if (item[0] >= e->items) 16750291652SCodrin Ciubotariu return -EINVAL; 16850291652SCodrin Ciubotariu 16950291652SCodrin Ciubotariu val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l; 17050291652SCodrin Ciubotariu if (val == dd->sinc_order) 17150291652SCodrin Ciubotariu return 0; 17250291652SCodrin Ciubotariu 17350291652SCodrin Ciubotariu dd->sinc_order = val; 17450291652SCodrin Ciubotariu 17550291652SCodrin Ciubotariu return 1; 17650291652SCodrin Ciubotariu } 17750291652SCodrin Ciubotariu 17850291652SCodrin Ciubotariu static int mchp_pdmc_af_get(struct snd_kcontrol *kcontrol, 17950291652SCodrin Ciubotariu struct snd_ctl_elem_value *uvalue) 18050291652SCodrin Ciubotariu { 18150291652SCodrin Ciubotariu struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); 18250291652SCodrin Ciubotariu struct mchp_pdmc *dd = snd_soc_component_get_drvdata(component); 18350291652SCodrin Ciubotariu 18450291652SCodrin Ciubotariu uvalue->value.integer.value[0] = !!dd->audio_filter_en; 18550291652SCodrin Ciubotariu 18650291652SCodrin Ciubotariu return 0; 18750291652SCodrin Ciubotariu } 18850291652SCodrin Ciubotariu 18950291652SCodrin Ciubotariu static int mchp_pdmc_af_put(struct snd_kcontrol *kcontrol, 19050291652SCodrin Ciubotariu struct snd_ctl_elem_value *uvalue) 19150291652SCodrin Ciubotariu { 19250291652SCodrin Ciubotariu struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); 19350291652SCodrin Ciubotariu struct mchp_pdmc *dd = snd_soc_component_get_drvdata(component); 194a8ae15eaSAlviro Iskandar Setiawan bool af = uvalue->value.integer.value[0] ? true : false; 19550291652SCodrin Ciubotariu 19650291652SCodrin Ciubotariu if (dd->audio_filter_en == af) 19750291652SCodrin Ciubotariu return 0; 19850291652SCodrin Ciubotariu 19950291652SCodrin Ciubotariu dd->audio_filter_en = af; 20050291652SCodrin Ciubotariu 20150291652SCodrin Ciubotariu return 1; 20250291652SCodrin Ciubotariu } 20350291652SCodrin Ciubotariu 20450291652SCodrin Ciubotariu static int mchp_pdmc_chmap_ctl_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) 20550291652SCodrin Ciubotariu { 20650291652SCodrin Ciubotariu struct mchp_pdmc_chmap *info = snd_kcontrol_chip(kcontrol); 20750291652SCodrin Ciubotariu 20850291652SCodrin Ciubotariu uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 20950291652SCodrin Ciubotariu uinfo->count = info->dd->mic_no; 21050291652SCodrin Ciubotariu uinfo->value.integer.min = 0; 21150291652SCodrin Ciubotariu uinfo->value.integer.max = SNDRV_CHMAP_RR; /* maxmimum 4 channels */ 21250291652SCodrin Ciubotariu return 0; 21350291652SCodrin Ciubotariu } 21450291652SCodrin Ciubotariu 21550291652SCodrin Ciubotariu static inline struct snd_pcm_substream * 21650291652SCodrin Ciubotariu mchp_pdmc_chmap_substream(struct mchp_pdmc_chmap *info, unsigned int idx) 21750291652SCodrin Ciubotariu { 21850291652SCodrin Ciubotariu struct snd_pcm_substream *s; 21950291652SCodrin Ciubotariu 22050291652SCodrin Ciubotariu for (s = info->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; s; s = s->next) 22150291652SCodrin Ciubotariu if (s->number == idx) 22250291652SCodrin Ciubotariu return s; 22350291652SCodrin Ciubotariu return NULL; 22450291652SCodrin Ciubotariu } 22550291652SCodrin Ciubotariu 22650291652SCodrin Ciubotariu static struct snd_pcm_chmap_elem *mchp_pdmc_chmap_get(struct snd_pcm_substream *substream, 22750291652SCodrin Ciubotariu struct mchp_pdmc_chmap *ch_info) 22850291652SCodrin Ciubotariu { 22950291652SCodrin Ciubotariu struct snd_pcm_chmap_elem *map; 23050291652SCodrin Ciubotariu 23150291652SCodrin Ciubotariu for (map = ch_info->chmap; map->channels; map++) { 23250291652SCodrin Ciubotariu if (map->channels == substream->runtime->channels) 23350291652SCodrin Ciubotariu return map; 23450291652SCodrin Ciubotariu } 23550291652SCodrin Ciubotariu return NULL; 23650291652SCodrin Ciubotariu } 23750291652SCodrin Ciubotariu 23850291652SCodrin Ciubotariu static int mchp_pdmc_chmap_ctl_get(struct snd_kcontrol *kcontrol, 23950291652SCodrin Ciubotariu struct snd_ctl_elem_value *ucontrol) 24050291652SCodrin Ciubotariu { 24150291652SCodrin Ciubotariu struct mchp_pdmc_chmap *info = snd_kcontrol_chip(kcontrol); 24250291652SCodrin Ciubotariu struct mchp_pdmc *dd = info->dd; 24350291652SCodrin Ciubotariu unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 24450291652SCodrin Ciubotariu struct snd_pcm_substream *substream; 24550291652SCodrin Ciubotariu const struct snd_pcm_chmap_elem *map; 24650291652SCodrin Ciubotariu int i; 24750291652SCodrin Ciubotariu u32 cfgr_val = 0; 24850291652SCodrin Ciubotariu 24950291652SCodrin Ciubotariu if (!info->chmap) 25050291652SCodrin Ciubotariu return -EINVAL; 25150291652SCodrin Ciubotariu substream = mchp_pdmc_chmap_substream(info, idx); 25250291652SCodrin Ciubotariu if (!substream) 25350291652SCodrin Ciubotariu return -ENODEV; 25450291652SCodrin Ciubotariu memset(ucontrol->value.integer.value, 0, sizeof(long) * info->dd->mic_no); 25550291652SCodrin Ciubotariu if (!substream->runtime) 25650291652SCodrin Ciubotariu return 0; /* no channels set */ 25750291652SCodrin Ciubotariu 25850291652SCodrin Ciubotariu map = mchp_pdmc_chmap_get(substream, info); 25950291652SCodrin Ciubotariu if (!map) 26050291652SCodrin Ciubotariu return -EINVAL; 26150291652SCodrin Ciubotariu 26250291652SCodrin Ciubotariu for (i = 0; i < map->channels; i++) { 26350291652SCodrin Ciubotariu int map_idx = map->channels == 1 ? map->map[i] - SNDRV_CHMAP_MONO : 26450291652SCodrin Ciubotariu map->map[i] - SNDRV_CHMAP_FL; 26550291652SCodrin Ciubotariu 26650291652SCodrin Ciubotariu /* make sure the reported channel map is the real one, so write the map */ 26750291652SCodrin Ciubotariu if (dd->channel_mic_map[map_idx].ds_pos) 26850291652SCodrin Ciubotariu cfgr_val |= MCHP_PDMC_CFGR_PDMSEL(i); 26950291652SCodrin Ciubotariu if (dd->channel_mic_map[map_idx].clk_edge) 27050291652SCodrin Ciubotariu cfgr_val |= MCHP_PDMC_CFGR_BSSEL(i); 27150291652SCodrin Ciubotariu 27250291652SCodrin Ciubotariu ucontrol->value.integer.value[i] = map->map[i]; 27350291652SCodrin Ciubotariu } 27450291652SCodrin Ciubotariu 27550291652SCodrin Ciubotariu regmap_write(dd->regmap, MCHP_PDMC_CFGR, cfgr_val); 27650291652SCodrin Ciubotariu 27750291652SCodrin Ciubotariu return 0; 27850291652SCodrin Ciubotariu } 27950291652SCodrin Ciubotariu 28050291652SCodrin Ciubotariu static int mchp_pdmc_chmap_ctl_put(struct snd_kcontrol *kcontrol, 28150291652SCodrin Ciubotariu struct snd_ctl_elem_value *ucontrol) 28250291652SCodrin Ciubotariu { 28350291652SCodrin Ciubotariu struct mchp_pdmc_chmap *info = snd_kcontrol_chip(kcontrol); 28450291652SCodrin Ciubotariu struct mchp_pdmc *dd = info->dd; 28550291652SCodrin Ciubotariu unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 28650291652SCodrin Ciubotariu struct snd_pcm_substream *substream; 28750291652SCodrin Ciubotariu struct snd_pcm_chmap_elem *map; 28850291652SCodrin Ciubotariu u32 cfgr_val = 0; 28950291652SCodrin Ciubotariu int i; 29050291652SCodrin Ciubotariu 29150291652SCodrin Ciubotariu if (!info->chmap) 29250291652SCodrin Ciubotariu return -EINVAL; 29350291652SCodrin Ciubotariu substream = mchp_pdmc_chmap_substream(info, idx); 29450291652SCodrin Ciubotariu if (!substream) 29550291652SCodrin Ciubotariu return -ENODEV; 29650291652SCodrin Ciubotariu 29750291652SCodrin Ciubotariu map = mchp_pdmc_chmap_get(substream, info); 29850291652SCodrin Ciubotariu if (!map) 29950291652SCodrin Ciubotariu return -EINVAL; 30050291652SCodrin Ciubotariu 30150291652SCodrin Ciubotariu for (i = 0; i < map->channels; i++) { 30250291652SCodrin Ciubotariu int map_idx; 30350291652SCodrin Ciubotariu 30450291652SCodrin Ciubotariu map->map[i] = ucontrol->value.integer.value[i]; 30550291652SCodrin Ciubotariu map_idx = map->channels == 1 ? map->map[i] - SNDRV_CHMAP_MONO : 30650291652SCodrin Ciubotariu map->map[i] - SNDRV_CHMAP_FL; 30750291652SCodrin Ciubotariu 30850291652SCodrin Ciubotariu /* configure IP for the desired channel map */ 30950291652SCodrin Ciubotariu if (dd->channel_mic_map[map_idx].ds_pos) 31050291652SCodrin Ciubotariu cfgr_val |= MCHP_PDMC_CFGR_PDMSEL(i); 31150291652SCodrin Ciubotariu if (dd->channel_mic_map[map_idx].clk_edge) 31250291652SCodrin Ciubotariu cfgr_val |= MCHP_PDMC_CFGR_BSSEL(i); 31350291652SCodrin Ciubotariu } 31450291652SCodrin Ciubotariu 31550291652SCodrin Ciubotariu regmap_write(dd->regmap, MCHP_PDMC_CFGR, cfgr_val); 31650291652SCodrin Ciubotariu 31750291652SCodrin Ciubotariu return 0; 31850291652SCodrin Ciubotariu } 31950291652SCodrin Ciubotariu 32050291652SCodrin Ciubotariu static void mchp_pdmc_chmap_ctl_private_free(struct snd_kcontrol *kcontrol) 32150291652SCodrin Ciubotariu { 32250291652SCodrin Ciubotariu struct mchp_pdmc_chmap *info = snd_kcontrol_chip(kcontrol); 32350291652SCodrin Ciubotariu 32450291652SCodrin Ciubotariu info->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].chmap_kctl = NULL; 32550291652SCodrin Ciubotariu kfree(info); 32650291652SCodrin Ciubotariu } 32750291652SCodrin Ciubotariu 32850291652SCodrin Ciubotariu static int mchp_pdmc_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag, 32950291652SCodrin Ciubotariu unsigned int size, unsigned int __user *tlv) 33050291652SCodrin Ciubotariu { 33150291652SCodrin Ciubotariu struct mchp_pdmc_chmap *info = snd_kcontrol_chip(kcontrol); 33250291652SCodrin Ciubotariu const struct snd_pcm_chmap_elem *map; 33350291652SCodrin Ciubotariu unsigned int __user *dst; 33450291652SCodrin Ciubotariu int c, count = 0; 33550291652SCodrin Ciubotariu 33650291652SCodrin Ciubotariu if (!info->chmap) 33750291652SCodrin Ciubotariu return -EINVAL; 33850291652SCodrin Ciubotariu if (size < 8) 33950291652SCodrin Ciubotariu return -ENOMEM; 34050291652SCodrin Ciubotariu if (put_user(SNDRV_CTL_TLVT_CONTAINER, tlv)) 34150291652SCodrin Ciubotariu return -EFAULT; 34250291652SCodrin Ciubotariu size -= 8; 34350291652SCodrin Ciubotariu dst = tlv + 2; 34450291652SCodrin Ciubotariu for (map = info->chmap; map->channels; map++) { 34550291652SCodrin Ciubotariu int chs_bytes = map->channels * 4; 34650291652SCodrin Ciubotariu 34750291652SCodrin Ciubotariu if (size < 8) 34850291652SCodrin Ciubotariu return -ENOMEM; 34950291652SCodrin Ciubotariu if (put_user(SNDRV_CTL_TLVT_CHMAP_VAR, dst) || 35050291652SCodrin Ciubotariu put_user(chs_bytes, dst + 1)) 35150291652SCodrin Ciubotariu return -EFAULT; 35250291652SCodrin Ciubotariu dst += 2; 35350291652SCodrin Ciubotariu size -= 8; 35450291652SCodrin Ciubotariu count += 8; 35550291652SCodrin Ciubotariu if (size < chs_bytes) 35650291652SCodrin Ciubotariu return -ENOMEM; 35750291652SCodrin Ciubotariu size -= chs_bytes; 35850291652SCodrin Ciubotariu count += chs_bytes; 35950291652SCodrin Ciubotariu for (c = 0; c < map->channels; c++) { 36050291652SCodrin Ciubotariu if (put_user(map->map[c], dst)) 36150291652SCodrin Ciubotariu return -EFAULT; 36250291652SCodrin Ciubotariu dst++; 36350291652SCodrin Ciubotariu } 36450291652SCodrin Ciubotariu } 36550291652SCodrin Ciubotariu if (put_user(count, tlv + 1)) 36650291652SCodrin Ciubotariu return -EFAULT; 36750291652SCodrin Ciubotariu return 0; 36850291652SCodrin Ciubotariu } 36950291652SCodrin Ciubotariu 37050291652SCodrin Ciubotariu static const struct snd_kcontrol_new mchp_pdmc_snd_controls[] = { 37150291652SCodrin Ciubotariu SOC_SINGLE_BOOL_EXT("Audio Filter", 0, &mchp_pdmc_af_get, &mchp_pdmc_af_put), 37250291652SCodrin Ciubotariu { 37350291652SCodrin Ciubotariu .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 37450291652SCodrin Ciubotariu .name = "SINC Filter Order", 37550291652SCodrin Ciubotariu .info = snd_soc_info_enum_double, 37650291652SCodrin Ciubotariu .get = mchp_pdmc_sinc_order_get, 37750291652SCodrin Ciubotariu .put = mchp_pdmc_sinc_order_put, 37850291652SCodrin Ciubotariu .private_value = (unsigned long)&mchp_pdmc_sinc_filter_order_enum, 37950291652SCodrin Ciubotariu }, 38050291652SCodrin Ciubotariu }; 38150291652SCodrin Ciubotariu 38250291652SCodrin Ciubotariu static int mchp_pdmc_close(struct snd_soc_component *component, 38350291652SCodrin Ciubotariu struct snd_pcm_substream *substream) 38450291652SCodrin Ciubotariu { 38550291652SCodrin Ciubotariu return snd_soc_add_component_controls(component, mchp_pdmc_snd_controls, 38650291652SCodrin Ciubotariu ARRAY_SIZE(mchp_pdmc_snd_controls)); 38750291652SCodrin Ciubotariu } 38850291652SCodrin Ciubotariu 38950291652SCodrin Ciubotariu static int mchp_pdmc_open(struct snd_soc_component *component, 39050291652SCodrin Ciubotariu struct snd_pcm_substream *substream) 39150291652SCodrin Ciubotariu { 39250291652SCodrin Ciubotariu int i; 39350291652SCodrin Ciubotariu 39450291652SCodrin Ciubotariu /* remove controls that can't be changed at runtime */ 39550291652SCodrin Ciubotariu for (i = 0; i < ARRAY_SIZE(mchp_pdmc_snd_controls); i++) { 39650291652SCodrin Ciubotariu const struct snd_kcontrol_new *control = &mchp_pdmc_snd_controls[i]; 39750291652SCodrin Ciubotariu struct snd_ctl_elem_id id; 39850291652SCodrin Ciubotariu int err; 39950291652SCodrin Ciubotariu 40050291652SCodrin Ciubotariu if (component->name_prefix) 40150291652SCodrin Ciubotariu snprintf(id.name, sizeof(id.name), "%s %s", component->name_prefix, 40250291652SCodrin Ciubotariu control->name); 40350291652SCodrin Ciubotariu else 40450291652SCodrin Ciubotariu strscpy(id.name, control->name, sizeof(id.name)); 40550291652SCodrin Ciubotariu 40650291652SCodrin Ciubotariu id.numid = 0; 40750291652SCodrin Ciubotariu id.iface = control->iface; 40850291652SCodrin Ciubotariu id.device = control->device; 40950291652SCodrin Ciubotariu id.subdevice = control->subdevice; 41050291652SCodrin Ciubotariu id.index = control->index; 411d8b366c4STakashi Iwai err = snd_ctl_remove_id(component->card->snd_card, &id); 412d8b366c4STakashi Iwai if (err < 0) 41350291652SCodrin Ciubotariu dev_err(component->dev, "%d: Failed to remove %s\n", err, 41450291652SCodrin Ciubotariu control->name); 41550291652SCodrin Ciubotariu } 41650291652SCodrin Ciubotariu 41750291652SCodrin Ciubotariu return 0; 41850291652SCodrin Ciubotariu } 41950291652SCodrin Ciubotariu 42050291652SCodrin Ciubotariu static const struct snd_soc_component_driver mchp_pdmc_dai_component = { 42150291652SCodrin Ciubotariu .name = "mchp-pdmc", 42250291652SCodrin Ciubotariu .controls = mchp_pdmc_snd_controls, 42350291652SCodrin Ciubotariu .num_controls = ARRAY_SIZE(mchp_pdmc_snd_controls), 42450291652SCodrin Ciubotariu .open = &mchp_pdmc_open, 42550291652SCodrin Ciubotariu .close = &mchp_pdmc_close, 4267593e008SCharles Keepax .legacy_dai_naming = 1, 42738cb2a36SKuninori Morimoto .trigger_start = SND_SOC_TRIGGER_ORDER_LDC, 42850291652SCodrin Ciubotariu }; 42950291652SCodrin Ciubotariu 43050291652SCodrin Ciubotariu static const unsigned int mchp_pdmc_1mic[] = {1}; 43150291652SCodrin Ciubotariu static const unsigned int mchp_pdmc_2mic[] = {1, 2}; 43250291652SCodrin Ciubotariu static const unsigned int mchp_pdmc_3mic[] = {1, 2, 3}; 43350291652SCodrin Ciubotariu static const unsigned int mchp_pdmc_4mic[] = {1, 2, 3, 4}; 43450291652SCodrin Ciubotariu 43550291652SCodrin Ciubotariu static const struct snd_pcm_hw_constraint_list mchp_pdmc_chan_constr[] = { 43650291652SCodrin Ciubotariu { 43750291652SCodrin Ciubotariu .list = mchp_pdmc_1mic, 43850291652SCodrin Ciubotariu .count = ARRAY_SIZE(mchp_pdmc_1mic), 43950291652SCodrin Ciubotariu }, 44050291652SCodrin Ciubotariu { 44150291652SCodrin Ciubotariu .list = mchp_pdmc_2mic, 44250291652SCodrin Ciubotariu .count = ARRAY_SIZE(mchp_pdmc_2mic), 44350291652SCodrin Ciubotariu }, 44450291652SCodrin Ciubotariu { 44550291652SCodrin Ciubotariu .list = mchp_pdmc_3mic, 44650291652SCodrin Ciubotariu .count = ARRAY_SIZE(mchp_pdmc_3mic), 44750291652SCodrin Ciubotariu }, 44850291652SCodrin Ciubotariu { 44950291652SCodrin Ciubotariu .list = mchp_pdmc_4mic, 45050291652SCodrin Ciubotariu .count = ARRAY_SIZE(mchp_pdmc_4mic), 45150291652SCodrin Ciubotariu }, 45250291652SCodrin Ciubotariu }; 45350291652SCodrin Ciubotariu 45450291652SCodrin Ciubotariu static int mchp_pdmc_startup(struct snd_pcm_substream *substream, 45550291652SCodrin Ciubotariu struct snd_soc_dai *dai) 45650291652SCodrin Ciubotariu { 45750291652SCodrin Ciubotariu struct mchp_pdmc *dd = snd_soc_dai_get_drvdata(dai); 45850291652SCodrin Ciubotariu 45950291652SCodrin Ciubotariu regmap_write(dd->regmap, MCHP_PDMC_CR, MCHP_PDMC_CR_SWRST); 46050291652SCodrin Ciubotariu 46150291652SCodrin Ciubotariu snd_pcm_hw_constraint_list(substream->runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, 46250291652SCodrin Ciubotariu &mchp_pdmc_chan_constr[dd->mic_no - 1]); 46350291652SCodrin Ciubotariu 46450291652SCodrin Ciubotariu return 0; 46550291652SCodrin Ciubotariu } 46650291652SCodrin Ciubotariu 46750291652SCodrin Ciubotariu static int mchp_pdmc_dai_probe(struct snd_soc_dai *dai) 46850291652SCodrin Ciubotariu { 46950291652SCodrin Ciubotariu struct mchp_pdmc *dd = snd_soc_dai_get_drvdata(dai); 47050291652SCodrin Ciubotariu 47150291652SCodrin Ciubotariu snd_soc_dai_init_dma_data(dai, NULL, &dd->addr); 47250291652SCodrin Ciubotariu 47350291652SCodrin Ciubotariu return 0; 47450291652SCodrin Ciubotariu } 47550291652SCodrin Ciubotariu 47650291652SCodrin Ciubotariu static int mchp_pdmc_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) 47750291652SCodrin Ciubotariu { 47850291652SCodrin Ciubotariu unsigned int fmt_master = fmt & SND_SOC_DAIFMT_MASTER_MASK; 47950291652SCodrin Ciubotariu unsigned int fmt_format = fmt & SND_SOC_DAIFMT_FORMAT_MASK; 48050291652SCodrin Ciubotariu 48150291652SCodrin Ciubotariu /* IP needs to be bitclock master */ 4820fd054a5SCharles Keepax if (fmt_master != SND_SOC_DAIFMT_BP_FP && 4830fd054a5SCharles Keepax fmt_master != SND_SOC_DAIFMT_BP_FC) 48450291652SCodrin Ciubotariu return -EINVAL; 48550291652SCodrin Ciubotariu 48650291652SCodrin Ciubotariu /* IP supports only PDM interface */ 48750291652SCodrin Ciubotariu if (fmt_format != SND_SOC_DAIFMT_PDM) 48850291652SCodrin Ciubotariu return -EINVAL; 48950291652SCodrin Ciubotariu 49050291652SCodrin Ciubotariu return 0; 49150291652SCodrin Ciubotariu } 49250291652SCodrin Ciubotariu 49350291652SCodrin Ciubotariu static u32 mchp_pdmc_mr_set_osr(int audio_filter_en, unsigned int osr) 49450291652SCodrin Ciubotariu { 49550291652SCodrin Ciubotariu if (audio_filter_en) { 49650291652SCodrin Ciubotariu switch (osr) { 49750291652SCodrin Ciubotariu case 64: 49850291652SCodrin Ciubotariu return MCHP_PDMC_MR_OSR64; 49950291652SCodrin Ciubotariu case 128: 50050291652SCodrin Ciubotariu return MCHP_PDMC_MR_OSR128; 50150291652SCodrin Ciubotariu case 256: 50250291652SCodrin Ciubotariu return MCHP_PDMC_MR_OSR256; 50350291652SCodrin Ciubotariu } 50450291652SCodrin Ciubotariu } else { 50550291652SCodrin Ciubotariu switch (osr) { 50650291652SCodrin Ciubotariu case 8: 50750291652SCodrin Ciubotariu return MCHP_PDMC_MR_SINC_OSR_8; 50850291652SCodrin Ciubotariu case 16: 50950291652SCodrin Ciubotariu return MCHP_PDMC_MR_SINC_OSR_16; 51050291652SCodrin Ciubotariu case 32: 51150291652SCodrin Ciubotariu return MCHP_PDMC_MR_SINC_OSR_32; 51250291652SCodrin Ciubotariu case 64: 51350291652SCodrin Ciubotariu return MCHP_PDMC_MR_SINC_OSR_64; 51450291652SCodrin Ciubotariu case 128: 51550291652SCodrin Ciubotariu return MCHP_PDMC_MR_SINC_OSR_128; 51650291652SCodrin Ciubotariu case 256: 51750291652SCodrin Ciubotariu return MCHP_PDMC_MR_SINC_OSR_256; 51850291652SCodrin Ciubotariu } 51950291652SCodrin Ciubotariu } 52050291652SCodrin Ciubotariu return 0; 52150291652SCodrin Ciubotariu } 52250291652SCodrin Ciubotariu 523*8f0280c8SCodrin Ciubotariu static inline int mchp_pdmc_period_to_maxburst(int period_size, int sample_size) 52450291652SCodrin Ciubotariu { 525*8f0280c8SCodrin Ciubotariu int p_size = period_size; 526*8f0280c8SCodrin Ciubotariu int s_size = sample_size; 527*8f0280c8SCodrin Ciubotariu 528*8f0280c8SCodrin Ciubotariu if (DMA_BURST_ALIGNED(p_size, s_size, MCHP_PDMC_DMA_8_WORD_CHUNK)) 529*8f0280c8SCodrin Ciubotariu return MCHP_PDMC_DMA_8_WORD_CHUNK; 530*8f0280c8SCodrin Ciubotariu if (DMA_BURST_ALIGNED(p_size, s_size, MCHP_PDMC_DMA_4_WORD_CHUNK)) 531*8f0280c8SCodrin Ciubotariu return MCHP_PDMC_DMA_4_WORD_CHUNK; 532*8f0280c8SCodrin Ciubotariu if (DMA_BURST_ALIGNED(p_size, s_size, MCHP_PDMC_DMA_2_WORD_CHUNK)) 533*8f0280c8SCodrin Ciubotariu return MCHP_PDMC_DMA_2_WORD_CHUNK; 534*8f0280c8SCodrin Ciubotariu return MCHP_PDMC_DMA_1_WORD_CHUNK; 53550291652SCodrin Ciubotariu } 53650291652SCodrin Ciubotariu 53750291652SCodrin Ciubotariu static struct snd_pcm_chmap_elem mchp_pdmc_std_chmaps[] = { 53850291652SCodrin Ciubotariu { .channels = 1, 53950291652SCodrin Ciubotariu .map = { SNDRV_CHMAP_MONO } }, 54050291652SCodrin Ciubotariu { .channels = 2, 54150291652SCodrin Ciubotariu .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } }, 54250291652SCodrin Ciubotariu { .channels = 3, 54350291652SCodrin Ciubotariu .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, 54450291652SCodrin Ciubotariu SNDRV_CHMAP_RL } }, 54550291652SCodrin Ciubotariu { .channels = 4, 54650291652SCodrin Ciubotariu .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, 54750291652SCodrin Ciubotariu SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } }, 54850291652SCodrin Ciubotariu { } 54950291652SCodrin Ciubotariu }; 55050291652SCodrin Ciubotariu 55150291652SCodrin Ciubotariu static int mchp_pdmc_hw_params(struct snd_pcm_substream *substream, 55250291652SCodrin Ciubotariu struct snd_pcm_hw_params *params, 55350291652SCodrin Ciubotariu struct snd_soc_dai *dai) 55450291652SCodrin Ciubotariu { 55550291652SCodrin Ciubotariu struct mchp_pdmc *dd = snd_soc_dai_get_drvdata(dai); 55650291652SCodrin Ciubotariu struct snd_soc_component *comp = dai->component; 55750291652SCodrin Ciubotariu unsigned long gclk_rate = 0; 55850291652SCodrin Ciubotariu unsigned long best_diff_rate = ~0UL; 55950291652SCodrin Ciubotariu unsigned int channels = params_channels(params); 56050291652SCodrin Ciubotariu unsigned int osr = 0, osr_start; 56150291652SCodrin Ciubotariu unsigned int fs = params_rate(params); 562*8f0280c8SCodrin Ciubotariu int sample_bytes = params_physical_width(params) / 8; 563*8f0280c8SCodrin Ciubotariu int period_bytes = params_period_size(params) * 564*8f0280c8SCodrin Ciubotariu params_channels(params) * sample_bytes; 565*8f0280c8SCodrin Ciubotariu int maxburst; 56650291652SCodrin Ciubotariu u32 mr_val = 0; 56750291652SCodrin Ciubotariu u32 cfgr_val = 0; 56850291652SCodrin Ciubotariu int i; 56950291652SCodrin Ciubotariu int ret; 57050291652SCodrin Ciubotariu 571*8f0280c8SCodrin Ciubotariu dev_dbg(comp->dev, "%s() rate=%u format=%#x width=%u channels=%u period_bytes=%d\n", 57250291652SCodrin Ciubotariu __func__, params_rate(params), params_format(params), 573*8f0280c8SCodrin Ciubotariu params_width(params), params_channels(params), period_bytes); 57450291652SCodrin Ciubotariu 57550291652SCodrin Ciubotariu if (channels > dd->mic_no) { 57650291652SCodrin Ciubotariu dev_err(comp->dev, "more channels %u than microphones %d\n", 57750291652SCodrin Ciubotariu channels, dd->mic_no); 57850291652SCodrin Ciubotariu return -EINVAL; 57950291652SCodrin Ciubotariu } 58050291652SCodrin Ciubotariu 58150291652SCodrin Ciubotariu dd->pdmcen = 0; 58250291652SCodrin Ciubotariu for (i = 0; i < channels; i++) { 58350291652SCodrin Ciubotariu dd->pdmcen |= MCHP_PDMC_MR_PDMCEN(i); 58450291652SCodrin Ciubotariu if (dd->channel_mic_map[i].ds_pos) 58550291652SCodrin Ciubotariu cfgr_val |= MCHP_PDMC_CFGR_PDMSEL(i); 58650291652SCodrin Ciubotariu if (dd->channel_mic_map[i].clk_edge) 58750291652SCodrin Ciubotariu cfgr_val |= MCHP_PDMC_CFGR_BSSEL(i); 58850291652SCodrin Ciubotariu } 58950291652SCodrin Ciubotariu 59050291652SCodrin Ciubotariu for (osr_start = dd->audio_filter_en ? 64 : 8; 59150291652SCodrin Ciubotariu osr_start <= 256 && best_diff_rate; osr_start *= 2) { 59250291652SCodrin Ciubotariu long round_rate; 59350291652SCodrin Ciubotariu unsigned long diff_rate; 59450291652SCodrin Ciubotariu 59550291652SCodrin Ciubotariu round_rate = clk_round_rate(dd->gclk, 59650291652SCodrin Ciubotariu (unsigned long)fs * 16 * osr_start); 59750291652SCodrin Ciubotariu if (round_rate < 0) 59850291652SCodrin Ciubotariu continue; 59950291652SCodrin Ciubotariu diff_rate = abs((fs * 16 * osr_start) - round_rate); 60050291652SCodrin Ciubotariu if (diff_rate < best_diff_rate) { 60150291652SCodrin Ciubotariu best_diff_rate = diff_rate; 60250291652SCodrin Ciubotariu osr = osr_start; 60350291652SCodrin Ciubotariu gclk_rate = fs * 16 * osr; 60450291652SCodrin Ciubotariu } 60550291652SCodrin Ciubotariu } 60650291652SCodrin Ciubotariu if (!gclk_rate) { 60750291652SCodrin Ciubotariu dev_err(comp->dev, "invalid sampling rate: %u\n", fs); 60850291652SCodrin Ciubotariu return -EINVAL; 60950291652SCodrin Ciubotariu } 61050291652SCodrin Ciubotariu 611e8c8e9deSClaudiu Beznea /* CLK is enabled by runtime PM. */ 612e8c8e9deSClaudiu Beznea clk_disable_unprepare(dd->gclk); 613e8c8e9deSClaudiu Beznea 61450291652SCodrin Ciubotariu /* set the rate */ 61550291652SCodrin Ciubotariu ret = clk_set_rate(dd->gclk, gclk_rate); 616e8c8e9deSClaudiu Beznea clk_prepare_enable(dd->gclk); 61750291652SCodrin Ciubotariu if (ret) { 61850291652SCodrin Ciubotariu dev_err(comp->dev, "unable to set rate %lu to GCLK: %d\n", 61950291652SCodrin Ciubotariu gclk_rate, ret); 62050291652SCodrin Ciubotariu return ret; 62150291652SCodrin Ciubotariu } 62250291652SCodrin Ciubotariu 62350291652SCodrin Ciubotariu mr_val |= mchp_pdmc_mr_set_osr(dd->audio_filter_en, osr); 62450291652SCodrin Ciubotariu 62512974257SClaudiu Beznea mr_val |= FIELD_PREP(MCHP_PDMC_MR_SINCORDER_MASK, dd->sinc_order); 62650291652SCodrin Ciubotariu 627*8f0280c8SCodrin Ciubotariu maxburst = mchp_pdmc_period_to_maxburst(period_bytes, sample_bytes); 628*8f0280c8SCodrin Ciubotariu dd->addr.maxburst = maxburst; 62912974257SClaudiu Beznea mr_val |= FIELD_PREP(MCHP_PDMC_MR_CHUNK_MASK, dd->addr.maxburst); 63050291652SCodrin Ciubotariu dev_dbg(comp->dev, "maxburst set to %d\n", dd->addr.maxburst); 63150291652SCodrin Ciubotariu 63250291652SCodrin Ciubotariu snd_soc_component_update_bits(comp, MCHP_PDMC_MR, 63350291652SCodrin Ciubotariu MCHP_PDMC_MR_OSR_MASK | 63450291652SCodrin Ciubotariu MCHP_PDMC_MR_SINCORDER_MASK | 63550291652SCodrin Ciubotariu MCHP_PDMC_MR_SINC_OSR_MASK | 63650291652SCodrin Ciubotariu MCHP_PDMC_MR_CHUNK_MASK, mr_val); 63750291652SCodrin Ciubotariu 63850291652SCodrin Ciubotariu snd_soc_component_write(comp, MCHP_PDMC_CFGR, cfgr_val); 63950291652SCodrin Ciubotariu 64050291652SCodrin Ciubotariu return 0; 64150291652SCodrin Ciubotariu } 64250291652SCodrin Ciubotariu 643c5682e2bSClaudiu Beznea static void mchp_pdmc_noise_filter_workaround(struct mchp_pdmc *dd) 644c5682e2bSClaudiu Beznea { 645c5682e2bSClaudiu Beznea u32 tmp, steps = 16; 646c5682e2bSClaudiu Beznea 647c5682e2bSClaudiu Beznea /* 648c5682e2bSClaudiu Beznea * PDMC doesn't wait for microphones' startup time thus the acquisition 649c5682e2bSClaudiu Beznea * may start before the microphones are ready leading to poc noises at 650c5682e2bSClaudiu Beznea * the beginning of capture. To avoid this, we need to wait 50ms (in 651c5682e2bSClaudiu Beznea * normal startup procedure) or 150 ms (worst case after resume from sleep 652c5682e2bSClaudiu Beznea * states) after microphones are enabled and then clear the FIFOs (by 653c5682e2bSClaudiu Beznea * reading the RHR 16 times) and possible interrupts before continuing. 654c5682e2bSClaudiu Beznea * Also, for this to work the DMA needs to be started after interrupts 655c5682e2bSClaudiu Beznea * are enabled. 656c5682e2bSClaudiu Beznea */ 657c5682e2bSClaudiu Beznea usleep_range(dd->startup_delay_us, dd->startup_delay_us + 5); 658c5682e2bSClaudiu Beznea 659c5682e2bSClaudiu Beznea while (steps--) 660c5682e2bSClaudiu Beznea regmap_read(dd->regmap, MCHP_PDMC_RHR, &tmp); 661c5682e2bSClaudiu Beznea 662c5682e2bSClaudiu Beznea /* Clear interrupts. */ 663c5682e2bSClaudiu Beznea regmap_read(dd->regmap, MCHP_PDMC_ISR, &tmp); 664c5682e2bSClaudiu Beznea } 665c5682e2bSClaudiu Beznea 66650291652SCodrin Ciubotariu static int mchp_pdmc_trigger(struct snd_pcm_substream *substream, 66750291652SCodrin Ciubotariu int cmd, struct snd_soc_dai *dai) 66850291652SCodrin Ciubotariu { 66950291652SCodrin Ciubotariu struct mchp_pdmc *dd = snd_soc_dai_get_drvdata(dai); 67050291652SCodrin Ciubotariu struct snd_soc_component *cpu = dai->component; 67150291652SCodrin Ciubotariu #ifdef DEBUG 67250291652SCodrin Ciubotariu u32 val; 67350291652SCodrin Ciubotariu #endif 67450291652SCodrin Ciubotariu 67550291652SCodrin Ciubotariu switch (cmd) { 67650291652SCodrin Ciubotariu case SNDRV_PCM_TRIGGER_RESUME: 677404c61c4SClaudiu Beznea case SNDRV_PCM_TRIGGER_START: 678404c61c4SClaudiu Beznea case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 67950291652SCodrin Ciubotariu snd_soc_component_update_bits(cpu, MCHP_PDMC_MR, 68050291652SCodrin Ciubotariu MCHP_PDMC_MR_PDMCEN_MASK, 68150291652SCodrin Ciubotariu dd->pdmcen); 682c5682e2bSClaudiu Beznea 683c5682e2bSClaudiu Beznea mchp_pdmc_noise_filter_workaround(dd); 684c5682e2bSClaudiu Beznea 685c5682e2bSClaudiu Beznea /* Enable interrupts. */ 686c5682e2bSClaudiu Beznea regmap_write(dd->regmap, MCHP_PDMC_IER, dd->suspend_irq | 687c5682e2bSClaudiu Beznea MCHP_PDMC_IR_RXOVR | MCHP_PDMC_IR_RXUDR); 688c5682e2bSClaudiu Beznea dd->suspend_irq = 0; 68950291652SCodrin Ciubotariu break; 69050291652SCodrin Ciubotariu case SNDRV_PCM_TRIGGER_SUSPEND: 691404c61c4SClaudiu Beznea regmap_read(dd->regmap, MCHP_PDMC_IMR, &dd->suspend_irq); 692404c61c4SClaudiu Beznea fallthrough; 693404c61c4SClaudiu Beznea case SNDRV_PCM_TRIGGER_STOP: 69450291652SCodrin Ciubotariu /* Disable overrun and underrun error interrupts */ 695404c61c4SClaudiu Beznea regmap_write(dd->regmap, MCHP_PDMC_IDR, dd->suspend_irq | 69650291652SCodrin Ciubotariu MCHP_PDMC_IR_RXOVR | MCHP_PDMC_IR_RXUDR); 697404c61c4SClaudiu Beznea fallthrough; 698404c61c4SClaudiu Beznea case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 69950291652SCodrin Ciubotariu snd_soc_component_update_bits(cpu, MCHP_PDMC_MR, 70050291652SCodrin Ciubotariu MCHP_PDMC_MR_PDMCEN_MASK, 0); 70150291652SCodrin Ciubotariu break; 70250291652SCodrin Ciubotariu default: 70350291652SCodrin Ciubotariu return -EINVAL; 70450291652SCodrin Ciubotariu } 70550291652SCodrin Ciubotariu 70650291652SCodrin Ciubotariu #ifdef DEBUG 70750291652SCodrin Ciubotariu regmap_read(dd->regmap, MCHP_PDMC_MR, &val); 70850291652SCodrin Ciubotariu dev_dbg(dd->dev, "MR (0x%02x): 0x%08x\n", MCHP_PDMC_MR, val); 70950291652SCodrin Ciubotariu regmap_read(dd->regmap, MCHP_PDMC_CFGR, &val); 71050291652SCodrin Ciubotariu dev_dbg(dd->dev, "CFGR (0x%02x): 0x%08x\n", MCHP_PDMC_CFGR, val); 71150291652SCodrin Ciubotariu regmap_read(dd->regmap, MCHP_PDMC_IMR, &val); 71250291652SCodrin Ciubotariu dev_dbg(dd->dev, "IMR (0x%02x): 0x%08x\n", MCHP_PDMC_IMR, val); 71350291652SCodrin Ciubotariu #endif 71450291652SCodrin Ciubotariu 71550291652SCodrin Ciubotariu return 0; 71650291652SCodrin Ciubotariu } 71750291652SCodrin Ciubotariu 71850291652SCodrin Ciubotariu static int mchp_pdmc_add_chmap_ctls(struct snd_pcm *pcm, struct mchp_pdmc *dd) 71950291652SCodrin Ciubotariu { 72050291652SCodrin Ciubotariu struct mchp_pdmc_chmap *info; 72150291652SCodrin Ciubotariu struct snd_kcontrol_new knew = { 72250291652SCodrin Ciubotariu .iface = SNDRV_CTL_ELEM_IFACE_PCM, 72350291652SCodrin Ciubotariu .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | 72450291652SCodrin Ciubotariu SNDRV_CTL_ELEM_ACCESS_TLV_READ | 72550291652SCodrin Ciubotariu SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, 72650291652SCodrin Ciubotariu .info = mchp_pdmc_chmap_ctl_info, 72750291652SCodrin Ciubotariu .get = mchp_pdmc_chmap_ctl_get, 72850291652SCodrin Ciubotariu .put = mchp_pdmc_chmap_ctl_put, 72950291652SCodrin Ciubotariu .tlv.c = mchp_pdmc_chmap_ctl_tlv, 73050291652SCodrin Ciubotariu }; 73150291652SCodrin Ciubotariu int err; 73250291652SCodrin Ciubotariu 73350291652SCodrin Ciubotariu if (WARN_ON(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].chmap_kctl)) 73450291652SCodrin Ciubotariu return -EBUSY; 73550291652SCodrin Ciubotariu info = kzalloc(sizeof(*info), GFP_KERNEL); 73650291652SCodrin Ciubotariu if (!info) 73750291652SCodrin Ciubotariu return -ENOMEM; 73850291652SCodrin Ciubotariu info->pcm = pcm; 73950291652SCodrin Ciubotariu info->dd = dd; 74050291652SCodrin Ciubotariu info->chmap = mchp_pdmc_std_chmaps; 74150291652SCodrin Ciubotariu knew.name = "Capture Channel Map"; 74250291652SCodrin Ciubotariu knew.device = pcm->device; 74350291652SCodrin Ciubotariu knew.count = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream_count; 74450291652SCodrin Ciubotariu info->kctl = snd_ctl_new1(&knew, info); 74550291652SCodrin Ciubotariu if (!info->kctl) { 74650291652SCodrin Ciubotariu kfree(info); 74750291652SCodrin Ciubotariu return -ENOMEM; 74850291652SCodrin Ciubotariu } 74950291652SCodrin Ciubotariu info->kctl->private_free = mchp_pdmc_chmap_ctl_private_free; 75050291652SCodrin Ciubotariu err = snd_ctl_add(pcm->card, info->kctl); 75150291652SCodrin Ciubotariu if (err < 0) 75250291652SCodrin Ciubotariu return err; 75350291652SCodrin Ciubotariu pcm->streams[SNDRV_PCM_STREAM_CAPTURE].chmap_kctl = info->kctl; 75450291652SCodrin Ciubotariu return 0; 75550291652SCodrin Ciubotariu } 75650291652SCodrin Ciubotariu 75750291652SCodrin Ciubotariu static int mchp_pdmc_pcm_new(struct snd_soc_pcm_runtime *rtd, 75850291652SCodrin Ciubotariu struct snd_soc_dai *dai) 75950291652SCodrin Ciubotariu { 76050291652SCodrin Ciubotariu struct mchp_pdmc *dd = snd_soc_dai_get_drvdata(dai); 76150291652SCodrin Ciubotariu int ret; 76250291652SCodrin Ciubotariu 76350291652SCodrin Ciubotariu ret = mchp_pdmc_add_chmap_ctls(rtd->pcm, dd); 76451124a30SClaudiu Beznea if (ret < 0) 76550291652SCodrin Ciubotariu dev_err(dd->dev, "failed to add channel map controls: %d\n", ret); 76650291652SCodrin Ciubotariu 76751124a30SClaudiu Beznea return ret; 76850291652SCodrin Ciubotariu } 76950291652SCodrin Ciubotariu 7702ff8a43dSKuninori Morimoto static const struct snd_soc_dai_ops mchp_pdmc_dai_ops = { 77150291652SCodrin Ciubotariu .probe = mchp_pdmc_dai_probe, 7722ff8a43dSKuninori Morimoto .set_fmt = mchp_pdmc_set_fmt, 7732ff8a43dSKuninori Morimoto .startup = mchp_pdmc_startup, 7742ff8a43dSKuninori Morimoto .hw_params = mchp_pdmc_hw_params, 7752ff8a43dSKuninori Morimoto .trigger = mchp_pdmc_trigger, 7762ff8a43dSKuninori Morimoto .pcm_new = &mchp_pdmc_pcm_new, 7772ff8a43dSKuninori Morimoto }; 7782ff8a43dSKuninori Morimoto 7792ff8a43dSKuninori Morimoto static struct snd_soc_dai_driver mchp_pdmc_dai = { 78050291652SCodrin Ciubotariu .capture = { 78150291652SCodrin Ciubotariu .stream_name = "Capture", 78250291652SCodrin Ciubotariu .channels_min = 1, 78350291652SCodrin Ciubotariu .channels_max = 4, 78450291652SCodrin Ciubotariu .rate_min = 8000, 78550291652SCodrin Ciubotariu .rate_max = 192000, 78650291652SCodrin Ciubotariu .rates = SNDRV_PCM_RATE_KNOT, 78750291652SCodrin Ciubotariu .formats = SNDRV_PCM_FMTBIT_S24_LE, 78850291652SCodrin Ciubotariu }, 78950291652SCodrin Ciubotariu .ops = &mchp_pdmc_dai_ops, 79050291652SCodrin Ciubotariu }; 79150291652SCodrin Ciubotariu 79250291652SCodrin Ciubotariu /* PDMC interrupt handler */ 79350291652SCodrin Ciubotariu static irqreturn_t mchp_pdmc_interrupt(int irq, void *dev_id) 79450291652SCodrin Ciubotariu { 795cb72b29cSClaudiu Beznea struct mchp_pdmc *dd = dev_id; 79650291652SCodrin Ciubotariu u32 isr, msr, pending; 79750291652SCodrin Ciubotariu irqreturn_t ret = IRQ_NONE; 79850291652SCodrin Ciubotariu 79950291652SCodrin Ciubotariu regmap_read(dd->regmap, MCHP_PDMC_ISR, &isr); 80050291652SCodrin Ciubotariu regmap_read(dd->regmap, MCHP_PDMC_IMR, &msr); 80150291652SCodrin Ciubotariu 80250291652SCodrin Ciubotariu pending = isr & msr; 80350291652SCodrin Ciubotariu dev_dbg(dd->dev, "ISR (0x%02x): 0x%08x, IMR (0x%02x): 0x%08x, pending: 0x%08x\n", 80450291652SCodrin Ciubotariu MCHP_PDMC_ISR, isr, MCHP_PDMC_IMR, msr, pending); 80550291652SCodrin Ciubotariu if (!pending) 80650291652SCodrin Ciubotariu return IRQ_NONE; 80750291652SCodrin Ciubotariu 80850291652SCodrin Ciubotariu if (pending & MCHP_PDMC_IR_RXUDR) { 80950291652SCodrin Ciubotariu dev_warn(dd->dev, "underrun detected\n"); 81050291652SCodrin Ciubotariu regmap_write(dd->regmap, MCHP_PDMC_IDR, MCHP_PDMC_IR_RXUDR); 81150291652SCodrin Ciubotariu ret = IRQ_HANDLED; 81250291652SCodrin Ciubotariu } 81350291652SCodrin Ciubotariu if (pending & MCHP_PDMC_IR_RXOVR) { 81450291652SCodrin Ciubotariu dev_warn(dd->dev, "overrun detected\n"); 81550291652SCodrin Ciubotariu regmap_write(dd->regmap, MCHP_PDMC_IDR, MCHP_PDMC_IR_RXOVR); 81650291652SCodrin Ciubotariu ret = IRQ_HANDLED; 81750291652SCodrin Ciubotariu } 81850291652SCodrin Ciubotariu 81950291652SCodrin Ciubotariu return ret; 82050291652SCodrin Ciubotariu } 82150291652SCodrin Ciubotariu 82250291652SCodrin Ciubotariu /* regmap configuration */ 82350291652SCodrin Ciubotariu static bool mchp_pdmc_readable_reg(struct device *dev, unsigned int reg) 82450291652SCodrin Ciubotariu { 82550291652SCodrin Ciubotariu switch (reg) { 82650291652SCodrin Ciubotariu case MCHP_PDMC_MR: 82750291652SCodrin Ciubotariu case MCHP_PDMC_CFGR: 82850291652SCodrin Ciubotariu case MCHP_PDMC_IMR: 82950291652SCodrin Ciubotariu case MCHP_PDMC_ISR: 830c5682e2bSClaudiu Beznea case MCHP_PDMC_RHR: 83150291652SCodrin Ciubotariu case MCHP_PDMC_VER: 83250291652SCodrin Ciubotariu return true; 83350291652SCodrin Ciubotariu default: 83450291652SCodrin Ciubotariu return false; 83550291652SCodrin Ciubotariu } 83650291652SCodrin Ciubotariu } 83750291652SCodrin Ciubotariu 83850291652SCodrin Ciubotariu static bool mchp_pdmc_writeable_reg(struct device *dev, unsigned int reg) 83950291652SCodrin Ciubotariu { 84050291652SCodrin Ciubotariu switch (reg) { 84150291652SCodrin Ciubotariu case MCHP_PDMC_CR: 84250291652SCodrin Ciubotariu case MCHP_PDMC_MR: 84350291652SCodrin Ciubotariu case MCHP_PDMC_CFGR: 84450291652SCodrin Ciubotariu case MCHP_PDMC_IER: 84550291652SCodrin Ciubotariu case MCHP_PDMC_IDR: 84650291652SCodrin Ciubotariu return true; 84750291652SCodrin Ciubotariu default: 84850291652SCodrin Ciubotariu return false; 84950291652SCodrin Ciubotariu } 85050291652SCodrin Ciubotariu } 85150291652SCodrin Ciubotariu 852c5682e2bSClaudiu Beznea static bool mchp_pdmc_volatile_reg(struct device *dev, unsigned int reg) 853c5682e2bSClaudiu Beznea { 854c5682e2bSClaudiu Beznea switch (reg) { 855c5682e2bSClaudiu Beznea case MCHP_PDMC_ISR: 856c5682e2bSClaudiu Beznea case MCHP_PDMC_RHR: 857c5682e2bSClaudiu Beznea return true; 858c5682e2bSClaudiu Beznea default: 859c5682e2bSClaudiu Beznea return false; 860c5682e2bSClaudiu Beznea } 861c5682e2bSClaudiu Beznea } 862c5682e2bSClaudiu Beznea 86350291652SCodrin Ciubotariu static bool mchp_pdmc_precious_reg(struct device *dev, unsigned int reg) 86450291652SCodrin Ciubotariu { 86550291652SCodrin Ciubotariu switch (reg) { 86650291652SCodrin Ciubotariu case MCHP_PDMC_RHR: 86750291652SCodrin Ciubotariu case MCHP_PDMC_ISR: 86850291652SCodrin Ciubotariu return true; 86950291652SCodrin Ciubotariu default: 87050291652SCodrin Ciubotariu return false; 87150291652SCodrin Ciubotariu } 87250291652SCodrin Ciubotariu } 87350291652SCodrin Ciubotariu 87450291652SCodrin Ciubotariu static const struct regmap_config mchp_pdmc_regmap_config = { 87550291652SCodrin Ciubotariu .reg_bits = 32, 87650291652SCodrin Ciubotariu .reg_stride = 4, 87750291652SCodrin Ciubotariu .val_bits = 32, 87850291652SCodrin Ciubotariu .max_register = MCHP_PDMC_VER, 87950291652SCodrin Ciubotariu .readable_reg = mchp_pdmc_readable_reg, 88050291652SCodrin Ciubotariu .writeable_reg = mchp_pdmc_writeable_reg, 88150291652SCodrin Ciubotariu .precious_reg = mchp_pdmc_precious_reg, 882c5682e2bSClaudiu Beznea .volatile_reg = mchp_pdmc_volatile_reg, 883e8c8e9deSClaudiu Beznea .cache_type = REGCACHE_FLAT, 88450291652SCodrin Ciubotariu }; 88550291652SCodrin Ciubotariu 88650291652SCodrin Ciubotariu static int mchp_pdmc_dt_init(struct mchp_pdmc *dd) 88750291652SCodrin Ciubotariu { 88850291652SCodrin Ciubotariu struct device_node *np = dd->dev->of_node; 88950291652SCodrin Ciubotariu bool mic_ch[MCHP_PDMC_DS_NO][MCHP_PDMC_EDGE_NO] = {0}; 89050291652SCodrin Ciubotariu int i; 89150291652SCodrin Ciubotariu int ret; 89250291652SCodrin Ciubotariu 89350291652SCodrin Ciubotariu if (!np) { 89450291652SCodrin Ciubotariu dev_err(dd->dev, "device node not found\n"); 89550291652SCodrin Ciubotariu return -EINVAL; 89650291652SCodrin Ciubotariu } 89750291652SCodrin Ciubotariu 89850291652SCodrin Ciubotariu dd->mic_no = of_property_count_u32_elems(np, "microchip,mic-pos"); 89950291652SCodrin Ciubotariu if (dd->mic_no < 0) { 900c639e85eSCodrin Ciubotariu dev_err(dd->dev, "failed to get microchip,mic-pos: %d", 90150291652SCodrin Ciubotariu dd->mic_no); 90250291652SCodrin Ciubotariu return dd->mic_no; 90350291652SCodrin Ciubotariu } 90450291652SCodrin Ciubotariu if (!dd->mic_no || dd->mic_no % 2 || 90550291652SCodrin Ciubotariu dd->mic_no / 2 > MCHP_PDMC_MAX_CHANNELS) { 906c639e85eSCodrin Ciubotariu dev_err(dd->dev, "invalid array length for microchip,mic-pos: %d", 90750291652SCodrin Ciubotariu dd->mic_no); 90850291652SCodrin Ciubotariu return -EINVAL; 90950291652SCodrin Ciubotariu } 91050291652SCodrin Ciubotariu 91150291652SCodrin Ciubotariu dd->mic_no /= 2; 91250291652SCodrin Ciubotariu 9136b6bb5e2SColin Ian King dev_info(dd->dev, "%d PDM microphones declared\n", dd->mic_no); 91450291652SCodrin Ciubotariu 915c639e85eSCodrin Ciubotariu /* 916c639e85eSCodrin Ciubotariu * by default, we consider the order of microphones in 917c639e85eSCodrin Ciubotariu * microchip,mic-pos to be the same with the channel mapping; 918c639e85eSCodrin Ciubotariu * 1st microphone channel 0, 2nd microphone channel 1, etc. 91950291652SCodrin Ciubotariu */ 92050291652SCodrin Ciubotariu for (i = 0; i < dd->mic_no; i++) { 92150291652SCodrin Ciubotariu int ds; 92250291652SCodrin Ciubotariu int edge; 92350291652SCodrin Ciubotariu 92450291652SCodrin Ciubotariu ret = of_property_read_u32_index(np, "microchip,mic-pos", i * 2, 92550291652SCodrin Ciubotariu &ds); 92650291652SCodrin Ciubotariu if (ret) { 92750291652SCodrin Ciubotariu dev_err(dd->dev, 92850291652SCodrin Ciubotariu "failed to get value no %d value from microchip,mic-pos: %d", 92950291652SCodrin Ciubotariu i * 2, ret); 93050291652SCodrin Ciubotariu return ret; 93150291652SCodrin Ciubotariu } 93250291652SCodrin Ciubotariu if (ds >= MCHP_PDMC_DS_NO) { 93350291652SCodrin Ciubotariu dev_err(dd->dev, 93450291652SCodrin Ciubotariu "invalid DS index in microchip,mic-pos array: %d", 93550291652SCodrin Ciubotariu ds); 93650291652SCodrin Ciubotariu return -EINVAL; 93750291652SCodrin Ciubotariu } 93850291652SCodrin Ciubotariu 93950291652SCodrin Ciubotariu ret = of_property_read_u32_index(np, "microchip,mic-pos", i * 2 + 1, 94050291652SCodrin Ciubotariu &edge); 94150291652SCodrin Ciubotariu if (ret) { 94250291652SCodrin Ciubotariu dev_err(dd->dev, 94350291652SCodrin Ciubotariu "failed to get value no %d value from microchip,mic-pos: %d", 94450291652SCodrin Ciubotariu i * 2 + 1, ret); 94550291652SCodrin Ciubotariu return ret; 94650291652SCodrin Ciubotariu } 94750291652SCodrin Ciubotariu 94850291652SCodrin Ciubotariu if (edge != MCHP_PDMC_CLK_POSITIVE && 94950291652SCodrin Ciubotariu edge != MCHP_PDMC_CLK_NEGATIVE) { 95050291652SCodrin Ciubotariu dev_err(dd->dev, 95150291652SCodrin Ciubotariu "invalid edge in microchip,mic-pos array: %d", edge); 95250291652SCodrin Ciubotariu return -EINVAL; 95350291652SCodrin Ciubotariu } 95450291652SCodrin Ciubotariu if (mic_ch[ds][edge]) { 95550291652SCodrin Ciubotariu dev_err(dd->dev, 95650291652SCodrin Ciubotariu "duplicated mic (DS %d, edge %d) in microchip,mic-pos array", 95750291652SCodrin Ciubotariu ds, edge); 95850291652SCodrin Ciubotariu return -EINVAL; 95950291652SCodrin Ciubotariu } 96050291652SCodrin Ciubotariu mic_ch[ds][edge] = true; 96150291652SCodrin Ciubotariu dd->channel_mic_map[i].ds_pos = ds; 96250291652SCodrin Ciubotariu dd->channel_mic_map[i].clk_edge = edge; 96350291652SCodrin Ciubotariu } 96450291652SCodrin Ciubotariu 965c5682e2bSClaudiu Beznea dd->startup_delay_us = 150000; 966c5682e2bSClaudiu Beznea of_property_read_u32(np, "microchip,startup-delay-us", &dd->startup_delay_us); 967c5682e2bSClaudiu Beznea 96850291652SCodrin Ciubotariu return 0; 96950291652SCodrin Ciubotariu } 97050291652SCodrin Ciubotariu 97150291652SCodrin Ciubotariu /* used to clean the channel index found on RHR's MSB */ 97250291652SCodrin Ciubotariu static int mchp_pdmc_process(struct snd_pcm_substream *substream, 97350291652SCodrin Ciubotariu int channel, unsigned long hwoff, 97469d0fd34STakashi Iwai unsigned long bytes) 97550291652SCodrin Ciubotariu { 97650291652SCodrin Ciubotariu struct snd_pcm_runtime *runtime = substream->runtime; 97750291652SCodrin Ciubotariu u8 *dma_ptr = runtime->dma_area + hwoff + 97850291652SCodrin Ciubotariu channel * (runtime->dma_bytes / runtime->channels); 97950291652SCodrin Ciubotariu u8 *dma_ptr_end = dma_ptr + bytes; 98050291652SCodrin Ciubotariu unsigned int sample_size = samples_to_bytes(runtime, 1); 98150291652SCodrin Ciubotariu 98250291652SCodrin Ciubotariu for (; dma_ptr < dma_ptr_end; dma_ptr += sample_size) 98350291652SCodrin Ciubotariu *dma_ptr = 0; 98450291652SCodrin Ciubotariu 98550291652SCodrin Ciubotariu return 0; 98650291652SCodrin Ciubotariu } 98750291652SCodrin Ciubotariu 98850291652SCodrin Ciubotariu static struct snd_dmaengine_pcm_config mchp_pdmc_config = { 98950291652SCodrin Ciubotariu .process = mchp_pdmc_process, 9902bde1985SCodrin Ciubotariu .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config, 99150291652SCodrin Ciubotariu }; 99250291652SCodrin Ciubotariu 993e8c8e9deSClaudiu Beznea static int mchp_pdmc_runtime_suspend(struct device *dev) 994e8c8e9deSClaudiu Beznea { 995e8c8e9deSClaudiu Beznea struct mchp_pdmc *dd = dev_get_drvdata(dev); 996e8c8e9deSClaudiu Beznea 997e8c8e9deSClaudiu Beznea regcache_cache_only(dd->regmap, true); 998e8c8e9deSClaudiu Beznea 999e8c8e9deSClaudiu Beznea clk_disable_unprepare(dd->gclk); 1000e8c8e9deSClaudiu Beznea clk_disable_unprepare(dd->pclk); 1001e8c8e9deSClaudiu Beznea 1002e8c8e9deSClaudiu Beznea return 0; 1003e8c8e9deSClaudiu Beznea } 1004e8c8e9deSClaudiu Beznea 1005e8c8e9deSClaudiu Beznea static int mchp_pdmc_runtime_resume(struct device *dev) 1006e8c8e9deSClaudiu Beznea { 1007e8c8e9deSClaudiu Beznea struct mchp_pdmc *dd = dev_get_drvdata(dev); 1008e8c8e9deSClaudiu Beznea int ret; 1009e8c8e9deSClaudiu Beznea 1010e8c8e9deSClaudiu Beznea ret = clk_prepare_enable(dd->pclk); 1011e8c8e9deSClaudiu Beznea if (ret) { 1012e8c8e9deSClaudiu Beznea dev_err(dd->dev, 1013e8c8e9deSClaudiu Beznea "failed to enable the peripheral clock: %d\n", ret); 1014e8c8e9deSClaudiu Beznea return ret; 1015e8c8e9deSClaudiu Beznea } 1016e8c8e9deSClaudiu Beznea ret = clk_prepare_enable(dd->gclk); 1017e8c8e9deSClaudiu Beznea if (ret) { 1018e8c8e9deSClaudiu Beznea dev_err(dd->dev, 1019e8c8e9deSClaudiu Beznea "failed to enable generic clock: %d\n", ret); 1020e8c8e9deSClaudiu Beznea goto disable_pclk; 1021e8c8e9deSClaudiu Beznea } 1022e8c8e9deSClaudiu Beznea 1023e8c8e9deSClaudiu Beznea regcache_cache_only(dd->regmap, false); 1024e8c8e9deSClaudiu Beznea regcache_mark_dirty(dd->regmap); 1025e8c8e9deSClaudiu Beznea ret = regcache_sync(dd->regmap); 1026e8c8e9deSClaudiu Beznea if (ret) { 1027e8c8e9deSClaudiu Beznea regcache_cache_only(dd->regmap, true); 1028e8c8e9deSClaudiu Beznea clk_disable_unprepare(dd->gclk); 1029e8c8e9deSClaudiu Beznea disable_pclk: 1030e8c8e9deSClaudiu Beznea clk_disable_unprepare(dd->pclk); 1031e8c8e9deSClaudiu Beznea } 1032e8c8e9deSClaudiu Beznea 1033e8c8e9deSClaudiu Beznea return ret; 1034e8c8e9deSClaudiu Beznea } 1035e8c8e9deSClaudiu Beznea 103650291652SCodrin Ciubotariu static int mchp_pdmc_probe(struct platform_device *pdev) 103750291652SCodrin Ciubotariu { 103850291652SCodrin Ciubotariu struct device *dev = &pdev->dev; 103950291652SCodrin Ciubotariu struct mchp_pdmc *dd; 104050291652SCodrin Ciubotariu struct resource *res; 104150291652SCodrin Ciubotariu void __iomem *io_base; 104250291652SCodrin Ciubotariu u32 version; 104350291652SCodrin Ciubotariu int irq; 104450291652SCodrin Ciubotariu int ret; 104550291652SCodrin Ciubotariu 104650291652SCodrin Ciubotariu dd = devm_kzalloc(dev, sizeof(*dd), GFP_KERNEL); 104750291652SCodrin Ciubotariu if (!dd) 104850291652SCodrin Ciubotariu return -ENOMEM; 104950291652SCodrin Ciubotariu 105050291652SCodrin Ciubotariu dd->dev = &pdev->dev; 105150291652SCodrin Ciubotariu ret = mchp_pdmc_dt_init(dd); 105250291652SCodrin Ciubotariu if (ret < 0) 105350291652SCodrin Ciubotariu return ret; 105450291652SCodrin Ciubotariu 105550291652SCodrin Ciubotariu irq = platform_get_irq(pdev, 0); 10562588a014SJiapeng Chong if (irq < 0) 105750291652SCodrin Ciubotariu return irq; 105850291652SCodrin Ciubotariu 105950291652SCodrin Ciubotariu dd->pclk = devm_clk_get(dev, "pclk"); 106050291652SCodrin Ciubotariu if (IS_ERR(dd->pclk)) { 106150291652SCodrin Ciubotariu ret = PTR_ERR(dd->pclk); 106250291652SCodrin Ciubotariu dev_err(dev, "failed to get peripheral clock: %d\n", ret); 106350291652SCodrin Ciubotariu return ret; 106450291652SCodrin Ciubotariu } 106550291652SCodrin Ciubotariu 106650291652SCodrin Ciubotariu dd->gclk = devm_clk_get(dev, "gclk"); 106750291652SCodrin Ciubotariu if (IS_ERR(dd->gclk)) { 106850291652SCodrin Ciubotariu ret = PTR_ERR(dd->gclk); 106950291652SCodrin Ciubotariu dev_err(dev, "failed to get GCK: %d\n", ret); 107050291652SCodrin Ciubotariu return ret; 107150291652SCodrin Ciubotariu } 107250291652SCodrin Ciubotariu 107350291652SCodrin Ciubotariu io_base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); 107450291652SCodrin Ciubotariu if (IS_ERR(io_base)) { 107550291652SCodrin Ciubotariu ret = PTR_ERR(io_base); 107650291652SCodrin Ciubotariu dev_err(dev, "failed to remap register memory: %d\n", ret); 107750291652SCodrin Ciubotariu return ret; 107850291652SCodrin Ciubotariu } 107950291652SCodrin Ciubotariu 108050291652SCodrin Ciubotariu dd->regmap = devm_regmap_init_mmio(dev, io_base, 108150291652SCodrin Ciubotariu &mchp_pdmc_regmap_config); 108250291652SCodrin Ciubotariu if (IS_ERR(dd->regmap)) { 108350291652SCodrin Ciubotariu ret = PTR_ERR(dd->regmap); 108450291652SCodrin Ciubotariu dev_err(dev, "failed to init register map: %d\n", ret); 108550291652SCodrin Ciubotariu return ret; 108650291652SCodrin Ciubotariu } 108750291652SCodrin Ciubotariu 108850291652SCodrin Ciubotariu ret = devm_request_irq(dev, irq, mchp_pdmc_interrupt, 0, 1089cb72b29cSClaudiu Beznea dev_name(&pdev->dev), dd); 109050291652SCodrin Ciubotariu if (ret < 0) { 109150291652SCodrin Ciubotariu dev_err(dev, "can't register ISR for IRQ %u (ret=%i)\n", 109250291652SCodrin Ciubotariu irq, ret); 109350291652SCodrin Ciubotariu return ret; 109450291652SCodrin Ciubotariu } 109550291652SCodrin Ciubotariu 109650291652SCodrin Ciubotariu /* by default audio filter is enabled and the SINC Filter order 109750291652SCodrin Ciubotariu * will be set to the recommended value, 3 109850291652SCodrin Ciubotariu */ 109950291652SCodrin Ciubotariu dd->audio_filter_en = true; 110050291652SCodrin Ciubotariu dd->sinc_order = 3; 110150291652SCodrin Ciubotariu 110250291652SCodrin Ciubotariu dd->addr.addr = (dma_addr_t)res->start + MCHP_PDMC_RHR; 110350291652SCodrin Ciubotariu platform_set_drvdata(pdev, dd); 110450291652SCodrin Ciubotariu 1105e8c8e9deSClaudiu Beznea pm_runtime_enable(dd->dev); 1106e8c8e9deSClaudiu Beznea if (!pm_runtime_enabled(dd->dev)) { 1107e8c8e9deSClaudiu Beznea ret = mchp_pdmc_runtime_resume(dd->dev); 1108e8c8e9deSClaudiu Beznea if (ret) 1109e8c8e9deSClaudiu Beznea return ret; 1110e8c8e9deSClaudiu Beznea } 1111e8c8e9deSClaudiu Beznea 111250291652SCodrin Ciubotariu /* register platform */ 111350291652SCodrin Ciubotariu ret = devm_snd_dmaengine_pcm_register(dev, &mchp_pdmc_config, 0); 111450291652SCodrin Ciubotariu if (ret) { 111550291652SCodrin Ciubotariu dev_err(dev, "could not register platform: %d\n", ret); 1116e8c8e9deSClaudiu Beznea goto pm_runtime_suspend; 111750291652SCodrin Ciubotariu } 111850291652SCodrin Ciubotariu 111950291652SCodrin Ciubotariu ret = devm_snd_soc_register_component(dev, &mchp_pdmc_dai_component, 112050291652SCodrin Ciubotariu &mchp_pdmc_dai, 1); 112150291652SCodrin Ciubotariu if (ret) { 112250291652SCodrin Ciubotariu dev_err(dev, "could not register CPU DAI: %d\n", ret); 1123e8c8e9deSClaudiu Beznea goto pm_runtime_suspend; 112450291652SCodrin Ciubotariu } 112550291652SCodrin Ciubotariu 112650291652SCodrin Ciubotariu /* print IP version */ 112750291652SCodrin Ciubotariu regmap_read(dd->regmap, MCHP_PDMC_VER, &version); 112850291652SCodrin Ciubotariu dev_info(dd->dev, "hw version: %#lx\n", 112950291652SCodrin Ciubotariu version & MCHP_PDMC_VER_VERSION); 113050291652SCodrin Ciubotariu 113150291652SCodrin Ciubotariu return 0; 1132e8c8e9deSClaudiu Beznea 1133e8c8e9deSClaudiu Beznea pm_runtime_suspend: 1134e8c8e9deSClaudiu Beznea if (!pm_runtime_status_suspended(dd->dev)) 1135e8c8e9deSClaudiu Beznea mchp_pdmc_runtime_suspend(dd->dev); 1136e8c8e9deSClaudiu Beznea pm_runtime_disable(dd->dev); 1137e8c8e9deSClaudiu Beznea 1138e8c8e9deSClaudiu Beznea return ret; 1139e8c8e9deSClaudiu Beznea } 1140e8c8e9deSClaudiu Beznea 114186fdd482SUwe Kleine-König static void mchp_pdmc_remove(struct platform_device *pdev) 1142e8c8e9deSClaudiu Beznea { 1143e8c8e9deSClaudiu Beznea struct mchp_pdmc *dd = platform_get_drvdata(pdev); 1144e8c8e9deSClaudiu Beznea 1145e8c8e9deSClaudiu Beznea if (!pm_runtime_status_suspended(dd->dev)) 1146e8c8e9deSClaudiu Beznea mchp_pdmc_runtime_suspend(dd->dev); 1147e8c8e9deSClaudiu Beznea 1148e8c8e9deSClaudiu Beznea pm_runtime_disable(dd->dev); 114950291652SCodrin Ciubotariu } 115050291652SCodrin Ciubotariu 115150291652SCodrin Ciubotariu static const struct of_device_id mchp_pdmc_of_match[] = { 115250291652SCodrin Ciubotariu { 115350291652SCodrin Ciubotariu .compatible = "microchip,sama7g5-pdmc", 115450291652SCodrin Ciubotariu }, { 115550291652SCodrin Ciubotariu /* sentinel */ 115650291652SCodrin Ciubotariu } 115750291652SCodrin Ciubotariu }; 115850291652SCodrin Ciubotariu MODULE_DEVICE_TABLE(of, mchp_pdmc_of_match); 115950291652SCodrin Ciubotariu 1160e8c8e9deSClaudiu Beznea static const struct dev_pm_ops mchp_pdmc_pm_ops = { 1161404c61c4SClaudiu Beznea SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) 1162e8c8e9deSClaudiu Beznea RUNTIME_PM_OPS(mchp_pdmc_runtime_suspend, mchp_pdmc_runtime_resume, 1163e8c8e9deSClaudiu Beznea NULL) 1164e8c8e9deSClaudiu Beznea }; 1165e8c8e9deSClaudiu Beznea 116650291652SCodrin Ciubotariu static struct platform_driver mchp_pdmc_driver = { 116750291652SCodrin Ciubotariu .driver = { 116850291652SCodrin Ciubotariu .name = "mchp-pdmc", 116950291652SCodrin Ciubotariu .of_match_table = of_match_ptr(mchp_pdmc_of_match), 1170e8c8e9deSClaudiu Beznea .pm = pm_ptr(&mchp_pdmc_pm_ops), 117150291652SCodrin Ciubotariu }, 117250291652SCodrin Ciubotariu .probe = mchp_pdmc_probe, 1173130af75bSUwe Kleine-König .remove = mchp_pdmc_remove, 117450291652SCodrin Ciubotariu }; 117550291652SCodrin Ciubotariu module_platform_driver(mchp_pdmc_driver); 117650291652SCodrin Ciubotariu 117750291652SCodrin Ciubotariu MODULE_DESCRIPTION("Microchip PDMC driver under ALSA SoC architecture"); 117850291652SCodrin Ciubotariu MODULE_AUTHOR("Codrin Ciubotariu <codrin.ciubotariu@microchip.com>"); 117950291652SCodrin Ciubotariu MODULE_LICENSE("GPL v2"); 1180