kernel-ark/sound/soc/pxa/palm27x.c
Mark Brown 875065491f ASoC: Rename snd_soc_card to snd_soc_machine
One of the issues with the ASoC v1 API which has been addressed in the
ASoC v2 work that Liam Girdwood has done is that the ALSA card provided
by ASoC is distributed around the ASoC structures. For example, machine
wide data such as the struct snd_card are maintained as part of the
CODEC data structure, preventing the use of multiple codecs. This has
been addressed by refactoring the data structures so that all the data
for the ALSA card is contained in a single structure snd_soc_card which
replaces the existing snd_soc_machine and snd_soc_device.

Begin the process of backporting this by renaming struct snd_soc_machine
to struct snd_soc_card, better reflecting its function and bringing it
closer to standard ALSA terminology.

Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
2008-11-21 14:02:01 +00:00

270 lines
6.5 KiB
C

/*
* linux/sound/soc/pxa/palm27x.c
*
* SoC Audio driver for Palm T|X, T5 and LifeDrive
*
* based on tosa.c
*
* Copyright (C) 2008 Marek Vasut <marek.vasut@gmail.com>
*
* 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.
*
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/device.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <asm/mach-types.h>
#include <mach/audio.h>
#include <mach/palmasoc.h>
#include "../codecs/wm9712.h"
#include "pxa2xx-pcm.h"
#include "pxa2xx-ac97.h"
static int palm27x_jack_func = 1;
static int palm27x_spk_func = 1;
static int palm27x_ep_gpio = -1;
static void palm27x_ext_control(struct snd_soc_codec *codec)
{
if (!palm27x_spk_func)
snd_soc_dapm_enable_pin(codec, "Speaker");
else
snd_soc_dapm_disable_pin(codec, "Speaker");
if (!palm27x_jack_func)
snd_soc_dapm_enable_pin(codec, "Headphone Jack");
else
snd_soc_dapm_disable_pin(codec, "Headphone Jack");
snd_soc_dapm_sync(codec);
}
static int palm27x_startup(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_codec *codec = rtd->socdev->codec;
/* check the jack status at stream startup */
palm27x_ext_control(codec);
return 0;
}
static struct snd_soc_ops palm27x_ops = {
.startup = palm27x_startup,
};
static irqreturn_t palm27x_interrupt(int irq, void *v)
{
palm27x_spk_func = gpio_get_value(palm27x_ep_gpio);
palm27x_jack_func = !palm27x_spk_func;
return IRQ_HANDLED;
}
static int palm27x_get_jack(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = palm27x_jack_func;
return 0;
}
static int palm27x_set_jack(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
if (palm27x_jack_func == ucontrol->value.integer.value[0])
return 0;
palm27x_jack_func = ucontrol->value.integer.value[0];
palm27x_ext_control(codec);
return 1;
}
static int palm27x_get_spk(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = palm27x_spk_func;
return 0;
}
static int palm27x_set_spk(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
if (palm27x_spk_func == ucontrol->value.integer.value[0])
return 0;
palm27x_spk_func = ucontrol->value.integer.value[0];
palm27x_ext_control(codec);
return 1;
}
/* PalmTX machine dapm widgets */
static const struct snd_soc_dapm_widget palm27x_dapm_widgets[] = {
SND_SOC_DAPM_HP("Headphone Jack", NULL),
SND_SOC_DAPM_SPK("Speaker", NULL),
};
/* PalmTX audio map */
static const struct snd_soc_dapm_route audio_map[] = {
/* headphone connected to HPOUTL, HPOUTR */
{"Headphone Jack", NULL, "HPOUTL"},
{"Headphone Jack", NULL, "HPOUTR"},
/* ext speaker connected to ROUT2, LOUT2 */
{"Speaker", NULL, "LOUT2"},
{"Speaker", NULL, "ROUT2"},
};
static const char *jack_function[] = {"Headphone", "Off"};
static const char *spk_function[] = {"On", "Off"};
static const struct soc_enum palm27x_enum[] = {
SOC_ENUM_SINGLE_EXT(2, jack_function),
SOC_ENUM_SINGLE_EXT(2, spk_function),
};
static const struct snd_kcontrol_new palm27x_controls[] = {
SOC_ENUM_EXT("Jack Function", palm27x_enum[0], palm27x_get_jack,
palm27x_set_jack),
SOC_ENUM_EXT("Speaker Function", palm27x_enum[1], palm27x_get_spk,
palm27x_set_spk),
};
static int palm27x_ac97_init(struct snd_soc_codec *codec)
{
int i, err;
snd_soc_dapm_nc_pin(codec, "OUT3");
snd_soc_dapm_nc_pin(codec, "MONOOUT");
/* add palm27x specific controls */
for (i = 0; i < ARRAY_SIZE(palm27x_controls); i++) {
err = snd_ctl_add(codec->card,
snd_soc_cnew(&palm27x_controls[i],
codec, NULL));
if (err < 0)
return err;
}
/* add palm27x specific widgets */
snd_soc_dapm_new_controls(codec, palm27x_dapm_widgets,
ARRAY_SIZE(palm27x_dapm_widgets));
/* set up palm27x specific audio path audio_map */
snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
snd_soc_dapm_sync(codec);
return 0;
}
static struct snd_soc_dai_link palm27x_dai[] = {
{
.name = "AC97 HiFi",
.stream_name = "AC97 HiFi",
.cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_HIFI],
.codec_dai = &wm9712_dai[WM9712_DAI_AC97_HIFI],
.init = palm27x_ac97_init,
.ops = &palm27x_ops,
},
{
.name = "AC97 Aux",
.stream_name = "AC97 Aux",
.cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_AUX],
.codec_dai = &wm9712_dai[WM9712_DAI_AC97_AUX],
.ops = &palm27x_ops,
},
};
static struct snd_soc_card palm27x_asoc = {
.name = "Palm/PXA27x",
.dai_link = palm27x_dai,
.num_links = ARRAY_SIZE(palm27x_dai),
};
static struct snd_soc_device palm27x_snd_devdata = {
.card = &palm27x_asoc,
.platform = &pxa2xx_soc_platform,
.codec_dev = &soc_codec_dev_wm9712,
};
static struct platform_device *palm27x_snd_device;
static int __init palm27x_asoc_init(void)
{
int ret;
if (!(machine_is_palmtx() || machine_is_palmt5() ||
machine_is_palmld()))
return -ENODEV;
ret = gpio_request(palm27x_ep_gpio, "Headphone Jack");
if (ret)
return ret;
ret = gpio_direction_input(palm27x_ep_gpio);
if (ret)
goto err_alloc;
if (request_irq(gpio_to_irq(palm27x_ep_gpio), palm27x_interrupt,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
"Headphone jack", NULL))
goto err_alloc;
palm27x_snd_device = platform_device_alloc("soc-audio", -1);
if (!palm27x_snd_device) {
ret = -ENOMEM;
goto err_dev;
}
platform_set_drvdata(palm27x_snd_device, &palm27x_snd_devdata);
palm27x_snd_devdata.dev = &palm27x_snd_device->dev;
ret = platform_device_add(palm27x_snd_device);
if (ret != 0)
goto put_device;
return 0;
put_device:
platform_device_put(palm27x_snd_device);
err_dev:
free_irq(gpio_to_irq(palm27x_ep_gpio), NULL);
err_alloc:
gpio_free(palm27x_ep_gpio);
return ret;
}
static void __exit palm27x_asoc_exit(void)
{
free_irq(gpio_to_irq(palm27x_ep_gpio), NULL);
gpio_free(palm27x_ep_gpio);
platform_device_unregister(palm27x_snd_device);
}
void __init palm27x_asoc_set_pdata(struct palm27x_asoc_info *data)
{
palm27x_ep_gpio = data->jack_gpio;
}
module_init(palm27x_asoc_init);
module_exit(palm27x_asoc_exit);
/* Module information */
MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>");
MODULE_DESCRIPTION("ALSA SoC Palm T|X, T5 and LifeDrive");
MODULE_LICENSE("GPL");