/* * sound\soc\sunxi\sunxi-dmic.c * (C) Copyright 2014-2016 * AllWinner Technology Co., Ltd. * wolfgang huang * huangxin * * 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" #ifdef CONFIG_SND_SUNXI_MAD #include "sunxi-mad.h" #endif #define DRV_NAME "sunxi-dmic" #if defined(CONFIG_ARCH_SUN50IW6) || defined(CONFIG_ARCH_SUN8IW15) || \ defined(CONFIG_ARCH_SUN50IW8) #undef DMIC_AUDIO_DEMAND #else #define DMIC_AUDIO_DEMAND #endif #define SUNXI_DMIC_DEBUG 1 struct dmic_rate { unsigned int samplerate; unsigned int rate_bit; }; struct dmic_chmap { unsigned int chan; unsigned int chan_bit; }; struct sunxi_dmic_info { struct regmap *regmap; struct regulator *power_supply; struct clk *pllclk; struct clk *moduleclk; struct clk *dmic_rst; struct device *dev; struct snd_soc_dai_driver dai; struct sunxi_dma_params capture_dma_param; struct pinctrl *pinctrl; struct pinctrl_state *pinstate; struct pinctrl_state *pinstate_sleep; #ifdef CONFIG_SND_SUNXI_MAD struct resource res; unsigned int mad_bind; unsigned int lpsd_chan_sel; unsigned int mad_standby_chan_sel; unsigned int audio_src_chan_num; #endif u32 chanmap; }; static void __iomem *sunxi_dmic_membase; 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 label reg_labels[] = { LABEL(SUNXI_DMIC_EN), LABEL(SUNXI_DMIC_SR), LABEL(SUNXI_DMIC_CTR), LABEL(SUNXI_DMIC_INTC), LABEL(SUNXI_DMIC_INTS), LABEL(SUNXI_DMIC_FIFO_CTR), LABEL(SUNXI_DMIC_FIFO_STA), LABEL(SUNXI_DMIC_CH_NUM), LABEL(SUNXI_DMIC_CH_MAP), LABEL(SUNXI_DMIC_CNT), LABEL(SUNXI_DMIC_DATA0_1_VOL), LABEL(SUNXI_DMIC_DATA2_3_VOL), LABEL(SUNXI_DMIC_HPF_CTRL), LABEL(SUNXI_DMIC_HPF_COEF), LABEL(SUNXI_DMIC_HPF_GAIN), LABEL_END, }; #if 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; } static struct class_attribute dmic_class_attrs[] = { __ATTR(dmic_reg_debug, S_IRUGO|S_IWUSR, dmic_class_debug_show, dmic_class_debug_store), __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, int enable) { if (enable) { regmap_update_bits(sunxi_dmic->regmap, SUNXI_DMIC_INTC, (1<regmap, SUNXI_DMIC_EN, (0xFF<chanmap)<regmap, SUNXI_DMIC_EN, (1<regmap, SUNXI_DMIC_EN, (1<regmap, SUNXI_DMIC_EN, (0xFF<regmap, SUNXI_DMIC_INTC, (1<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_mad_priv *sunxi_dmic_priv = snd_soc_card_get_drvdata(card); sunxi_dmic->mad_bind = sunxi_dmic_priv->mad_bind; #endif 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, (3<regmap, SUNXI_DMIC_FIFO_CTR, (3<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)) { printk("config mad !\n"); sunxi_mad_init(); sunxi_mad_audio_source_sel(3, 1); /*path_sel: dmic-3*/ sunxi_mad_hw_params(params_channels(params), params_rate(params)); sunxi_dmic->audio_src_chan_num = params_channels(params); /*enable mad*/ sunxi_sram_dma_config(&sunxi_dmic->capture_dma_param); /*enable dmic*/ //regmap_update_bits(sunxi_dmic->regmap, SUNXI_DMIC_EN, // (1<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, 1); #ifdef CONFIG_SND_SUNXI_MAD /*enable dmic*/ if (sunxi_dmic->mad_bind == 1) { regmap_update_bits(sunxi_dmic->regmap, SUNXI_DMIC_EN, (1<mad_bind == 1) { mad_dma_en(0); } #endif 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 (clk_set_rate(sunxi_dmic->pllclk, freq)) { dev_err(sunxi_dmic->dev, "Freq : %u not support\n", freq); return -EINVAL; } if (sunxi_dmic->moduleclk) clk_prepare_enable(sunxi_dmic->moduleclk); 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); #ifdef CONFIG_SND_SUNXI_MAD if (sunxi_dmic->mad_bind == 1) { sunxi_mad_suspend_external(sunxi_dmic->lpsd_chan_sel, sunxi_dmic->mad_standby_chan_sel); pr_debug("mad suspend succeed %s\n", __func__); return 0; } #endif pr_debug("[DMIC]Enter %s\n", __func__); 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->pinctrl != NULL) devm_pinctrl_put(sunxi_dmic->pinctrl); sunxi_dmic->pinctrl = NULL; sunxi_dmic->pinstate = NULL; sunxi_dmic->pinstate_sleep = NULL; #if 0 if (sunxi_dmic->moduleclk != NULL) clk_disable(sunxi_dmic->moduleclk); #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); #ifdef CONFIG_SND_SUNXI_MAD if (sunxi_dmic->mad_bind == 1) { sunxi_mad_resume_external(sunxi_dmic->mad_standby_chan_sel, sunxi_dmic->audio_src_chan_num); pr_debug("mad resume succeed %s\n", __func__); return 0; } #endif pr_debug("[DMIC]Enter %s\n", __func__); if (sunxi_dmic->pllclk != NULL) { if (clk_prepare_enable(sunxi_dmic->pllclk)) { pr_err("enable sunxi_dmic->pllclk failed!\n"); } } #if 0 if (sunxi_dmic->moduleclk != NULL) { if (clk_prepare_enable(sunxi_dmic->moduleclk)) { pr_err("[dmic] enable sunxi_dmic->moduleclk failed!\n"); } } #endif if (!sunxi_dmic->pinctrl) { sunxi_dmic->pinctrl = devm_pinctrl_get(cpu_dai->dev); if (IS_ERR_OR_NULL(sunxi_dmic->pinctrl)) { pr_warn("[dmic]request pinctrl handle for audio failed\n"); return -EINVAL; } } if (!sunxi_dmic->pinstate) { sunxi_dmic->pinstate = pinctrl_lookup_state(sunxi_dmic->pinctrl, PINCTRL_STATE_DEFAULT); if (IS_ERR_OR_NULL(sunxi_dmic->pinstate)) { pr_warn("[dmic]lookup pin default state failed\n"); return -EINVAL; } } 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)) { pr_warn("[dmic]lookup pin sleep state failed\n"); return -EINVAL; } } ret = pinctrl_select_state(sunxi_dmic->pinctrl, sunxi_dmic->pinstate); if (ret) { pr_warn("[dmic]select pin default state failed\n"); return ret; } #if 0 sunxi_dmic_init(sunxi_dmic); #endif 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, (1<capture_dma_param.dma_addr = sunxi_dmic->res.start + SUNXI_DMIC_DATA; sunxi_dmic->capture_dma_param.dma_drq_type_num = DRQSRC_DMIC; } #endif /* IC:Gating and reset should be disabled after shutdown */ if (sunxi_dmic->moduleclk) clk_disable(sunxi_dmic->moduleclk); } #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]; 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 *lpsd_chan_sel_function[] = {"NULL", "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]; 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 *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, *memregion; struct device_node *np = pdev->dev.of_node; struct sunxi_dmic_info *sunxi_dmic; 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 = 2; sunxi_dmic->mad_standby_chan_sel = 2; sunxi_dmic->res.start = res.start; #endif memregion = devm_request_mem_region(&pdev->dev, res.start, resource_size(&res), DRV_NAME); if (!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_regulator_put; } 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); sunxi_dmic->moduleclk = of_clk_get(np, 1); if (IS_ERR(sunxi_dmic->pllclk) || IS_ERR(sunxi_dmic->moduleclk)) { dev_err(&pdev->dev, "Can't get dmic pll clocks\n"); if (IS_ERR(sunxi_dmic->pllclk)) { ret = PTR_ERR(sunxi_dmic->pllclk); goto err_pinctrl_put; } else { ret = PTR_ERR(sunxi_dmic->moduleclk); goto err_pllclk_put; } } else { if (clk_set_parent(sunxi_dmic->moduleclk, sunxi_dmic->pllclk)) pr_err("try to set moduleclk parent failed!\n"); clk_prepare_enable(sunxi_dmic->pllclk); /* * The moduleclk should enable at startup * and disable at shutdown */ /*clk_prepare_enable(sunxi_dmic->moduleclk);*/ } #if 0 /* * only for dmic reset test. * (clk driver should support it and the dts) * It need to enable at start_up, and disable at shutdown. */ sunxi_dmic->dmic_rst = of_clk_get(np, 2); if (IS_ERR(sunxi_dmic->dmic_rst)) { dev_err(&pdev->dev, "Can't get dmic rst\n"); ret = PTR_ERR(sunxi_dmic->dmic_rst); goto err_pllclk_put; } else { /*clk_prepare_enable(sunxi_dmic->dmic_rst);*/ } #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_pllclk_put; } 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; } #if 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); #if 0 err_moduleclk_put: clk_put(sunxi_dmic->moduleclk); #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_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) { snd_soc_unregister_component(&pdev->dev); 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");