Path: blob/master/sound/soc/samsung/s3c24xx_simtec.c
10817 views
/* sound/soc/samsung/s3c24xx_simtec.c1*2* Copyright 2009 Simtec Electronics3*4* This program is free software; you can redistribute it and/or modify5* it under the terms of the GNU General Public License version 2 as6* published by the Free Software Foundation.7*/89#include <linux/gpio.h>10#include <linux/clk.h>1112#include <sound/soc.h>1314#include <plat/audio-simtec.h>1516#include "s3c24xx-i2s.h"17#include "s3c24xx_simtec.h"1819static struct s3c24xx_audio_simtec_pdata *pdata;20static struct clk *xtal_clk;2122static int spk_gain;23static int spk_unmute;2425/**26* speaker_gain_get - read the speaker gain setting.27* @kcontrol: The control for the speaker gain.28* @ucontrol: The value that needs to be updated.29*30* Read the value for the AMP gain control.31*/32static int speaker_gain_get(struct snd_kcontrol *kcontrol,33struct snd_ctl_elem_value *ucontrol)34{35ucontrol->value.integer.value[0] = spk_gain;36return 0;37}3839/**40* speaker_gain_set - set the value of the speaker amp gain41* @value: The value to write.42*/43static void speaker_gain_set(int value)44{45gpio_set_value_cansleep(pdata->amp_gain[0], value & 1);46gpio_set_value_cansleep(pdata->amp_gain[1], value >> 1);47}4849/**50* speaker_gain_put - set the speaker gain setting.51* @kcontrol: The control for the speaker gain.52* @ucontrol: The value that needs to be set.53*54* Set the value of the speaker gain from the specified55* @ucontrol setting.56*57* Note, if the speaker amp is muted, then we do not set a gain value58* as at-least one of the ICs that is fitted will try and power up even59* if the main control is set to off.60*/61static int speaker_gain_put(struct snd_kcontrol *kcontrol,62struct snd_ctl_elem_value *ucontrol)63{64int value = ucontrol->value.integer.value[0];6566spk_gain = value;6768if (!spk_unmute)69speaker_gain_set(value);7071return 0;72}7374static const struct snd_kcontrol_new amp_gain_controls[] = {75SOC_SINGLE_EXT("Speaker Gain", 0, 0, 3, 0,76speaker_gain_get, speaker_gain_put),77};7879/**80* spk_unmute_state - set the unmute state of the speaker81* @to: zero to unmute, non-zero to ununmute.82*/83static void spk_unmute_state(int to)84{85pr_debug("%s: to=%d\n", __func__, to);8687spk_unmute = to;88gpio_set_value(pdata->amp_gpio, to);8990/* if we're umuting, also re-set the gain */91if (to && pdata->amp_gain[0] > 0)92speaker_gain_set(spk_gain);93}9495/**96* speaker_unmute_get - read the speaker unmute setting.97* @kcontrol: The control for the speaker gain.98* @ucontrol: The value that needs to be updated.99*100* Read the value for the AMP gain control.101*/102static int speaker_unmute_get(struct snd_kcontrol *kcontrol,103struct snd_ctl_elem_value *ucontrol)104{105ucontrol->value.integer.value[0] = spk_unmute;106return 0;107}108109/**110* speaker_unmute_put - set the speaker unmute setting.111* @kcontrol: The control for the speaker gain.112* @ucontrol: The value that needs to be set.113*114* Set the value of the speaker gain from the specified115* @ucontrol setting.116*/117static int speaker_unmute_put(struct snd_kcontrol *kcontrol,118struct snd_ctl_elem_value *ucontrol)119{120spk_unmute_state(ucontrol->value.integer.value[0]);121return 0;122}123124/* This is added as a manual control as the speaker amps create clicks125* when their power state is changed, which are far more noticeable than126* anything produced by the CODEC itself.127*/128static const struct snd_kcontrol_new amp_unmute_controls[] = {129SOC_SINGLE_EXT("Speaker Switch", 0, 0, 1, 0,130speaker_unmute_get, speaker_unmute_put),131};132133void simtec_audio_init(struct snd_soc_pcm_runtime *rtd)134{135struct snd_soc_codec *codec = rtd->codec;136137if (pdata->amp_gpio > 0) {138pr_debug("%s: adding amp routes\n", __func__);139140snd_soc_add_controls(codec, amp_unmute_controls,141ARRAY_SIZE(amp_unmute_controls));142}143144if (pdata->amp_gain[0] > 0) {145pr_debug("%s: adding amp controls\n", __func__);146snd_soc_add_controls(codec, amp_gain_controls,147ARRAY_SIZE(amp_gain_controls));148}149}150EXPORT_SYMBOL_GPL(simtec_audio_init);151152#define CODEC_CLOCK 12000000153154/**155* simtec_hw_params - update hardware parameters156* @substream: The audio substream instance.157* @params: The parameters requested.158*159* Update the codec data routing and configuration settings160* from the supplied data.161*/162static int simtec_hw_params(struct snd_pcm_substream *substream,163struct snd_pcm_hw_params *params)164{165struct snd_soc_pcm_runtime *rtd = substream->private_data;166struct snd_soc_dai *codec_dai = rtd->codec_dai;167struct snd_soc_dai *cpu_dai = rtd->cpu_dai;168int ret;169170/* Set the CODEC as the bus clock master, I2S */171ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |172SND_SOC_DAIFMT_NB_NF |173SND_SOC_DAIFMT_CBM_CFM);174if (ret) {175pr_err("%s: failed set cpu dai format\n", __func__);176return ret;177}178179/* Set the CODEC as the bus clock master */180ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |181SND_SOC_DAIFMT_NB_NF |182SND_SOC_DAIFMT_CBM_CFM);183if (ret) {184pr_err("%s: failed set codec dai format\n", __func__);185return ret;186}187188ret = snd_soc_dai_set_sysclk(codec_dai, 0,189CODEC_CLOCK, SND_SOC_CLOCK_IN);190if (ret) {191pr_err( "%s: failed setting codec sysclk\n", __func__);192return ret;193}194195if (pdata->use_mpllin) {196ret = snd_soc_dai_set_sysclk(cpu_dai, S3C24XX_CLKSRC_MPLL,1970, SND_SOC_CLOCK_OUT);198199if (ret) {200pr_err("%s: failed to set MPLLin as clksrc\n",201__func__);202return ret;203}204}205206if (pdata->output_cdclk) {207int cdclk_scale;208209cdclk_scale = clk_get_rate(xtal_clk) / CODEC_CLOCK;210cdclk_scale--;211212ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,213cdclk_scale);214}215216return 0;217}218219static int simtec_call_startup(struct s3c24xx_audio_simtec_pdata *pd)220{221/* call any board supplied startup code, this currently only222* covers the bast/vr1000 which have a CPLD in the way of the223* LRCLK */224if (pd->startup)225pd->startup();226227return 0;228}229230static struct snd_soc_ops simtec_snd_ops = {231.hw_params = simtec_hw_params,232};233234/**235* attach_gpio_amp - get and configure the necessary gpios236* @dev: The device we're probing.237* @pd: The platform data supplied by the board.238*239* If there is a GPIO based amplifier attached to the board, claim240* the necessary GPIO lines for it, and set default values.241*/242static int attach_gpio_amp(struct device *dev,243struct s3c24xx_audio_simtec_pdata *pd)244{245int ret;246247/* attach gpio amp gain (if any) */248if (pdata->amp_gain[0] > 0) {249ret = gpio_request(pd->amp_gain[0], "gpio-amp-gain0");250if (ret) {251dev_err(dev, "cannot get amp gpio gain0\n");252return ret;253}254255ret = gpio_request(pd->amp_gain[1], "gpio-amp-gain1");256if (ret) {257dev_err(dev, "cannot get amp gpio gain1\n");258gpio_free(pdata->amp_gain[0]);259return ret;260}261262gpio_direction_output(pd->amp_gain[0], 0);263gpio_direction_output(pd->amp_gain[1], 0);264}265266/* note, currently we assume GPA0 isn't valid amp */267if (pdata->amp_gpio > 0) {268ret = gpio_request(pd->amp_gpio, "gpio-amp");269if (ret) {270dev_err(dev, "cannot get amp gpio %d (%d)\n",271pd->amp_gpio, ret);272goto err_amp;273}274275/* set the amp off at startup */276spk_unmute_state(0);277}278279return 0;280281err_amp:282if (pd->amp_gain[0] > 0) {283gpio_free(pd->amp_gain[0]);284gpio_free(pd->amp_gain[1]);285}286287return ret;288}289290static void detach_gpio_amp(struct s3c24xx_audio_simtec_pdata *pd)291{292if (pd->amp_gain[0] > 0) {293gpio_free(pd->amp_gain[0]);294gpio_free(pd->amp_gain[1]);295}296297if (pd->amp_gpio > 0)298gpio_free(pd->amp_gpio);299}300301#ifdef CONFIG_PM302int simtec_audio_resume(struct device *dev)303{304simtec_call_startup(pdata);305return 0;306}307308const struct dev_pm_ops simtec_audio_pmops = {309.resume = simtec_audio_resume,310};311EXPORT_SYMBOL_GPL(simtec_audio_pmops);312#endif313314int __devinit simtec_audio_core_probe(struct platform_device *pdev,315struct snd_soc_card *card)316{317struct platform_device *snd_dev;318int ret;319320card->dai_link->ops = &simtec_snd_ops;321322pdata = pdev->dev.platform_data;323if (!pdata) {324dev_err(&pdev->dev, "no platform data supplied\n");325return -EINVAL;326}327328simtec_call_startup(pdata);329330xtal_clk = clk_get(&pdev->dev, "xtal");331if (IS_ERR(xtal_clk)) {332dev_err(&pdev->dev, "could not get clkout0\n");333return -EINVAL;334}335336dev_info(&pdev->dev, "xtal rate is %ld\n", clk_get_rate(xtal_clk));337338ret = attach_gpio_amp(&pdev->dev, pdata);339if (ret)340goto err_clk;341342snd_dev = platform_device_alloc("soc-audio", -1);343if (!snd_dev) {344dev_err(&pdev->dev, "failed to alloc soc-audio devicec\n");345ret = -ENOMEM;346goto err_gpio;347}348349platform_set_drvdata(snd_dev, card);350351ret = platform_device_add(snd_dev);352if (ret) {353dev_err(&pdev->dev, "failed to add soc-audio dev\n");354goto err_pdev;355}356357platform_set_drvdata(pdev, snd_dev);358return 0;359360err_pdev:361platform_device_put(snd_dev);362363err_gpio:364detach_gpio_amp(pdata);365366err_clk:367clk_put(xtal_clk);368return ret;369}370EXPORT_SYMBOL_GPL(simtec_audio_core_probe);371372int __devexit simtec_audio_remove(struct platform_device *pdev)373{374struct platform_device *snd_dev = platform_get_drvdata(pdev);375376platform_device_unregister(snd_dev);377378detach_gpio_amp(pdata);379clk_put(xtal_clk);380return 0;381}382EXPORT_SYMBOL_GPL(simtec_audio_remove);383384MODULE_AUTHOR("Ben Dooks <[email protected]>");385MODULE_DESCRIPTION("ALSA SoC Simtec Audio common support");386MODULE_LICENSE("GPL");387388389