diff options
Diffstat (limited to 'packages/linux/linux-ezx-2.6.23/patches/ezx-asoc.patch')
-rw-r--r-- | packages/linux/linux-ezx-2.6.23/patches/ezx-asoc.patch | 1302 |
1 files changed, 1302 insertions, 0 deletions
diff --git a/packages/linux/linux-ezx-2.6.23/patches/ezx-asoc.patch b/packages/linux/linux-ezx-2.6.23/patches/ezx-asoc.patch new file mode 100644 index 0000000000..2226022a2c --- /dev/null +++ b/packages/linux/linux-ezx-2.6.23/patches/ezx-asoc.patch @@ -0,0 +1,1302 @@ +Index: linux-2.6.23/sound/soc/codecs/pcap2.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.23/sound/soc/codecs/pcap2.c 2007-10-22 22:28:06.000000000 +0200 +@@ -0,0 +1,796 @@ ++/* ++ * pcap2.c - PCAP2 ASIC Audio driver ++ * ++ * Copyright (C) 2007 Daniel Ribeiro <drwyrm@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/delay.h> ++#include <linux/moduleparam.h> ++#include <linux/init.h> ++#include <linux/delay.h> ++#include <linux/pm.h> ++#include <linux/platform_device.h> ++#include <sound/driver.h> ++#include <sound/core.h> ++#include <sound/pcm.h> ++#include <sound/pcm_params.h> ++#include <sound/soc.h> ++#include <sound/soc-dapm.h> ++#include <sound/initval.h> ++#include <asm/arch/pxa-regs.h> ++#include <asm/arch/ezx-pcap.h> ++#include <asm/arch/ezx.h> ++#include <asm/arch/hardware.h> ++ ++#include "pcap2.h" ++ ++#define AUDIO_NAME "pcap2-codec" ++#define PCAP2_VERSION "0.1" ++ ++extern int ezx_pcap_write(u_int8_t, u_int32_t); ++extern int ezx_pcap_read(u_int8_t, u_int32_t *); ++static struct snd_soc_device *pcap2_codec_socdev; ++ ++/* ++ * Debug ++ */ ++ ++//#define PCAP2_DEBUG ++ ++#ifdef PCAP2_DEBUG ++#define dbg(format, arg...) \ ++ printk(KERN_DEBUG AUDIO_NAME ": " format "\n" , ## arg) ++#else ++#define dbg(format, arg...) ++#endif ++ ++#define err(format, arg...) \ ++ printk(KERN_ERR AUDIO_NAME ": " format "\n" , ## arg) ++#define info(format, arg...) \ ++ printk(KERN_INFO AUDIO_NAME ": " format "\n" , ## arg) ++#define warn(format, arg...) \ ++ printk(KERN_WARNING AUDIO_NAME ": " format "\n" , ## arg) ++ ++#define dump_registers() pcap2_codec_read(NULL, 13); \ ++ pcap2_codec_read(NULL, 12); \ ++ pcap2_codec_read(NULL, 11); \ ++ pcap2_codec_read(NULL, 26); ++ ++ ++ ++ ++/* ++ * ASoC limits register value to 16 bits and pcap uses 32 bit registers ++ * to work around this, we get 16 bits from low, mid or high positions. ++ * ASoC limits register number to 8 bits we use 0x1f for register ++ * number and 0xe0 for register offset. -WM ++ */ ++static int pcap2_codec_write(struct snd_soc_codec *codec, unsigned int reg, ++ unsigned int value) ++{ ++ unsigned int tmp; ++ ++ ezx_pcap_read((reg & 0x1f), &tmp); ++ ++ if (reg & SL) { ++ tmp &= 0xffff0000; ++ tmp |= (value & 0xffff); ++ } ++ else if (reg & SM) { ++ tmp &= 0xff0000ff; ++ tmp |= ((value << 8) & 0x00ffff00); ++ } ++ else if (reg & SH) { ++ tmp &= 0xffff; ++ tmp |= ((value << 16) & 0xffff0000); ++ } ++ else ++ tmp = value; ++ ++ dbg("codec_write reg=%x, rval=%08x, fval=%08x", reg, tmp, value); ++ ezx_pcap_write((reg & 0x1f), tmp); ++ return 0; ++ ++} ++ ++static unsigned int pcap2_codec_read(struct snd_soc_codec *codec, unsigned int reg) ++{ ++ unsigned int tmp, ret; ++ ++ ezx_pcap_read((reg & 0x1f), &tmp); ++ ret = tmp; ++ if (reg & SL) ++ ret = (tmp & 0xffff); ++ else if (reg & SM) ++ ret = ((tmp >> 8) & 0xffff); ++ else if (reg & SH) ++ ret = ((tmp >> 16) & 0xffff); ++ ++ dbg("codec_read reg=%x, rval=%08x, fval=%08x", reg, tmp, ret); ++ return(ret); ++ ++} ++ ++static const char *pcap2_output_select[] = {"2ch", "2->1ch", "2->1ch -3db", "2->1ch -6db"}; ++ ++static const struct soc_enum pcap2_enum[] = { ++SOC_ENUM_SINGLE((PCAP2_OUTPUT_AMP|SH), 3, 4, pcap2_output_select), ++}; ++ ++static const struct snd_kcontrol_new pcap2_input_mixer_controls[] = { ++SOC_DAPM_SINGLE("A3 Switch", (PCAP2_INPUT_AMP|SL), 6, 1, 0), ++SOC_DAPM_SINGLE("A5 Switch", (PCAP2_INPUT_AMP|SL), 8, 1, 0), ++}; ++ ++static const struct snd_kcontrol_new pcap2_output_mixer_controls[] = { ++SOC_DAPM_SINGLE("A1 Switch", (PCAP2_OUTPUT_AMP|SL), 0, 1, 0), ++SOC_DAPM_SINGLE("A2 Switch", (PCAP2_OUTPUT_AMP|SL), 1, 1, 0), ++SOC_DAPM_SINGLE("AR Switch", (PCAP2_OUTPUT_AMP|SL), 5, 1, 0), ++SOC_DAPM_SINGLE("AL Switch", (PCAP2_OUTPUT_AMP|SL), 6, 1, 0), ++}; ++ ++/* pcap2 codec non DAPM controls */ ++static const struct snd_kcontrol_new pcap2_codec_snd_controls[] = { ++SOC_SINGLE("Output gain", (PCAP2_OUTPUT_AMP|SM), 5, 15, 0), ++SOC_SINGLE("Input gain", (PCAP2_INPUT_AMP|SL), 0, 31, 0), ++}; ++ ++static const struct snd_kcontrol_new pcap2_codec_dm_mux_control[] = { ++ SOC_DAPM_ENUM("Output Mode", pcap2_enum[0]), ++}; ++ ++/* add non dapm controls */ ++static int pcap2_codec_add_controls(struct snd_soc_codec *codec) ++{ ++ int err, i; ++ ++ for (i = 0; i < ARRAY_SIZE(pcap2_codec_snd_controls); i++) { ++ if ((err = snd_ctl_add(codec->card, ++ snd_soc_cnew(&pcap2_codec_snd_controls[i],codec, NULL))) < 0) ++ return err; ++ } ++ ++ return 0; ++} ++ ++/* pcap2 codec DAPM controls */ ++static const struct snd_soc_dapm_widget pcap2_codec_dapm_widgets[] = { ++ SND_SOC_DAPM_DAC("ST_DAC", "ST_DAC playback", SND_SOC_NOPM, 0, 0), ++ SND_SOC_DAPM_DAC("CDC_DAC", "CDC_DAC playback", SND_SOC_NOPM, 0, 0), ++ SND_SOC_DAPM_ADC("CDC_ADC", "CDC_DAC capture", SND_SOC_NOPM, 0, 0), ++ SND_SOC_DAPM_PGA("PGA_ST", (PCAP2_OUTPUT_AMP|SL), 9, 0, NULL, 0), ++ SND_SOC_DAPM_PGA("PGA_CDC", (PCAP2_OUTPUT_AMP|SL), 8, 0, NULL, 0), ++ SND_SOC_DAPM_PGA("PGA_R", (PCAP2_OUTPUT_AMP|SL), 11, 0, NULL, 0), ++ SND_SOC_DAPM_PGA("PGA_L", (PCAP2_OUTPUT_AMP|SL), 12, 0, NULL, 0), ++ SND_SOC_DAPM_MUX("Downmixer", SND_SOC_NOPM, 0, 0, pcap2_codec_dm_mux_control), ++ SND_SOC_DAPM_PGA("PGA_A1CTRL", (PCAP2_OUTPUT_AMP|SH), 1, 1, NULL, 0), ++ SND_SOC_DAPM_MIXER("Output Mixer", SND_SOC_NOPM, 0, 0, &pcap2_output_mixer_controls[0], ARRAY_SIZE(pcap2_output_mixer_controls)), ++ SND_SOC_DAPM_OUTPUT("A1"), /* Earpiece */ ++ SND_SOC_DAPM_OUTPUT("A2"), /* LoudSpeaker */ ++ SND_SOC_DAPM_OUTPUT("AR"), /* headset right */ ++ SND_SOC_DAPM_OUTPUT("AL"), /* headset left */ ++ ++ SND_SOC_DAPM_MICBIAS("BIAS1", (PCAP2_INPUT_AMP|SL), 10, 0), ++ SND_SOC_DAPM_MICBIAS("BIAS2", (PCAP2_INPUT_AMP|SL), 11, 0), ++ SND_SOC_DAPM_MIXER("Input Mixer", SND_SOC_NOPM, 0, 0, &pcap2_input_mixer_controls[0], ARRAY_SIZE(pcap2_input_mixer_controls)), ++ SND_SOC_DAPM_INPUT("A3"), /* Headset Mic */ ++ SND_SOC_DAPM_INPUT("A5"), /* Builtin Mic */ ++}; ++ ++static const char *audio_map[][3] = { ++ { "A1", NULL, "Output Mixer" }, ++ { "A2", NULL, "Output Mixer" }, ++ { "AR", NULL, "Output Mixer" }, ++ { "AL", NULL, "Output Mixer" }, ++ ++ { "Output Mixer", "A1 Switch", "PGA_A1CTRL" }, ++ { "Output Mixer", "A2 Switch", "Downmixer" }, ++ { "Output Mixer", "AR Switch", "PGA_R" }, ++ { "Output Mixer", "AL Switch", "PGA_L" }, ++ ++ { "PGA_A1CTRL", NULL, "Downmixer" }, ++ ++ { "Downmixer", "2->1ch", "PGA_L" }, ++ { "Downmixer", "2->1ch", "PGA_R" }, ++ { "Downmixer", "2->1ch -3db", "PGA_L" }, ++ { "Downmixer", "2->1ch -3db", "PGA_R" }, ++ { "Downmixer", "2->1ch -6db", "PGA_L" }, ++ { "Downmixer", "2->1ch -6db", "PGA_R" }, ++ { "Downmixer", "2ch", "PGA_R" }, ++ ++ { "PGA_R", NULL, "PGA_ST" }, ++ { "PGA_L", NULL, "PGA_ST" }, ++ { "PGA_R", NULL, "PGA_CDC" }, ++ ++ { "PGA_ST", NULL, "ST_DAC" }, ++ { "PGA_CDC", NULL, "CDC_DAC" }, ++ ++ /* input path */ ++ { "BIAS1", NULL, "A3" }, ++ { "BIAS2", NULL, "A5" }, ++ ++ { "Input Mixer", "A3 Switch", "BIAS1" }, ++ { "Input Mixer", "A5 Switch", "BIAS2" }, ++ ++ { "PGA_R", NULL, "Input Mixer" }, ++ ++ { "PGA_CDC", NULL, "PGA_R" }, ++ { "CDC_ADC", NULL, "PGA_CDC" }, ++ ++ /* terminator */ ++ {NULL, NULL, NULL}, ++}; ++ ++static int pcap2_codec_add_widgets(struct snd_soc_codec *codec) ++{ ++ int i; ++ ++ for(i = 0; i < ARRAY_SIZE(pcap2_codec_dapm_widgets); i++) { ++ snd_soc_dapm_new_control(codec, &pcap2_codec_dapm_widgets[i]); ++ } ++ ++ /* set up audio path interconnects */ ++ for(i = 0; audio_map[i][0] != NULL; i++) { ++ snd_soc_dapm_connect_input(codec, audio_map[i][0], ++ audio_map[i][1], audio_map[i][2]); ++ } ++ ++ snd_soc_dapm_new_widgets(codec); ++ return 0; ++} ++ ++static int pcap2_codec_dapm_event(struct snd_soc_codec *codec, int event) ++{ ++ unsigned int input = pcap2_codec_read(codec, PCAP2_INPUT_AMP); ++ ++ input &= ~PCAP2_INPUT_AMP_LOWPWR; ++ ++ switch (event) { ++ case SNDRV_CTL_POWER_D0: ++ case SNDRV_CTL_POWER_D1: ++ case SNDRV_CTL_POWER_D2: ++ case SNDRV_CTL_POWER_D3hot: /* Off, with power */ ++ dbg("dapm: ON\n"); ++ break; ++ case SNDRV_CTL_POWER_D3cold: /* Off, without power */ ++ input |= PCAP2_INPUT_AMP_LOWPWR; ++ dbg("dapm: OFF\n"); ++ break; ++ } ++ codec->dapm_state = event; ++ pcap2_codec_write(codec, PCAP2_INPUT_AMP, input); ++ return 0; ++} ++ ++static int pcap2_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_codec_dai *codec_dai = rtd->dai->codec_dai; ++ struct snd_soc_codec *codec = codec_dai->codec; ++ unsigned int tmp; ++ ++ if (codec_dai->id == PCAP2_STEREO_DAI) { ++ tmp = pcap2_codec_read(codec, PCAP2_ST_DAC); ++ ++ tmp &= ~PCAP2_ST_DAC_RATE_MASK; ++ switch(params_rate(params)) { ++ case 8000: ++ break; ++ case 11025: ++ tmp |= PCAP2_ST_DAC_RATE_11025; ++ break; ++ case 12000: ++ tmp |= PCAP2_ST_DAC_RATE_12000; ++ break; ++ case 16000: ++ tmp |= PCAP2_ST_DAC_RATE_16000; ++ break; ++ case 22050: ++ tmp |= PCAP2_ST_DAC_RATE_22050; ++ break; ++ case 24000: ++ tmp |= PCAP2_ST_DAC_RATE_24000; ++ break; ++ case 32000: ++ tmp |= PCAP2_ST_DAC_RATE_32000; ++ break; ++ case 44100: ++ tmp |= PCAP2_ST_DAC_RATE_44100; ++ break; ++ case 48000: ++ tmp |= PCAP2_ST_DAC_RATE_48000; ++ break; ++ default: ++ return -EINVAL; ++ } ++ tmp |= PCAP2_ST_DAC_RESET_DF; ++ pcap2_codec_write(codec, PCAP2_ST_DAC, tmp); ++ } ++ else { ++ tmp = pcap2_codec_read(codec, PCAP2_CODEC); ++ ++ tmp &= ~PCAP2_CODEC_RATE_MASK; ++ switch(params_rate(params)) { ++ case 8000: ++ break; ++ case 16000: ++ tmp |= PCAP2_CODEC_RATE_16000; ++ break; ++ default: ++ return -EINVAL; ++ } ++ tmp |= PCAP2_CODEC_RESET_DF; ++ pcap2_codec_write(codec, PCAP2_CODEC, tmp); ++ } ++ ++ return 0; ++} ++ ++static int pcap2_hw_free(struct snd_pcm_substream *substream) ++{ ++ struct snd_soc_pcm_runtime *rtd = substream->private_data; ++ struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai; ++ struct snd_soc_codec *codec = codec_dai->codec; ++ struct snd_soc_dapm_widget *w; ++ unsigned int tmp; ++ ++ if (codec_dai->id == PCAP2_STEREO_DAI) { ++ snd_soc_dapm_set_endpoint(codec, "ST_DAC", 0); ++ tmp = pcap2_codec_read(codec, PCAP2_ST_DAC); ++ tmp &= ~(PCAP2_ST_DAC_EN | PCAP2_ST_DAC_CLK_EN); ++ pcap2_codec_write(codec, PCAP2_ST_DAC, tmp); ++ } ++ else { ++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ++ snd_soc_dapm_set_endpoint(codec, "CDC_DAC", 0); ++ else ++ snd_soc_dapm_set_endpoint(codec, "CDC_ADC", 0); ++ list_for_each_entry(w, &codec->dapm_widgets, list) { ++ if ((!strcmp(w->name, "CDC_DAC") || !strcmp(w->name, "CDC_ADC")) && w->connected) ++ goto in_use; ++ } ++ tmp = pcap2_codec_read(codec, PCAP2_CODEC); ++ tmp &= ~(PCAP2_CODEC_EN | PCAP2_CODEC_CLK_EN); ++ pcap2_codec_write(codec, PCAP2_CODEC, tmp); ++ } ++in_use: ++ snd_soc_dapm_sync_endpoints(codec); ++ ++ return 0; ++} ++ ++static int pcap2_set_dai_sysclk(struct snd_soc_codec_dai *codec_dai, ++ int clk_id, unsigned int freq, int dir) ++{ ++ struct snd_soc_codec *codec = codec_dai->codec; ++ ++ unsigned int tmp; ++ if (codec_dai->id == PCAP2_STEREO_DAI) { ++ /* ST_DAC */ ++ ++ tmp = pcap2_codec_read(codec, PCAP2_ST_DAC); ++ ++ tmp &= ~PCAP2_ST_DAC_CLKSEL_MASK; ++ switch (clk_id) { ++ case PCAP2_CLK_AP: ++ tmp |= PCAP2_ST_DAC_CLKSEL_AP; ++ break; ++ case PCAP2_CLK_BP: ++ break; ++ default: ++ return -ENODEV; ++ } ++ ++ tmp &= ~PCAP2_ST_DAC_CLK_MASK; ++ switch (freq) { ++ case 13000000: ++ break; ++/* case 15M36: ++ tmp |= PCAP2_ST_DAC_CLK_15M36; ++ break; ++ case 16M8: ++ tmp |= PCAP2_ST_DAC_CLK_16M8; ++ break; ++ case 19M44: ++ tmp |= PCAP2_ST_DAC_CLK_19M44; ++ break; ++*/ case 26000000: ++ tmp |= PCAP2_ST_DAC_CLK_26M; ++ break; ++/* case EXT_MCLK: ++ tmp |= PCAP2_ST_DAC_CLK_MCLK; ++ break; ++ case FSYNC: ++ tmp |= PCAP2_ST_DAC_CLK_FSYNC; ++ break; ++ case BITCLK: ++ tmp |= PCAP2_ST_DAC_CLK_BITCLK; ++ break; ++*/ default: ++ return -EINVAL; ++ } ++ pcap2_codec_write(codec, PCAP2_ST_DAC, tmp); ++ } ++ else { ++ /* MONO_DAC */ ++ tmp = pcap2_codec_read(codec, PCAP2_CODEC); ++ ++ tmp &= ~PCAP2_CODEC_CLKSEL_MASK; ++ switch (clk_id) { ++ case PCAP2_CLK_AP: ++ tmp |= PCAP2_CODEC_CLKSEL_AP; ++ break; ++ case PCAP2_CLK_BP: ++ break; ++ default: ++ return -ENODEV; ++ } ++ ++ tmp &= ~PCAP2_CODEC_CLK_MASK; ++ switch (freq) { ++ case 13000000: ++ break; ++/* case 15M36: ++ tmp |= PCAP2_CODEC_CLK_15M36; ++ break; ++ case 16M8: ++ tmp |= PCAP2_CODEC_CLK_16M8; ++ break; ++ case 19M44: ++ tmp |= PCAP2_CODEC_CLK_19M44; ++ break; ++*/ case 26000000: ++ tmp |= PCAP2_CODEC_CLK_26M; ++ break; ++ default: ++ return -EINVAL; ++ } ++ pcap2_codec_write(codec, PCAP2_CODEC, tmp); ++ } ++ return 0; ++} ++ ++static int pcap2_set_dai_fmt(struct snd_soc_codec_dai *codec_dai, ++ unsigned int fmt) ++{ ++ struct snd_soc_codec *codec = codec_dai->codec; ++ unsigned int tmp = 0; ++ ++ if (codec_dai->id == PCAP2_STEREO_DAI) { ++ /* ST_DAC */ ++ ++ /* disable CODEC */ ++ pcap2_codec_write(codec, PCAP2_CODEC, 0); ++ ++ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { ++ case SND_SOC_DAIFMT_CBM_CFM: ++ break; ++ case SND_SOC_DAIFMT_CBS_CFS: ++ tmp |= 0x1; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { ++ case SND_SOC_DAIFMT_I2S: ++ tmp |= 0x4000; ++ break; ++/* case SND_SOC_NET: ++ tmp |= 0x2000; ++ break; ++*/ case SND_SOC_DAIFMT_DSP_B: ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ switch (fmt & SND_SOC_DAIFMT_INV_MASK) { ++ case SND_SOC_DAIFMT_IB_IF: ++ break; ++ case SND_SOC_DAIFMT_NB_NF: ++ tmp |= 0x60000; ++ break; ++ case SND_SOC_DAIFMT_IB_NF: ++ tmp |= 0x40000; ++ break; ++ case SND_SOC_DAIFMT_NB_IF: ++ tmp |= 0x20000; ++ break; ++ } ++ /* set dai to AP */ ++ tmp |= 0x1000; ++ ++ /* set BCLK */ ++ tmp |= 0x18000; ++ ++ pcap2_codec_write(codec, PCAP2_ST_DAC, tmp); ++ } ++ else { ++ /* MONO_DAC */ ++ ++ /* disable ST_DAC */ ++ pcap2_codec_write(codec, PCAP2_ST_DAC, 0); ++ ++ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { ++ case SND_SOC_DAIFMT_CBM_CFM: ++ break; ++ case SND_SOC_DAIFMT_CBS_CFS: ++ tmp |= 0x2; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { ++ case SND_SOC_DAIFMT_DSP_B: ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ switch (fmt & SND_SOC_DAIFMT_INV_MASK) { ++ case SND_SOC_DAIFMT_IB_IF: ++ break; ++ case SND_SOC_DAIFMT_NB_NF: ++ tmp |= 0x600; ++ break; ++ case SND_SOC_DAIFMT_IB_NF: ++ tmp |= 0x400; ++ break; ++ case SND_SOC_DAIFMT_NB_IF: ++ tmp |= 0x200; ++ break; ++ } ++ if (codec_dai->id == PCAP2_MONO_DAI) ++ /* set dai to AP */ ++ tmp |= 0x8000; ++ ++ tmp |= 0x5; /* IHF / OHF */ ++ ++ pcap2_codec_write(codec, PCAP2_CODEC, tmp); ++ } ++ return 0; ++} ++ ++static int pcap2_prepare(struct snd_pcm_substream *substream) ++{ ++ ++ struct snd_soc_pcm_runtime *rtd = substream->private_data; ++ struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai; ++ struct snd_soc_codec *codec = codec_dai->codec; ++ unsigned int tmp; ++ /* FIXME enable clock only if codec is master */ ++ if (codec_dai->id == PCAP2_STEREO_DAI) { ++ snd_soc_dapm_set_endpoint(codec, "ST_DAC", 1); ++ snd_soc_dapm_set_endpoint(codec, "CDC_DAC", 0); ++ snd_soc_dapm_set_endpoint(codec, "CDC_ADC", 0); ++ tmp = pcap2_codec_read(codec, PCAP2_ST_DAC); ++ tmp |= (PCAP2_ST_DAC_EN | PCAP2_ST_DAC_CLK_EN); ++ pcap2_codec_write(codec, PCAP2_ST_DAC, tmp); ++ } ++ else { ++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ++ snd_soc_dapm_set_endpoint(codec, "CDC_DAC", 1); ++ else ++ snd_soc_dapm_set_endpoint(codec, "CDC_ADC", 1); ++ snd_soc_dapm_set_endpoint(codec, "ST_DAC", 0); ++ tmp = pcap2_codec_read(codec, PCAP2_CODEC); ++ tmp |= (PCAP2_CODEC_EN | PCAP2_CODEC_CLK_EN); ++ pcap2_codec_write(codec, PCAP2_CODEC, tmp); ++ } ++ snd_soc_dapm_sync_endpoints(codec); ++ mdelay(1); ++#ifdef PCAP2_DEBUG ++ dump_registers(); ++#endif ++ return 0; ++} ++ ++/* ++ * Define codec DAI. ++ */ ++struct snd_soc_codec_dai pcap2_dai[] = { ++{ ++ .name = "PCAP2 MONO", ++ .id = 0, ++ .playback = { ++ .stream_name = "CDC_DAC playback", ++ .channels_min = 1, ++ .channels_max = 1, ++ .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000), ++ .formats = SNDRV_PCM_FMTBIT_S16_LE, ++ }, ++ .capture = { ++ .stream_name = "CDC_DAC capture", ++ .channels_min = 1, ++ .channels_max = 1, ++ .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000), ++ .formats = SNDRV_PCM_FMTBIT_S16_LE, ++ }, ++ .ops = { ++ .prepare = pcap2_prepare, ++ .hw_params = pcap2_hw_params, ++ .hw_free = pcap2_hw_free, ++ }, ++ .dai_ops = { ++// .digital_mute = pcap2_mute, ++ .set_fmt = pcap2_set_dai_fmt, ++ .set_sysclk = pcap2_set_dai_sysclk, ++ }, ++}, ++{ ++ .name = "PCAP2 STEREO", ++ .id = 1, ++ .playback = { ++ .stream_name = "ST_DAC playback", ++ .channels_min = 1, ++ .channels_max = 2, ++ .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | ++ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | ++ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | ++ SNDRV_PCM_RATE_48000), ++ .formats = SNDRV_PCM_FMTBIT_S16_LE, ++ }, ++ .capture = { /* FIXME: PCAP support this?? */ ++ .stream_name = "ST_DAC capture", ++ .channels_min = 1, ++ .channels_max = 1, ++ .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | ++ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | ++ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | ++ SNDRV_PCM_RATE_48000), ++ .formats = SNDRV_PCM_FMTBIT_S16_LE, ++ }, ++ .ops = { ++ .prepare = pcap2_prepare, ++ .hw_params = pcap2_hw_params, ++ .hw_free = pcap2_hw_free, ++ }, ++ .dai_ops = { ++// .digital_mute = pcap2_mute, ++ .set_fmt = pcap2_set_dai_fmt, ++ .set_sysclk = pcap2_set_dai_sysclk, ++ }, ++}, ++{ ++ .name = "PCAP2 BP", ++ .id = 2, ++ .playback = { ++ .stream_name = "BP playback", ++ .channels_min = 1, ++ .channels_max = 1, ++ .rates = SNDRV_PCM_RATE_8000, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE, ++ }, ++ .ops = { ++ .prepare = pcap2_prepare, ++ .hw_params = pcap2_hw_params, ++ .hw_free = pcap2_hw_free, ++ }, ++ .dai_ops = { ++// .digital_mute = pcap2_mute, ++ .set_fmt = pcap2_set_dai_fmt, ++ .set_sysclk = pcap2_set_dai_sysclk, ++ }, ++}, ++}; ++EXPORT_SYMBOL_GPL(pcap2_dai); ++ ++static int pcap2_codec_suspend(struct platform_device *pdev, pm_message_t state) ++{ ++ struct snd_soc_device *socdev = platform_get_drvdata(pdev); ++ struct snd_soc_codec *codec = socdev->codec; ++ ++ dbg("pcap2_codec_suspend"); ++ pcap2_codec_dapm_event(codec, SNDRV_CTL_POWER_D3cold); ++ return 0; ++} ++ ++static int pcap2_codec_resume(struct platform_device *pdev) ++{ ++ struct snd_soc_device *socdev = platform_get_drvdata(pdev); ++ struct snd_soc_codec *codec = socdev->codec; ++ ++ dbg("pcap2_codec_resume"); ++ pcap2_codec_dapm_event(codec, SNDRV_CTL_POWER_D3hot); ++ pcap2_codec_dapm_event(codec, codec->suspend_dapm_state); ++ return 0; ++} ++ ++/* ++ * initialise the PCAP2 driver ++ * register the mixer and dsp interfaces with the kernel ++ */ ++static int pcap2_codec_init(struct snd_soc_device *socdev) ++{ ++ struct snd_soc_codec *codec = socdev->codec; ++ int ret = 0; ++ ++ dbg("pcap2_codec_init"); ++ codec->name = "PCAP2 Audio"; ++ codec->owner = THIS_MODULE; ++ codec->read = pcap2_codec_read; ++ codec->write = pcap2_codec_write; ++ codec->dapm_event = pcap2_codec_dapm_event; ++ codec->dai = pcap2_dai; ++ codec->num_dai = ARRAY_SIZE(pcap2_dai); ++ ++ /* register pcms */ ++ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); ++ if (ret < 0) { ++ return ret; ++ } ++ /* power on device */ ++ pcap2_codec_dapm_event(codec, SNDRV_CTL_POWER_D3hot); ++ /* set the update bits */ ++ ++ pcap2_codec_add_controls(codec); ++ pcap2_codec_add_widgets(codec); ++ ret = snd_soc_register_card(socdev); ++ if (ret < 0) { ++ snd_soc_free_pcms(socdev); ++ snd_soc_dapm_free(socdev); ++ } ++ ++ return ret; ++} ++ ++static int pcap2_codec_probe(struct platform_device *pdev) ++{ ++ struct snd_soc_device *socdev = platform_get_drvdata(pdev); ++ struct pcap2_codec_setup_data *setup; ++ struct snd_soc_codec *codec; ++ int ret = 0; ++ info("PCAP2 Audio Codec %s", PCAP2_VERSION); ++ ++ setup = socdev->codec_data; ++ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); ++ if (codec == NULL) ++ return -ENOMEM; ++ ++ socdev->codec = codec; ++ mutex_init(&codec->mutex); ++ INIT_LIST_HEAD(&codec->dapm_widgets); ++ INIT_LIST_HEAD(&codec->dapm_paths); ++ ++ pcap2_codec_socdev = socdev; ++ ++ ret = pcap2_codec_init(socdev); ++ return ret; ++} ++ ++/* power down chip and remove */ ++static int pcap2_codec_remove(struct platform_device *pdev) ++{ ++ struct snd_soc_device *socdev = platform_get_drvdata(pdev); ++ struct snd_soc_codec *codec = socdev->codec; ++ if (codec->control_data) ++ pcap2_codec_dapm_event(codec, SNDRV_CTL_POWER_D3cold); ++ snd_soc_free_pcms(socdev); ++ snd_soc_dapm_free(socdev); ++ ++ kfree(codec); ++ ++ return 0; ++} ++ ++/* codec device ops */ ++struct snd_soc_codec_device soc_codec_dev_pcap2 = { ++ .probe = pcap2_codec_probe, ++ .remove = pcap2_codec_remove, ++ .suspend = pcap2_codec_suspend, ++ .resume = pcap2_codec_resume, ++}; ++ ++EXPORT_SYMBOL_GPL(soc_codec_dev_pcap2); ++ ++MODULE_DESCRIPTION("ASoC PCAP2 codec"); ++MODULE_AUTHOR("Daniel Ribeiro"); ++MODULE_LICENSE("GPL"); +Index: linux-2.6.23/sound/soc/codecs/pcap2.h +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.23/sound/soc/codecs/pcap2.h 2007-10-22 22:28:06.000000000 +0200 +@@ -0,0 +1,81 @@ ++/* ++ * 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. ++ */ ++ ++#ifndef _PCAP2_H ++#define _PCAP2_H ++ ++/* 16 bit reads/writes on pcap registers (ugly workaround) */ ++#define SL (1 << 5) /* lower 16 bits */ ++#define SM (1 << 6) /* mid 16 bits */ ++#define SH (1 << 7) /* higher 16 bits */ ++ ++/* PCAP2 register space */ ++#define PCAP2_CODEC 0x0b ++#define PCAP2_OUTPUT_AMP 0x0c ++#define PCAP2_ST_DAC 0x0d ++#define PCAP2_INPUT_AMP 0x1a ++ ++#define PCAP2_MONO_DAI 0 ++#define PCAP2_STEREO_DAI 1 ++#define PCAP2_BP_DAI 2 ++ ++#define PCAP2_CLK_BP 0 ++#define PCAP2_CLK_AP 1 ++ ++#define PCAP2_CODEC_EN 0x2000 ++#define PCAP2_CODEC_CLK_EN 0x1000 ++#define PCAP2_CODEC_RESET_DF 0x800 ++#define PCAP2_CODEC_RATE_MASK 0x4000 ++#define PCAP2_CODEC_RATE_8000 0x0 ++#define PCAP2_CODEC_RATE_16000 0x4000 ++#define PCAP2_CODEC_CLKSEL_MASK 0x10000 ++#define PCAP2_CODEC_CLKSEL_AP 0x10000 ++#define PCAP2_CODEC_CLKSEL_BP 0x0 ++#define PCAP2_CODEC_CLK_MASK 0x1c0 ++#define PCAP2_CODEC_CLK_13M 0x0 ++#define PCAP2_CODEC_CLK_15M36 0x40 ++#define PCAP2_CODEC_CLK_16M8 0x80 ++#define PCAP2_CODEC_CLK_19M44 0xc0 ++#define PCAP2_CODEC_CLK_26M 0x100 ++ ++#define PCAP2_ST_DAC_EN 0x80 ++#define PCAP2_ST_DAC_CLK_EN 0x20 ++#define PCAP2_ST_DAC_RESET_DF 0x40 ++#define PCAP2_ST_DAC_RATE_MASK 0xf00 ++#define PCAP2_ST_DAC_RATE_8000 0x0 ++#define PCAP2_ST_DAC_RATE_11025 0x100 ++#define PCAP2_ST_DAC_RATE_12000 0x200 ++#define PCAP2_ST_DAC_RATE_16000 0x300 ++#define PCAP2_ST_DAC_RATE_22050 0x400 ++#define PCAP2_ST_DAC_RATE_24000 0x500 ++#define PCAP2_ST_DAC_RATE_32000 0x600 ++#define PCAP2_ST_DAC_RATE_44100 0x700 ++#define PCAP2_ST_DAC_RATE_48000 0x800 ++#define PCAP2_ST_DAC_CLKSEL_MASK 0x80000 ++#define PCAP2_ST_DAC_CLKSEL_AP 0x80000 ++#define PCAP2_ST_DAC_CLKSEL_BP 0x0 ++#define PCAP2_ST_DAC_CLK_MASK 0x1c ++#define PCAP2_ST_DAC_CLK_13M 0x0 ++#define PCAP2_ST_DAC_CLK_15M36 0x4 ++#define PCAP2_ST_DAC_CLK_16M8 0x8 ++#define PCAP2_ST_DAC_CLK_19M44 0xc ++#define PCAP2_ST_DAC_CLK_26M 0x10 ++#define PCAP2_ST_DAC_CLK_MCLK 0x14 ++#define PCAP2_ST_DAC_CLK_FSYNC 0x18 ++#define PCAP2_ST_DAC_CLK_BITCLK 0x1c ++ ++#define PCAP2_INPUT_AMP_LOWPWR 0x80000 ++#define PCAP2_INPUT_AMP_V2EN2 0x200000 ++ ++#define PCAP2_OUTPUT_AMP_PGAR_EN 0x800 ++#define PCAP2_OUTPUT_AMP_PGAL_EN 0x1000 ++#define PCAP2_OUTPUT_AMP_CDC_SW 0x100 ++#define PCAP2_OUTPUT_AMP_ST_DAC_SW 0x200 ++ ++extern struct snd_soc_codec_dai pcap2_dai[]; ++extern struct snd_soc_codec_device soc_codec_dev_pcap2; ++ ++#endif +Index: linux-2.6.23/sound/soc/pxa/Kconfig +=================================================================== +--- linux-2.6.23.orig/sound/soc/pxa/Kconfig 2007-10-22 22:27:11.000000000 +0200 ++++ linux-2.6.23/sound/soc/pxa/Kconfig 2007-10-22 22:29:23.000000000 +0200 +@@ -57,3 +57,12 @@ + help + Say Y if you want to add support for SoC audio on Sharp + Zaurus SL-C6000x models (Tosa). ++ ++config SND_PXA2XX_SOC_EZX ++ tristate "SoC Audio support for EZX" ++ depends on SND_PXA2XX_SOC && PXA_EZX ++ select SND_PXA2XX_SOC_SSP ++ select SND_SOC_PCAP2 ++ help ++ Say Y if you want to add support for SoC audio on ++ Motorola EZX Phones (a780/e680). +Index: linux-2.6.23/sound/soc/pxa/ezx.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.23/sound/soc/pxa/ezx.c 2007-10-22 22:28:06.000000000 +0200 +@@ -0,0 +1,349 @@ ++/* ++ * ezx.c - Machine specific code for EZX phones ++ * ++ * Copyright (C) 2007 Daniel Ribeiro <drwyrm@gmail.com> ++ * ++ * 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 <linux/module.h> ++#include <linux/moduleparam.h> ++#include <linux/timer.h> ++#include <linux/interrupt.h> ++#include <linux/platform_device.h> ++#include <sound/driver.h> ++#include <sound/core.h> ++#include <sound/pcm.h> ++#include <sound/soc.h> ++#include <sound/soc-dapm.h> ++ ++#include <asm/mach-types.h> ++#include <asm/arch/pxa-regs.h> ++#include <asm/arch/hardware.h> ++ ++#include <asm/arch/ezx-pcap.h> ++ ++#include "../codecs/pcap2.h" ++#include "pxa2xx-pcm.h" ++#include "pxa2xx-ssp.h" ++ ++#define GPIO_HW_ATTENUATE_A780 96 ++ ++static struct snd_soc_codec *control_codec; ++ ++static void ezx_ext_control(struct snd_soc_codec *codec) ++{ ++ if (ezx_pcap_read_bit(pbit(PCAP_REG_PSTAT, PCAP_IRQ_A1))) ++ snd_soc_dapm_set_endpoint(codec, "Headset", 1); ++ else ++ snd_soc_dapm_set_endpoint(codec, "Headset", 0); ++ if (ezx_pcap_read_bit(pbit(PCAP_REG_PSTAT, PCAP_IRQ_MB2))) ++ snd_soc_dapm_set_endpoint(codec, "External Mic", 1); ++ else ++ snd_soc_dapm_set_endpoint(codec, "External Mic", 0); ++ ++ snd_soc_dapm_sync_endpoints(codec); ++} ++ ++static irqreturn_t jack_irq(int irq, void *data) ++{ ++ ezx_ext_control(control_codec); ++ return IRQ_HANDLED; ++} ++ ++ ++/* ++ * Alsa operations ++ * Only implement the required operations for your platform. ++ * These operations are specific to the machine only. ++ */ ++ ++ /* ++ * Called by ALSA when a PCM substream is opened, private data can be allocated. ++ */ ++static int ezx_machine_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 */ ++ ezx_ext_control(codec); ++ return 0; ++} ++ ++/* ++ * Called by ALSA when the hardware params are set by application. This ++ * function can also be called multiple times and can allocate buffers ++ * (using snd_pcm_lib_* ). It's non-atomic. ++ */ ++static int ezx_machine_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_codec_dai *codec_dai = rtd->dai->codec_dai; ++ struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai; ++ int ret; ++ ++ /* set codec DAI configuration */ ++ if (codec_dai->id == PCAP2_STEREO_DAI) ++ ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_B | ++ SND_SOC_DAIFMT_IB_NF | SND_SOC_DAIFMT_CBM_CFM); ++ else ++ ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_B | ++ SND_SOC_DAIFMT_IB_IF | SND_SOC_DAIFMT_CBM_CFM); ++ if(ret < 0) ++ return ret; ++ ++ /* Turn on clock output on CLK_PIO */ ++ OSCC |= 0x8; ++ ++ /* set clock source */ ++ ret = codec_dai->dai_ops.set_sysclk(codec_dai, PCAP2_CLK_AP, ++ 13000000, SND_SOC_CLOCK_IN); ++ if(ret < 0) ++ return ret; ++ ++ /* set cpu DAI configuration */ ++ ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_B | ++ SND_SOC_DAIFMT_IB_IF | SND_SOC_DAIFMT_CBM_CFM); ++ if (ret < 0) ++ return ret; ++ ++ ret = cpu_dai->dai_ops.set_tristate(cpu_dai, 0); ++ if (ret < 0) ++ return ret; ++ ++ ret = cpu_dai->dai_ops.set_sysclk(cpu_dai,PXA2XX_SSP_CLK_EXT, ++ 0, SND_SOC_CLOCK_IN); ++ if (ret < 0) ++ return ret; ++ ++ return 0; ++} ++ ++/* ++ * Free's resources allocated by hw_params, can be called multiple times ++ */ ++static int ezx_machine_hw_free(struct snd_pcm_substream *substream) ++{ ++ OSCC &= ~0x8; /* turn off clock output on CLK_PIO */ ++ ++ return 0; ++} ++ ++static int ezx_machine_prepare(struct snd_pcm_substream *substream) ++{ ++ struct snd_soc_pcm_runtime *rtd = substream->private_data; ++ struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai; ++ struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai; ++ ++ if (codec_dai->id == PCAP2_STEREO_DAI) { ++ /* override pxa2xx-ssp sample size for stereo/network mode */ ++ SSCR0_P(cpu_dai->id+1) &= ~(SSCR0_DSS | SSCR0_EDSS); ++ SSCR0_P(cpu_dai->id+1) |= (SSCR0_EDSS | SSCR0_DataSize(16)); ++ } ++ return 0; ++} ++ ++/* machine Alsa PCM operations */ ++static struct snd_soc_ops ezx_ops = { ++ .startup = ezx_machine_startup, ++ .prepare = ezx_machine_prepare, ++ .hw_free = ezx_machine_hw_free, ++ .hw_params = ezx_machine_hw_params, ++}; ++ ++static int bp_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_codec_dai *codec_dai = rtd->dai->codec_dai; ++// struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai; ++ int ret = 0; ++ /* set codec DAI configuration */ ++ ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_B | ++ SND_SOC_DAIFMT_IB_IF | SND_SOC_DAIFMT_CBM_CFM); ++ if(ret < 0) ++ return ret; ++ ++ /* set clock source */ ++ ret = codec_dai->dai_ops.set_sysclk(codec_dai, PCAP2_CLK_BP, ++ 13000000, SND_SOC_CLOCK_IN); ++ ++ return ret; ++} ++ ++ ++ ++/* machine dapm widgets */ ++static const struct snd_soc_dapm_widget ezx_dapm_widgets[] = { ++ SND_SOC_DAPM_HP("Headset", NULL), ++ SND_SOC_DAPM_SPK("Earpiece", NULL), ++ SND_SOC_DAPM_SPK("Loudspeaker", NULL), ++ SND_SOC_DAPM_MIC("Built-in Mic", NULL), ++ SND_SOC_DAPM_MIC("External Mic", NULL), ++}; ++ ++/* machine audio map (connections to the codec pins) */ ++static const char *audio_map[][3] = { ++ { "Headset", NULL, "AR" }, ++ { "Headset", NULL, "AL" }, ++ { "Earpiece", NULL, "A1" }, ++ { "Loudspeaker", NULL, "A2" }, ++ ++ { "Built-in Mic", NULL, "A5" }, ++ { "External Mic", NULL, "A3" }, ++ ++ {NULL, NULL, NULL}, ++}; ++ ++/* ++ * Initialise the machine audio subsystem. ++ */ ++static int ezx_machine_init(struct snd_soc_codec *codec) ++{ ++ int i; ++ /* mark unused codec pins as NC */ ++// snd_soc_dapm_set_endpoint(codec, "FIXME", 0); ++ control_codec = codec; ++ ++ /* Add ezx specific controls */ ++// for (i = 0; i < ARRAY_SIZE(ezx_controls); i++) { ++// if ((err = snd_ctl_add(codec->card, snd_soc_cnew(&ezx_controls[i], codec, NULL))) < 0) ++// return err; ++// } ++ ++ /* Add ezx specific widgets */ ++ for(i = 0; i < ARRAY_SIZE(ezx_dapm_widgets); i++) { ++ snd_soc_dapm_new_control(codec, &ezx_dapm_widgets[i]); ++ } ++ /* Set up ezx specific audio path interconnects */ ++ for(i = 0; audio_map[i][0] != NULL; i++) { ++ snd_soc_dapm_connect_input(codec, audio_map[i][0], audio_map[i][1], audio_map[i][2]); ++ } ++ ++ /* synchronise subsystem */ ++ snd_soc_dapm_sync_endpoints(codec); ++ return 0; ++} ++ ++static struct snd_soc_cpu_dai bp_dai = ++{ ++ .name = "Baseband", ++ .id = 0, ++ .type = SND_SOC_DAI_PCM, ++ .playback = { ++ .channels_min = 1, ++ .channels_max = 1, ++ .rates = SNDRV_PCM_RATE_8000, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE, ++ }, ++ .capture = { ++ .channels_min = 1, ++ .channels_max = 1, ++ .rates = SNDRV_PCM_RATE_8000, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE, ++ }, ++ .ops = { ++// .startup = bp_startup, ++// .shutdown = bp_shutdown, ++ .hw_params = bp_hw_params, ++// .hw_free = bp_hw_free, ++ }, ++}; ++ ++/* template digital audio interface glue - connects codec <--> CPU */ ++static struct snd_soc_dai_link ezx_dai[] = { ++{ ++ .name = "PCAP2 STEREO", ++ .stream_name = "stereo playback", ++ .cpu_dai = &pxa_ssp_dai[PXA2XX_DAI_SSP3], ++ .codec_dai = &pcap2_dai[PCAP2_STEREO_DAI], ++ .init = ezx_machine_init, ++ .ops = &ezx_ops, ++}, ++{ ++ .name = "PCAP2 MONO", ++ .stream_name = "mono playback", ++ .cpu_dai = &pxa_ssp_dai[PXA2XX_DAI_SSP3], ++ .codec_dai = &pcap2_dai[PCAP2_MONO_DAI], ++// .init = ezx_machine_init, /* the stereo call already registered our controls */ ++ .ops = &ezx_ops, ++}, ++{ ++ .name = "PCAP2 BP", ++ .stream_name = "BP Audio", ++ .cpu_dai = &bp_dai, ++ .codec_dai = &pcap2_dai[PCAP2_BP_DAI], ++}, ++}; ++ ++/* template audio machine driver */ ++static struct snd_soc_machine snd_soc_machine_ezx = { ++ .name = "Motorola EZX", ++// .probe ++// .remove ++// .suspend_pre ++// .resume_post ++ .dai_link = ezx_dai, ++ .num_links = ARRAY_SIZE(ezx_dai), ++}; ++ ++/* template audio subsystem */ ++static struct snd_soc_device ezx_snd_devdata = { ++ .machine = &snd_soc_machine_ezx, ++ .platform = &pxa2xx_soc_platform, ++ .codec_dev = &soc_codec_dev_pcap2, ++}; ++ ++static struct platform_device *ezx_snd_device; ++ ++static int __init ezx_init(void) ++{ ++ int ret; ++ ezx_snd_device = platform_device_alloc("soc-audio", -1); ++ if (!ezx_snd_device) ++ return -ENOMEM; ++ ++ platform_set_drvdata(ezx_snd_device, &ezx_snd_devdata); ++ ezx_snd_devdata.dev = &ezx_snd_device->dev; ++ ret = platform_device_add(ezx_snd_device); ++ ++ if (ret) ++ platform_device_put(ezx_snd_device); ++ /* configure gpio for ssp3 */ ++ pxa_gpio_mode(GPIO83_SFRM3_MD); /* SFRM */ ++ pxa_gpio_mode(GPIO81_STXD3_MD); /* TXD */ ++ pxa_gpio_mode(GPIO52_SCLK3_MD); /* SCLK */ ++ pxa_gpio_mode(GPIO89_SRXD3_MD); /* RXD */ ++ ++ /* configure gpio for ssp2 */ ++ pxa_gpio_mode(37 | GPIO_IN); /* SFRM */ ++ pxa_gpio_mode(38 | GPIO_IN); /* TXD */ ++ pxa_gpio_mode(22 | GPIO_IN); /* SCLK */ ++ pxa_gpio_mode(88 | GPIO_IN); /* RXD */ ++ ++ pxa_gpio_mode(GPIO_HW_ATTENUATE_A780 | GPIO_OUT); ++ pxa_gpio_set_value(GPIO_HW_ATTENUATE_A780, 1); ++ ++ /* request jack irq */ ++ request_irq(EZX_IRQ_HEADJACK, &jack_irq, IRQF_DISABLED, "headphone jack", NULL); ++ request_irq(EZX_IRQ_MIC, &jack_irq, IRQF_DISABLED, "mic jack", NULL); ++ ++ return ret; ++} ++ ++static void __exit ezx_exit(void) ++{ ++ free_irq(EZX_IRQ_HEADJACK, NULL); ++ free_irq(EZX_IRQ_MIC, NULL); ++ platform_device_unregister(ezx_snd_device); ++} ++ ++module_init(ezx_init); ++module_exit(ezx_exit); ++ +Index: linux-2.6.23/sound/soc/codecs/Makefile +=================================================================== +--- linux-2.6.23.orig/sound/soc/codecs/Makefile 2007-10-10 09:38:42.000000000 +0200 ++++ linux-2.6.23/sound/soc/codecs/Makefile 2007-10-22 22:30:09.000000000 +0200 +@@ -3,9 +3,11 @@ + snd-soc-wm8750-objs := wm8750.o + snd-soc-wm8753-objs := wm8753.o + snd-soc-wm9712-objs := wm9712.o ++snd-soc-pcap2-objs := pcap2.o + + obj-$(CONFIG_SND_SOC_AC97_CODEC) += snd-soc-ac97.o + obj-$(CONFIG_SND_SOC_WM8731) += snd-soc-wm8731.o + obj-$(CONFIG_SND_SOC_WM8750) += snd-soc-wm8750.o + obj-$(CONFIG_SND_SOC_WM8753) += snd-soc-wm8753.o + obj-$(CONFIG_SND_SOC_WM9712) += snd-soc-wm9712.o ++obj-$(CONFIG_SND_SOC_PCAP2) += snd-soc-pcap2.o +Index: linux-2.6.23/sound/soc/codecs/Kconfig +=================================================================== +--- linux-2.6.23.orig/sound/soc/codecs/Kconfig 2007-10-10 09:38:42.000000000 +0200 ++++ linux-2.6.23/sound/soc/codecs/Kconfig 2007-10-22 22:28:06.000000000 +0200 +@@ -17,3 +17,7 @@ + config SND_SOC_WM9712 + tristate + depends on SND_SOC ++ ++config SND_SOC_PCAP2 ++ tristate ++ depends on SND_SOC && EZX_PCAP +Index: linux-2.6.23/sound/soc/pxa/Makefile +=================================================================== +--- linux-2.6.23.orig/sound/soc/pxa/Makefile 2007-10-22 22:27:11.000000000 +0200 ++++ linux-2.6.23/sound/soc/pxa/Makefile 2007-10-22 22:28:06.000000000 +0200 +@@ -14,9 +14,10 @@ + snd-soc-poodle-objs := poodle.o + snd-soc-tosa-objs := tosa.o + snd-soc-spitz-objs := spitz.o ++snd-soc-ezx-objs := ezx.o + + obj-$(CONFIG_SND_PXA2XX_SOC_CORGI) += snd-soc-corgi.o + obj-$(CONFIG_SND_PXA2XX_SOC_POODLE) += snd-soc-poodle.o + obj-$(CONFIG_SND_PXA2XX_SOC_TOSA) += snd-soc-tosa.o + obj-$(CONFIG_SND_PXA2XX_SOC_SPITZ) += snd-soc-spitz.o +- ++obj-$(CONFIG_SND_PXA2XX_SOC_EZX) += snd-soc-ezx.o |