/* * sound\soc\sunxi\sunxi-dmic.c * (C) Copyright 2014-2018 * AllWinner Technology Co., Ltd. * wolfgang huang * huangxin * yumingfeng * * some simple description for this code * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "sunxi-pcm.h" #include "sunxi-dmic.h" #include "sunxi-snddmic.h" #ifdef CONFIG_SND_SUNXI_MAD #include "sunxi-mad.h" #endif #define DRV_NAME "sunxi-dmic" #define SUNXI_DMIC_RX_SYNC_EN (0) #if defined(CONFIG_ARCH_SUN50IW6) || defined(CONFIG_ARCH_SUN8IW15) || \ defined(CONFIG_ARCH_SUN8IW16) || defined(CONFIG_ARCH_SUN8IW17) ||\ defined(CONFIG_ARCH_SUN8IW18) #undef DMIC_AUDIO_DEMAND #else #define DMIC_AUDIO_DEMAND #endif #define SUNXI_DMIC_DEBUG struct dmic_rate { unsigned int samplerate; unsigned int rate_bit; }; struct dmic_chmap { unsigned int chan; unsigned int chan_bit; }; struct sunxi_dmic_info *sunxi_dmic; static const struct dmic_rate dmic_rate_s[] = { {44100, 0x0}, {48000, 0x0}, {22050, 0x2}, /* KNOT support */ {24000, 0x2}, {11025, 0x4}, {12000, 0x4}, {32000, 0x1}, {16000, 0x3}, {8000, 0x5}, }; static struct sunxi_dmic_reg_label reg_labels[] = { SUNXI_DMIC_REG_LABEL(SUNXI_DMIC_EN), SUNXI_DMIC_REG_LABEL(SUNXI_DMIC_SR), SUNXI_DMIC_REG_LABEL(SUNXI_DMIC_CTR), SUNXI_DMIC_REG_LABEL(SUNXI_DMIC_INTC), SUNXI_DMIC_REG_LABEL(SUNXI_DMIC_INTS), SUNXI_DMIC_REG_LABEL(SUNXI_DMIC_FIFO_CTR), SUNXI_DMIC_REG_LABEL(SUNXI_DMIC_FIFO_STA), SUNXI_DMIC_REG_LABEL(SUNXI_DMIC_CH_NUM), SUNXI_DMIC_REG_LABEL(SUNXI_DMIC_CH_MAP), SUNXI_DMIC_REG_LABEL(SUNXI_DMIC_CNT), SUNXI_DMIC_REG_LABEL(SUNXI_DMIC_DATA0_1_VOL), SUNXI_DMIC_REG_LABEL(SUNXI_DMIC_DATA2_3_VOL), SUNXI_DMIC_REG_LABEL(SUNXI_DMIC_HPF_CTRL), SUNXI_DMIC_REG_LABEL(SUNXI_DMIC_HPF_COEF), SUNXI_DMIC_REG_LABEL(SUNXI_DMIC_HPF_GAIN), SUNXI_DMIC_REG_LABEL_END, }; #ifdef SUNXI_DMIC_DEBUG /* * ex: * param 1: 0 read;1 write * param 2: address; * param 3: write value; * read: * echo 0,0x00 > dmic_reg_debug * echo 0,0x04 > dmic_reg_debug * write: * echo 1,0x00,0xa > dmic_reg_debug * echo 1,0x00,0xff > dmic_reg_debug */ static ssize_t dmic_class_debug_store(struct class *class, struct class_attribute *attr, const char *buf, size_t count) { int ret; int rw_flag; int reg_val_read; unsigned int input_reg_val = 0; unsigned int input_reg_offset = 0; ret = sscanf(buf, "%d,0x%x,0x%x", &rw_flag, &input_reg_offset, &input_reg_val); if (!(rw_flag == 1 || rw_flag == 0)) { pr_err("not rw_flag\n"); return count; } if (ret == 3 || ret == 2) { if (rw_flag) { writel(input_reg_val, sunxi_dmic->membase + input_reg_offset); } reg_val_read = readl(sunxi_dmic->membase + input_reg_offset); pr_err("\n\n Reg[0x%x] : 0x%x\n\n", input_reg_offset, reg_val_read); } else { pr_err("ret:%d, The num of params invalid!\n", ret); pr_err("\nExample:\n"); pr_err("\nRead reg[0x04]:\n"); pr_err(" echo 0,0x04 > dmic_reg_debug\n"); pr_err("Write reg[0x04]=0x10\n"); pr_err(" echo 1,0x04,0x10 > dmic_reg_debug\n"); } return count; } static ssize_t dmic_class_debug_show(struct class *class, struct class_attribute *attr, char *buf) { int count = 0; int i = 0; count += sprintf(buf, "Dump dmic reg:\n"); while (reg_labels[i].name != NULL) { count += sprintf(buf + count, "%s 0x%p: 0x%x\n", reg_labels[i].name, (sunxi_dmic->membase + reg_labels[i].value), readl(sunxi_dmic->membase + reg_labels[i].value)); i++; } return count; } #ifdef CONFIG_SND_SUNXI_MAD static unsigned int input_mad_resume; /* * ex: * param 1: 0 suspend;1 resume * write: * echo 1 > dmic_mad_debug * echo 0 > dmic_mad_debug */ static ssize_t dmic_mad_debug_store(struct class *class, struct class_attribute *attr, const char *buf, size_t count) { int ret; ret = sscanf(buf, "%u", &input_mad_resume); pr_err("[%s] input_mad_resume: %d\n", __func__, input_mad_resume); return count; } static ssize_t dmic_mad_debug_show(struct class *class, struct class_attribute *attr, char *buf) { int count = 0; count += sprintf(buf, "Dump dmic info:\n"); if (input_mad_resume == 1) { if (sunxi_dmic->mad_bind == 1) { sunxi_mad_resume_external(); count += sprintf(buf + count, "mad resume succeed %s\n", __func__); } } else { if (sunxi_dmic->mad_bind == 1) { sunxi_mad_suspend_external(); count += sprintf(buf + count, "mad suspend succeed %s\n", __func__); } } return count; } #endif static struct class_attribute dmic_class_attrs[] = { __ATTR(dmic_reg_debug, 0644, dmic_class_debug_show, dmic_class_debug_store), #ifdef CONFIG_SND_SUNXI_MAD __ATTR(dmic_mad_debug, 0644, dmic_mad_debug_show, dmic_mad_debug_store), #endif __ATTR_NULL, }; static struct class dmic_class = { .name = "dmic", .class_attrs = dmic_class_attrs, }; #endif /* * Configure DMA , Chan enable & Global enable */ static void sunxi_dmic_enable(struct sunxi_dmic_info *sunxi_dmic, bool enable) { if (enable) { #ifdef CONFIG_SND_SUNXI_MAD /*enable dmic*/ if (sunxi_dmic->mad_bind == 1) { sunxi_mad_dma_type(SUNXI_MAD_DMA_IO); regmap_update_bits(sunxi_dmic->regmap, SUNXI_DMIC_EN, (0x1 << DMIC_MAD_DATA_EN), (0x1 << DMIC_MAD_DATA_EN)); sunxi_mad_dma_enable(true); } else #endif { regmap_update_bits(sunxi_dmic->regmap, SUNXI_DMIC_INTC, (0x1 << FIFO_DRQ_EN), (0x1 << FIFO_DRQ_EN)); } regmap_update_bits(sunxi_dmic->regmap, SUNXI_DMIC_EN, (0xFF<chanmap)<regmap, SUNXI_DMIC_EN, (0x1 << GLOBE_EN), (0x1 << GLOBE_EN)); #if SUNXI_DMIC_RX_SYNC_EN regmap_update_bits(sunxi_dmic->regmap, SUNXI_DMIC_EN, (0x1 << DMIC_RX_EN_MUX), (0x1 << DMIC_RX_EN_MUX)); regmap_update_bits(sunxi_dmic->regmap, SUNXI_DMIC_EN, (0x1 << DMIC_RX_SYNC_EN), (0x1 << DMIC_RX_SYNC_EN)); #endif } else { #ifdef CONFIG_SND_SUNXI_MAD if (sunxi_dmic->mad_bind == 1) sunxi_mad_dma_enable(false); else #endif { regmap_update_bits(sunxi_dmic->regmap, SUNXI_DMIC_EN, (0x1 << GLOBE_EN), (0x0 << GLOBE_EN)); regmap_update_bits(sunxi_dmic->regmap, SUNXI_DMIC_EN, (0xFF << DATA_CH_EN), (0x0 << DATA_CH_EN)); regmap_update_bits(sunxi_dmic->regmap, SUNXI_DMIC_INTC, (0x1 << FIFO_DRQ_EN), (0x0 << FIFO_DRQ_EN)); #if SUNXI_DMIC_RX_SYNC_EN regmap_update_bits(sunxi_dmic->regmap, SUNXI_DMIC_EN, (0x1 << DMIC_RX_EN_MUX), (0x0 << DMIC_RX_EN_MUX)); regmap_update_bits(sunxi_dmic->regmap, SUNXI_DMIC_EN, (0x1 << DMIC_RX_SYNC_EN), (0x0 << DMIC_RX_SYNC_EN)); #endif } } } static void sunxi_dmic_init(struct sunxi_dmic_info *sunxi_dmic) { regmap_write(sunxi_dmic->regmap, SUNXI_DMIC_CH_MAP, DMIC_CHANMAP_DEFAULT); regmap_update_bits(sunxi_dmic->regmap, SUNXI_DMIC_CTR, (0x7<regmap, SUNXI_DMIC_DATA0_1_VOL, DMIC_DEFAULT_VOL); regmap_write(sunxi_dmic->regmap, SUNXI_DMIC_DATA2_3_VOL, DMIC_DEFAULT_VOL); } static int sunxi_dmic_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { struct sunxi_dmic_info *sunxi_dmic = snd_soc_dai_get_drvdata(dai); int i; #ifdef CONFIG_SND_SUNXI_MAD struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_card *card = rtd->card; struct sunxi_snddmic_priv *snddmic_priv = snd_soc_card_get_drvdata(card); sunxi_dmic->mad_bind = snddmic_priv->mad_priv.mad_bind; #endif /* if clk rst */ sunxi_dmic_init(sunxi_dmic); /* sample resolution & sample fifo format */ switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: regmap_update_bits(sunxi_dmic->regmap, SUNXI_DMIC_FIFO_CTR, (0x1 << DMIC_SAMPLE_RESOLUTION), (0x0 << DMIC_SAMPLE_RESOLUTION)); regmap_update_bits(sunxi_dmic->regmap, SUNXI_DMIC_FIFO_CTR, (0x1 << DMIC_FIFO_MODE), (0x1 << DMIC_FIFO_MODE)); break; case SNDRV_PCM_FORMAT_S24_LE: regmap_update_bits(sunxi_dmic->regmap, SUNXI_DMIC_FIFO_CTR, (0x1 << DMIC_SAMPLE_RESOLUTION), (0x1 << DMIC_SAMPLE_RESOLUTION)); regmap_update_bits(sunxi_dmic->regmap, SUNXI_DMIC_FIFO_CTR, (0x1 << DMIC_FIFO_MODE), (0x0 << DMIC_FIFO_MODE)); break; default: dev_err(sunxi_dmic->dev, "Invalid format set\n"); return -EINVAL; } for (i = 0; i < ARRAY_SIZE(dmic_rate_s); i++) { if (dmic_rate_s[i].samplerate == params_rate(params)) { regmap_update_bits(sunxi_dmic->regmap, SUNXI_DMIC_SR, (7<= 24000) { regmap_update_bits(sunxi_dmic->regmap, SUNXI_DMIC_CTR, (1<regmap, SUNXI_DMIC_CTR, (1<chanmap = (1<regmap, SUNXI_DMIC_HPF_CTRL, sunxi_dmic->chanmap); /* DMIC num is M+1 */ regmap_update_bits(sunxi_dmic->regmap, SUNXI_DMIC_CH_NUM, (7<mad_bind == 1) { if (params_format(params) != SNDRV_PCM_FORMAT_S16_LE) { dev_err(sunxi_dmic->dev, "unsupported mad sample bits!\n"); return -EINVAL; } if ((params_rate(params) == 16000) || (params_rate(params) == 48000)) { #ifdef MAD_CLK_ALWAYS_ON sunxi_mad_module_clk_enable(false); #endif sunxi_mad_module_clk_enable(true); sunxi_mad_open(); /* mad config */ sunxi_mad_hw_params(params_channels(params), params_rate(params)); sunxi_dmic->audio_src_chan_num = params_channels(params); sunxi_mad_audio_src_chan_num(sunxi_dmic->audio_src_chan_num); sunxi_lpsd_chan_sel(sunxi_dmic->lpsd_chan_sel); sunxi_mad_standby_chan_sel(sunxi_dmic->mad_standby_chan_sel); sunxi_sram_ahb1_threshole_init(); sunxi_sram_dma_config(&sunxi_dmic->capture_dma_param); sunxi_mad_sram_init(); /* * should set it after sram reset. * path_sel: dmic-3 */ sunxi_mad_audio_source_sel(MAD_PATH_DMIC, 1); } else { dev_err(sunxi_dmic->dev, "unsupported mad rate\n"); return -EINVAL; } } #endif return 0; } static int sunxi_dmic_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { int ret = 0; struct sunxi_dmic_info *sunxi_dmic = snd_soc_dai_get_drvdata(dai); switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: sunxi_dmic_enable(sunxi_dmic, true); break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: sunxi_dmic_enable(sunxi_dmic, false); break; default: ret = -EINVAL; break; } return ret; } /* * Reset & Flush FIFO */ static int sunxi_dmic_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct sunxi_dmic_info *sunxi_dmic = snd_soc_dai_get_drvdata(dai); regmap_update_bits(sunxi_dmic->regmap, SUNXI_DMIC_FIFO_CTR, (1<regmap, SUNXI_DMIC_INTS, (1<regmap, SUNXI_DMIC_CNT, 0x0); return 0; } static int sunxi_dmic_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct sunxi_dmic_info *sunxi_dmic = snd_soc_dai_get_drvdata(dai); snd_soc_dai_set_dma_data(dai, substream, &sunxi_dmic->capture_dma_param); return 0; } static int sunxi_dmic_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int freq, int dir) { struct sunxi_dmic_info *sunxi_dmic = snd_soc_dai_get_drvdata(dai); #if defined(DMIC_PLL_AUDIO_X4) && defined(CONFIG_ARCH_SUN50IW10) if (clk_set_rate(sunxi_dmic->pllclkx4, freq * 4)) { dev_err(sunxi_dmic->dev, "set pllclkx4 %u failed\n", freq * 4); return -EINVAL; } if (clk_set_rate(sunxi_dmic->moduleclk, freq)) { dev_err(sunxi_dmic->dev, "set moduleclk %u failed\n", freq); return -EINVAL; } #else if (clk_set_rate(sunxi_dmic->pllclk, freq)) { dev_err(sunxi_dmic->dev, "set pllclk %u failed\n", freq); return -EINVAL; } #endif #if defined(ARCH_SUN50IW3) || defined(ARCH_SUN8IW15) || defined(ARCH_SUN8IW17) if (sunxi_dmic->moduleclk) { clk_prepare_enable(sunxi_dmic->moduleclk); sunxi_dmic->moduleclk_en = 1; } #endif return 0; } /* Dmic module init status */ static int sunxi_dmic_probe(struct snd_soc_dai *dai) { struct sunxi_dmic_info *sunxi_dmic = snd_soc_dai_get_drvdata(dai); sunxi_dmic_init(sunxi_dmic); return 0; } static int sunxi_dmic_suspend(struct snd_soc_dai *cpu_dai) { u32 ret = 0; struct sunxi_dmic_info *sunxi_dmic = snd_soc_dai_get_drvdata(cpu_dai); pr_debug("[DMIC]Enter %s\n", __func__); #ifdef CONFIG_SND_SUNXI_MAD if (sunxi_dmic->mad_bind == 1) { sunxi_mad_suspend_external(); pr_debug("mad suspend succeed %s\n", __func__); return 0; } #endif if (sunxi_dmic->pinstate_sleep != NULL) { ret = pinctrl_select_state(sunxi_dmic->pinctrl, sunxi_dmic->pinstate_sleep); if (ret) { pr_warn("[dmic]select pin sleep state failed\n"); return ret; } } if ((sunxi_dmic->moduleclk != NULL) && (sunxi_dmic->moduleclk_en == 1)) clk_disable(sunxi_dmic->moduleclk); #ifdef DMIC_PLL_AUDIO_X4 if (sunxi_dmic->pllclkx4 != NULL) clk_disable(sunxi_dmic->pllclkx4); #endif if (sunxi_dmic->pllclk != NULL) clk_disable(sunxi_dmic->pllclk); pr_debug("[DMIC]End %s\n", __func__); return 0; } static int sunxi_dmic_resume(struct snd_soc_dai *cpu_dai) { s32 ret = 0; struct sunxi_dmic_info *sunxi_dmic = snd_soc_dai_get_drvdata(cpu_dai); pr_debug("[DMIC]Enter %s\n", __func__); #ifdef CONFIG_SND_SUNXI_MAD if (sunxi_dmic->mad_bind == 1) { sunxi_mad_resume_external(); pr_debug("mad resume succeed %s\n", __func__); return 0; } #endif if (sunxi_dmic->pllclk != NULL) { if (clk_prepare_enable(sunxi_dmic->pllclk)) { pr_err("enable sunxi_dmic->pllclk failed!\n"); } } #ifdef DMIC_PLL_AUDIO_X4 if (sunxi_dmic->pllclkx4 != NULL) { if (clk_prepare_enable(sunxi_dmic->pllclkx4)) { pr_err("enable sunxi_dmic->pllclkx4 failed!\n"); } } #endif if ((sunxi_dmic->moduleclk != NULL) && (sunxi_dmic->moduleclk_en == 1)) { if (clk_prepare_enable(sunxi_dmic->moduleclk)) pr_err("[dmic] enable sunxi_dmic->moduleclk failed!\n"); } if (sunxi_dmic->pinstate != NULL) { ret = pinctrl_select_state(sunxi_dmic->pinctrl, sunxi_dmic->pinstate); if (ret) { pr_warn("[dmic]select pin default state failed\n"); return ret; } } if (sunxi_dmic->moduleclk_en == 1) sunxi_dmic_init(sunxi_dmic); pr_debug("[DMIC]End %s\n", __func__); return 0; } static void sunxi_dmic_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct sunxi_dmic_info *sunxi_dmic = snd_soc_dai_get_drvdata(dai); #ifdef CONFIG_SND_SUNXI_MAD if (sunxi_dmic->mad_bind == 1) { regmap_update_bits(sunxi_dmic->regmap, SUNXI_DMIC_EN, (0x1 << DMIC_MAD_DATA_EN), (0x0 << DMIC_MAD_DATA_EN)); sunxi_mad_audio_source_sel(MAD_PATH_DMIC, 0); /* should close the GLOBE_EN */ regmap_update_bits(sunxi_dmic->regmap, SUNXI_DMIC_EN, (0x1 << GLOBE_EN), (0x0 << GLOBE_EN)); regmap_update_bits(sunxi_dmic->regmap, SUNXI_DMIC_EN, (0xFF << DATA_CH_EN), (0x0 << DATA_CH_EN)); sunxi_mad_close(); sunxi_dmic->mad_bind = 0; #ifndef MAD_CLK_ALWAYS_ON sunxi_mad_module_clk_enable(false); #endif /*if not use mad again*/ sunxi_dmic->capture_dma_param.dma_addr = sunxi_dmic->res.start + SUNXI_DMIC_DATA; sunxi_dmic->capture_dma_param.dma_drq_type_num = DRQSRC_DMIC; } #endif #if defined(ARCH_SUN50IW3) || defined(ARCH_SUN8IW15) || defined(ARCH_SUN8IW17) /* dmic:Gating and reset should be disabled after shutdown */ if (sunxi_dmic->moduleclk) { clk_disable(sunxi_dmic->moduleclk); sunxi_dmic->moduleclk_en = 0; } #endif pr_debug("[%s] moduleclk_en:%d\n", __func__, sunxi_dmic->moduleclk_en); } #ifdef CONFIG_SND_SUNXI_MAD /*lpsd channel sel*/ static int sunxi_dmic_set_lpsd_chan(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct snd_soc_codec *codec = snd_soc_component_to_codec(component); struct sunxi_dmic_info *sunxi_dmic = snd_soc_codec_get_drvdata(codec); sunxi_dmic->lpsd_chan_sel = ucontrol->value.integer.value[0]; sunxi_lpsd_chan_sel(sunxi_dmic->lpsd_chan_sel); return 0; } static int sunxi_dmic_get_lpsd_chan(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct snd_soc_codec *codec = snd_soc_component_to_codec(component); struct sunxi_dmic_info *sunxi_dmic = snd_soc_codec_get_drvdata(codec); ucontrol->value.integer.value[0] = sunxi_dmic->lpsd_chan_sel; return 0; } static const char * const lpsd_chan_sel_function[] = {"0th_chan", "1st_chan", "2nd_chan", "3rd_chan", "4th_chan", "5th_chan", "6th_chan", "7th_chan", "8th_chan"}; static const struct soc_enum lpsd_chan_sel_enum[] = { SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(lpsd_chan_sel_function), lpsd_chan_sel_function), }; /*mad_standby channel sel*/ static int sunxi_dmic_set_mad_standby_chan(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct snd_soc_codec *codec = snd_soc_component_to_codec(component); struct sunxi_dmic_info *sunxi_dmic = snd_soc_codec_get_drvdata(codec); sunxi_dmic->mad_standby_chan_sel = ucontrol->value.integer.value[0]; sunxi_mad_standby_chan_sel(sunxi_dmic->mad_standby_chan_sel); return 0; } static int sunxi_dmic_get_mad_standby_chan(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct snd_soc_codec *codec = snd_soc_component_to_codec(component); struct sunxi_dmic_info *sunxi_dmic = snd_soc_codec_get_drvdata(codec); ucontrol->value.integer.value[0] = sunxi_dmic->mad_standby_chan_sel; return 0; } static const char * const mad_standby_chan_sel_function[] = { "Zero_Chan", "Two_Chan", "Four_Chan", }; static const struct soc_enum mad_standby_chan_sel_enum[] = { SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(mad_standby_chan_sel_function), mad_standby_chan_sel_function), }; /* dmic kcontrols */ static const struct snd_kcontrol_new sunxi_dmic_controls[] = { SOC_ENUM_EXT("lpsd channel sel function", lpsd_chan_sel_enum[0], sunxi_dmic_get_lpsd_chan, sunxi_dmic_set_lpsd_chan), SOC_ENUM_EXT("mad standby channel sel function", mad_standby_chan_sel_enum[0], sunxi_dmic_get_mad_standby_chan, sunxi_dmic_set_mad_standby_chan), }; #endif #define SUNXI_DMIC_RATES (SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_KNOT) static struct snd_soc_dai_ops sunxi_dmic_dai_ops = { .startup = sunxi_dmic_startup, .trigger = sunxi_dmic_trigger, .prepare = sunxi_dmic_prepare, .hw_params = sunxi_dmic_hw_params, .set_sysclk = sunxi_dmic_set_sysclk, .shutdown = sunxi_dmic_shutdown, }; static struct snd_soc_dai_driver sunxi_dmic_dai = { .probe = sunxi_dmic_probe, .suspend = sunxi_dmic_suspend, .resume = sunxi_dmic_resume, .capture = { .channels_min = 1, .channels_max = 8, .rates = SUNXI_DMIC_RATES, .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, }, .ops = &sunxi_dmic_dai_ops, }; static const struct snd_soc_component_driver sunxi_dmic_component = { .name = DRV_NAME, #ifdef CONFIG_SND_SUNXI_MAD .controls = sunxi_dmic_controls, .num_controls = ARRAY_SIZE(sunxi_dmic_controls), #endif }; static const struct regmap_config sunxi_dmic_regmap_config = { .reg_bits = 32, .reg_stride = 4, .val_bits = 32, .max_register = SUNXI_DMIC_HPF_GAIN, .cache_type = REGCACHE_NONE, }; static int sunxi_dmic_dev_probe(struct platform_device *pdev) { struct resource res; struct device_node *np = pdev->dev.of_node; int ret; sunxi_dmic = devm_kzalloc(&pdev->dev, sizeof(struct sunxi_dmic_info), GFP_KERNEL); if (!sunxi_dmic) { dev_err(&pdev->dev, "sunxi_dmic allocate failed\n"); ret = -ENOMEM; goto err_node_put; } dev_set_drvdata(&pdev->dev, sunxi_dmic); sunxi_dmic->dev = &pdev->dev; sunxi_dmic->dai = sunxi_dmic_dai; sunxi_dmic->dai.name = dev_name(&pdev->dev); #ifdef DMIC_AUDIO_DEMAND sunxi_dmic->power_supply = regulator_get(&pdev->dev, "audio-33"); if (IS_ERR(sunxi_dmic->power_supply)) { dev_err(&pdev->dev, "Failed to get sunxi dmic power supply\n"); ret = -EINVAL; goto err_devm_kfree; } else { ret = regulator_set_voltage(sunxi_dmic->power_supply, 3300000, 3300000); if (ret) dev_warn(&pdev->dev, "Failed to set sunxi dmic power supply to 3.3V\n"); ret = regulator_enable(sunxi_dmic->power_supply); if (ret) { dev_err(&pdev->dev, "Failed to enable sunxi dmic power supply\n"); ret = -EBUSY; goto err_regulator_put; } } #endif ret = of_address_to_resource(np, 0, &res); if (ret) { dev_err(&pdev->dev, "Failed to get sunxi dmic resource\n"); ret = -EINVAL; goto err_regulator_put; } #ifdef CONFIG_SND_SUNXI_MAD sunxi_dmic->mad_bind = 0; sunxi_dmic->lpsd_chan_sel = 1; sunxi_dmic->mad_standby_chan_sel = 1; sunxi_dmic->res.start = res.start; #endif sunxi_dmic->memregion = devm_request_mem_region(&pdev->dev, res.start, resource_size(&res), DRV_NAME); if (!sunxi_dmic->memregion) { dev_err(&pdev->dev, "sunxi dmic memory region already claimed\n"); ret = -EBUSY; goto err_regulator_put; } sunxi_dmic->membase = devm_ioremap(&pdev->dev, res.start, resource_size(&res)); if (!sunxi_dmic->membase) { dev_err(&pdev->dev, "sunxi dmic ioremap failed\n"); ret = -ENOMEM; goto err_devm_free_memregion; } sunxi_dmic->regmap = devm_regmap_init_mmio(&pdev->dev, sunxi_dmic->membase, &sunxi_dmic_regmap_config); if (IS_ERR_OR_NULL(sunxi_dmic->regmap)) { dev_err(&pdev->dev, "sunxi dmic registers regmap failed\n"); ret = -ENOMEM; goto err_iounmap; } sunxi_dmic->pinctrl = NULL; if (!sunxi_dmic->pinctrl) { sunxi_dmic->pinctrl = devm_pinctrl_get(&pdev->dev); if (IS_ERR_OR_NULL(sunxi_dmic->pinctrl)) { dev_err(&pdev->dev, "request pinctrl handle for audio failed\n"); ret = -EINVAL; goto err_iounmap; } } if (!sunxi_dmic->pinstate) { sunxi_dmic->pinstate = pinctrl_lookup_state(sunxi_dmic->pinctrl, PINCTRL_STATE_DEFAULT); if (IS_ERR_OR_NULL(sunxi_dmic->pinstate)) { dev_err(&pdev->dev, "lookup pin default state failed\n"); ret = -EINVAL; goto err_pinctrl_put; } } if (!sunxi_dmic->pinstate_sleep) { sunxi_dmic->pinstate_sleep = pinctrl_lookup_state(sunxi_dmic->pinctrl, PINCTRL_STATE_SLEEP); if (IS_ERR_OR_NULL(sunxi_dmic->pinstate_sleep)) { dev_err(&pdev->dev, "lookup pin sleep state failed\n"); ret = -EINVAL; goto err_pinctrl_put; } } /*only until this step , can the pinctrl system find pin conlict*/ ret = pinctrl_select_state(sunxi_dmic->pinctrl, sunxi_dmic->pinstate); if (ret) { dev_err(&pdev->dev, "pin select state failed\n"); goto err_pinctrl_put; } sunxi_dmic->pllclk = of_clk_get(np, 0); if (IS_ERR(sunxi_dmic->pllclk)) { dev_err(&pdev->dev, "Can't get dmic pll clocks\n"); ret = PTR_ERR(sunxi_dmic->pllclk); goto err_pinctrl_put; } #ifdef DMIC_PLL_AUDIO_X4 sunxi_dmic->pllclkx4 = of_clk_get(np, 1); if (IS_ERR(sunxi_dmic->pllclkx4)) { dev_err(&pdev->dev, "Can't get pll_audo clk clocks!\n"); ret = PTR_ERR(sunxi_dmic->pllclkx4); goto err_pllclk_put; } sunxi_dmic->moduleclk = of_clk_get(np, 2); if (IS_ERR(sunxi_dmic->moduleclk)) { dev_err(&pdev->dev, "Can't get spdif clocks\n"); ret = PTR_ERR(sunxi_dmic->moduleclk); goto err_pllclkx4_put; } else { if (clk_set_parent(sunxi_dmic->moduleclk, sunxi_dmic->pllclkx4)) { dev_err(&pdev->dev, "set parent of moduleclk to pllclkx4 failed!\n"); ret = -EINVAL; goto err_moduleclk_put; } } #else sunxi_dmic->moduleclk = of_clk_get(np, 1); if (IS_ERR(sunxi_dmic->moduleclk)) { ret = PTR_ERR(sunxi_dmic->moduleclk); goto err_pllclk_put; } else { if (clk_set_parent(sunxi_dmic->moduleclk, sunxi_dmic->pllclk)) { dev_err(&pdev->dev, "Can't set moduleclk parent clocks\n"); goto err_moduleclk_put; } } #endif if (clk_prepare_enable(sunxi_dmic->pllclk)) { dev_err(&pdev->dev, "pllclk enable failed\n"); ret = -EBUSY; goto err_moduleclk_put; } #ifdef DMIC_PLL_AUDIO_X4 if (clk_prepare_enable(sunxi_dmic->pllclkx4)) { dev_err(&pdev->dev, "pllclkx4 enable failed\n"); ret = -EBUSY; goto err_pllclk_disable; } #endif #if defined(ARCH_SUN50IW3) || defined(ARCH_SUN8IW15) || defined(ARCH_SUN8IW17) /* * The moduleclk should enable at startup * and disable at shutdown */ sunxi_dmic->moduleclk_en = 0; #else if (clk_prepare_enable(sunxi_dmic->moduleclk)) { dev_err(&pdev->dev, "moduleclk enable failed\n"); ret = -EBUSY; #ifdef DMIC_PLL_AUDIO_X4 goto err_pllclkx4_disable; #else goto err_pllclk_disable; #endif } sunxi_dmic->moduleclk_en = 1; #endif /* FIXME */ sunxi_dmic->capture_dma_param.dma_addr = res.start + SUNXI_DMIC_DATA; sunxi_dmic->capture_dma_param.dma_drq_type_num = DRQSRC_DMIC; sunxi_dmic->capture_dma_param.src_maxburst = 8; sunxi_dmic->capture_dma_param.dst_maxburst = 8; ret = snd_soc_register_component(&pdev->dev, &sunxi_dmic_component, &sunxi_dmic->dai, 1); if (ret) { dev_err(&pdev->dev, "Could not register DAI: %d\n", ret); ret = -ENOMEM; goto err_moduleclk_disable; } ret = asoc_dma_platform_register(&pdev->dev, 0); if (ret) { dev_err(&pdev->dev, "Could not register PCM: %d\n", ret); ret = -ENOMEM; goto err_unregister_component; } #ifdef SUNXI_DMIC_DEBUG ret = class_register(&dmic_class); if (ret) pr_warn("daudio: Failed to create debugfs directory\n"); #endif return 0; err_unregister_component: snd_soc_unregister_component(&pdev->dev); err_moduleclk_disable: #if defined(ARCH_SUN50IW3) || defined(ARCH_SUN8IW15) || defined(ARCH_SUN8IW17) /* do nothing */ #else clk_disable_unprepare(sunxi_dmic->moduleclk); #endif #ifdef DMIC_PLL_AUDIO_X4 err_pllclkx4_disable: clk_disable_unprepare(sunxi_dmic->pllclkx4); #endif err_pllclk_disable: clk_disable_unprepare(sunxi_dmic->pllclk); err_moduleclk_put: clk_put(sunxi_dmic->moduleclk); #ifdef DMIC_PLL_AUDIO_X4 err_pllclkx4_put: clk_put(sunxi_dmic->pllclkx4); #endif err_pllclk_put: clk_put(sunxi_dmic->pllclk); err_pinctrl_put: devm_pinctrl_put(sunxi_dmic->pinctrl); err_iounmap: iounmap(sunxi_dmic->membase); err_devm_free_memregion: devm_release_mem_region(&pdev->dev, sunxi_dmic->memregion->start, resource_size(sunxi_dmic->memregion)); err_regulator_put: #ifdef DMIC_AUDIO_DEMAND regulator_put(sunxi_dmic->power_supply); err_devm_kfree: #endif devm_kfree(&pdev->dev, sunxi_dmic); err_node_put: of_node_put(np); return ret; } static int __exit sunxi_dmic_dev_remove(struct platform_device *pdev) { struct sunxi_dmic_info *sunxi_dmic = dev_get_drvdata(&pdev->dev); #ifdef SUNXI_DMIC_DEBUG class_unregister(&dmic_class); #endif #if defined(ARCH_SUN50IW3) || defined(ARCH_SUN8IW15) || defined(ARCH_SUN8IW17) /* do nothing */ #else clk_disable_unprepare(sunxi_dmic->moduleclk); #endif clk_put(sunxi_dmic->moduleclk); #ifdef DMIC_PLL_AUDIO_X4 clk_disable_unprepare(sunxi_dmic->pllclkx4); clk_put(sunxi_dmic->pllclkx4); #endif clk_disable_unprepare(sunxi_dmic->pllclk); clk_put(sunxi_dmic->pllclk); if (sunxi_dmic->pinctrl != NULL) devm_pinctrl_put(sunxi_dmic->pinctrl); sunxi_dmic->pinctrl = NULL; sunxi_dmic->pinstate = NULL; sunxi_dmic->pinstate_sleep = NULL; snd_soc_unregister_component(&pdev->dev); devm_release_mem_region(&pdev->dev, sunxi_dmic->memregion->start, resource_size(sunxi_dmic->memregion)); platform_set_drvdata(pdev, NULL); return 0; } static const struct of_device_id sunxi_dmic_of_match[] = { { .compatible = "allwinner,sunxi-dmic", }, { }, }; MODULE_DEVICE_TABLE(of, sunxi_dmic_of_match); static struct platform_driver sunxi_dmic_driver = { .probe = sunxi_dmic_dev_probe, .remove = __exit_p(sunxi_dmic_dev_remove), .driver = { .name = DRV_NAME, .owner = THIS_MODULE, .of_match_table = sunxi_dmic_of_match, }, }; module_platform_driver(sunxi_dmic_driver); MODULE_AUTHOR("wolfgang huang "); MODULE_DESCRIPTION("SUNXI DMIC Machine ASoC driver"); MODULE_ALIAS("platform:sunxi-dmic"); MODULE_LICENSE("GPL");