/* * omap3h1.c -- SoC audio for OMAP3 H1 * * Author: Evan Wilson * * Adapted from omap3pandora * Author: Steve Sakoman * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #define DEBUG #include #include #include #include #include #include #include #include #include #include #include #include "omap-mcbsp.h" static struct clk *per_96m_fck; static unsigned long rate; static int mic_gpio_enable; static int omap3h1_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; //unsigned int fmt; int ret = 0; unsigned int freq; //freq = 256 * params_rate(params); // We are triggering from the 96 MHz FSCK // pr_info("ASoc OMAP3H1: setting system clock to: %d", freq); ret = snd_soc_dai_set_sysclk(cpu_dai, OMAP_MCBSP_SYSCLK_CLKS_FCLK, rate, SND_SOC_CLOCK_OUT); if (ret < 0) { printk(KERN_ERR "can't set DMIC cpu system clock\n"); return ret; } // Set the divider so that our clock rate is ~3KHz, this is about // 48KHz sampling frequency on the mic (32-bit/channel, 2 channels, 48 // Khz sampling) ret = snd_soc_dai_set_clkdiv(cpu_dai, OMAP_MCBSP_CLKGDV, 32); if (ret < 0) { pr_err("ASoc OMAP3H1: can't set SRG clock divider\n"); return ret; } return 0; } static int omap3h1_startup(struct snd_pcm_substream *substream) { gpio_set_value(mic_gpio_enable, 1); return clk_enable(per_96m_fck); } static void omap3h1_shutdown(struct snd_pcm_substream *substream) { gpio_set_value(mic_gpio_enable, 0); clk_disable(per_96m_fck); } static struct snd_soc_ops omap3h1_ops = { .hw_params = omap3h1_hw_params, .startup = omap3h1_startup, .shutdown = omap3h1_shutdown, }; //static const struct snd_soc_dapm_widget dmic_dapm_widgets[] = { // SND_SOC_DAPM_MIC("Digital Mic", NULL), //}; // //static const struct snd_soc_dapm_route dmic_audio_map[] = { // {"DMic", NULL, "Digital Mic"}, //}; /* Digital audio interface glue - connects codec <--> CPU */ static struct snd_soc_dai_link omap3h1_dai = { .name = "DMIC", .stream_name = "DMIC Capture", .cpu_dai_name = "omap-mcbsp.3", .codec_dai_name = "dmic-hifi", .platform_name = "omap-pcm-audio", .codec_name = "dmic-codec", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS, .ops = &omap3h1_ops, }; /* Audio machine driver */ static struct snd_soc_card snd_soc_omap3h1 = { .name = "omap3h1", .owner = THIS_MODULE, .dai_link = &omap3h1_dai, .num_links = 1, // .dapm_widgets = dmic_dapm_widgets, // .num_dapm_widgets = ARRAY_SIZE(dmic_dapm_widgets), // .dapm_routes = dmic_audio_map, // .num_dapm_routes = ARRAY_SIZE(dmic_audio_map), }; static int omap3h1_card_probe(struct platform_device *pdev) { struct device_node *node = pdev->dev.of_node; struct snd_soc_card *card = &snd_soc_omap3h1; int ret = 0; int gpio; #ifdef CONFIG_OF if (node) { struct device_node *dai_node; dai_node = of_parse_phandle(node, "olio,mcbsp", 0); if (!dai_node) { dev_err(&pdev->dev, "mcbsp node is not provided\n"); return -EINVAL; } omap3h1_dai.cpu_dai_name = NULL; omap3h1_dai.cpu_of_node = dai_node; } else { dev_err(&pdev->dev, "Missing node\n"); return -ENODEV; } gpio = of_get_named_gpio(node, "olio,mic_enable", 0); ret = (gpio < 0) ? -ENODEV : gpio_request(gpio, "mic_enable"); if (ret) { dev_err(&pdev->dev, "GPIO request error gpio=%d err=%d\n", gpio, ret); return ret; } gpio_direction_output(gpio, 0); mic_gpio_enable = gpio; #endif card->dev = &pdev->dev; snd_soc_card_set_drvdata(card, NULL); ret = snd_soc_register_card(card); if (ret) { dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", ret); return ret; } per_96m_fck = clk_get(&pdev->dev, "per_96m_fck"); if (IS_ERR(per_96m_fck)) { dev_err(&pdev->dev, "could not get per_96m_fck clock\n"); return PTR_ERR(per_96m_fck); } rate = clk_get_rate(per_96m_fck); return 0; } static int omap3h1_card_remove(struct platform_device *pdev) { struct snd_soc_card *card = platform_get_drvdata(pdev); gpio_free(mic_gpio_enable); snd_soc_unregister_card(card); return 0; } /** * omap3h1_sound_suspend - suspend microphone * */ static int omap3h1_sound_suspend(struct device *dev) { printk ("OLIO %s:%s Suspending\n", __FILE__, __FUNCTION__); snd_soc_suspend(dev); return 0; } /** * omap3h1_sound_resume - resume microphone * */ static int omap3h1_sound_resume(struct device *dev) { printk ("OLIO %s:%s Resuming\n", __FILE__, __FUNCTION__); snd_soc_resume (dev); return 0; } /* ---------------------------------------------------------------------- */ static const struct dev_pm_ops omap3h1_sound_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(omap3h1_sound_suspend, omap3h1_sound_resume) }; static const struct of_device_id omap_soc_h1_of_match[] = { {.compatible = "olio,omap-soc-omap3h1", }, { }, }; MODULE_DEVICE_TABLE(of, omap_soc_h1_of_match); static struct platform_driver omap3h1_driver = { .driver = { .name = "omap-soc-omap3h1", .of_match_table = of_match_ptr(omap_soc_h1_of_match), .pm = &snd_soc_pm_ops, }, .probe = omap3h1_card_probe, .remove = omap3h1_card_remove, }; module_platform_driver(omap3h1_driver); MODULE_AUTHOR("Evan Wilson