Index: blueangel.c =================================================================== RCS file: /cvs/linux/kernel26/sound/soc/pxa/blueangel.c,v retrieving revision 1.1 diff -u -r1.1 blueangel.c --- blueangel.c 6 Dec 2006 21:24:47 -0000 1.1 +++ blueangel.c 12 Apr 2007 20:48:01 -0000 @@ -1,13 +1,12 @@ /* - * SoC audio for HTC Blueangel + * SoC audio for HTC Magician + * + * Copyright (c) 2006 Philipp Zabel * * based on spitz.c, * Authors: Liam Girdwood * Richard Purdie * - * based on magician.c, - * Author: Luke Kenneth Casson Leighton - * * 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 @@ -15,12 +14,11 @@ * */ -#include #include -#include #include #include #include +#include #include #include #include @@ -30,91 +28,83 @@ #include #include #include -#include -#include +#include +#include #include #include "../codecs/uda1380.h" #include "pxa2xx-pcm.h" +#include "pxa2xx-i2s.h" +#include "pxa2xx-ssp.h" -#include -#include +#define BLUEANGEL_HP_OFF 0 +#define BLUEANGEL_HEADSET 1 +#define BLUEANGEL_HP 2 -#define BLUEANGEL_HP 0 -#define BLUEANGEL_MIC 1 -#define BLUEANGEL_LINE 2 -#define BLUEANGEL_HEADSET 3 -#define BLUEANGEL_HP_OFF 4 #define BLUEANGEL_SPK_ON 0 #define BLUEANGEL_SPK_OFF 1 -static int blueangel_jack_func = 0; -static int blueangel_spk_func = 0; +#define BLUEANGEL_MIC 0 +#define BLUEANGEL_MIC_EXT 1 +#define BLUEANGEL_BT_HS 2 -extern struct platform_device blueangel_asic3; +/* + * SSP GPIO's + */ +#define GPIO27_SSP_EXT_CLK (27 | GPIO_ALT_FN_1_IN) -static irqreturn_t earphone_isr(int irq, void *irq_desc) -{ - printk("earphone irq. GPIO99 = %d\n", GPLR(99) & GPIO_bit(99)); // EXT_MIC_DETECT, EARPHONE_DET -// printk("earphone irq. cpld18 = %d\n", blueangel_cpld18()); // is_earphone_connected - return IRQ_HANDLED; -} +static int blueangel_jack_func = BLUEANGEL_HP_OFF; +static int blueangel_spk_func = BLUEANGEL_SPK_ON; +static int blueangel_in_sel = BLUEANGEL_MIC; + +extern struct platform_device blueangel_asic3; static void blueangel_ext_control(struct snd_soc_codec *codec) { -printk("blueangel_ext_control(spk_func = %d, jack_func = %d)", blueangel_spk_func, blueangel_jack_func); - - asic3_set_gpio_out_b(&blueangel_asic3.dev, 1<<15, 1<<15); /* reset on? */ - udelay(15); - asic3_set_gpio_out_a(&blueangel_asic3.dev, 1<<15, 1<<15); /* power? */ - udelay(30); - /* now gpio 23 gets sent and also egpio14?? */ - udelay(15); - asic3_set_gpio_out_b(&blueangel_asic3.dev, 1<<15, 0); /* reset off? */ + asic3_set_gpio_out_b(&blueangel_asic3.dev, 1<private_data; + struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai; + struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai; + unsigned int acps, acds, div4; + int ret = 0; + + /* + * Rate = SSPSCLK / (word size(16)) + * SSPSCLK = (ACPS / ACDS) / SSPSCLKDIV(div4 or div1) + */ + switch (params_rate(params)) { + case 8000: + acps = 32842000; + acds = PXA2XX_SSP_CLK_AUDIO_DIV_32; /* wrong - 32 bits/sample */ + div4 = PXA2XX_SSP_CLK_SCDB_4; + break; + case 11025: + acps = 5622000; + acds = PXA2XX_SSP_CLK_AUDIO_DIV_8; /* 16 bits/sample, 1 slot */ + div4 = PXA2XX_SSP_CLK_SCDB_4; + break; + case 22050: + acps = 5622000; + acds = PXA2XX_SSP_CLK_AUDIO_DIV_4; + div4 = PXA2XX_SSP_CLK_SCDB_4; + break; + case 44100: + acps = 11345000; + acds = PXA2XX_SSP_CLK_AUDIO_DIV_4; + div4 = PXA2XX_SSP_CLK_SCDB_4; + break; + case 48000: + acps = 12235000; + acds = PXA2XX_SSP_CLK_AUDIO_DIV_4; + div4 = PXA2XX_SSP_CLK_SCDB_4; + break; + } + + /* set codec DAI configuration */ + ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_MSB | + SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); + if (ret < 0) + return ret; + + /* set cpu DAI configuration */ + ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_MSB | + SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); + if (ret < 0) + return ret; + + /* set audio clock as clock source */ + ret = cpu_dai->dai_ops.set_sysclk(cpu_dai, PXA2XX_SSP_CLK_AUDIO, 0, + SND_SOC_CLOCK_OUT); + if (ret < 0) + return ret; + + /* set the SSP audio system clock ACDS divider */ + ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai, + PXA2XX_SSP_AUDIO_DIV_ACDS, acds); + if (ret < 0) + return ret; + + /* set the SSP audio system clock SCDB divider4 */ + ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai, + PXA2XX_SSP_AUDIO_DIV_SCDB, div4); + if (ret < 0) + return ret; + + /* set SSP audio pll clock */ + ret = cpu_dai->dai_ops.set_pll(cpu_dai, 0, 0, acps); + if (ret < 0) + return ret; return 0; } -static struct snd_soc_ops blueangel_ops = { +/* + * We have to enable the SSP port early so the UDA1380 can flush + * it's register cache. The UDA1380 can only write it's interpolator and + * decimator registers when the link is running. + */ +static int blueangel_playback_prepare(struct snd_pcm_substream *substream) +{ + /* enable SSP clock - is this needed ? */ + SSCR0_P(1) |= SSCR0_SSE; + + /* FIXME: ENABLE I2S */ + SACR0 |= SACR0_BCKD; + SACR0 |= SACR0_ENB; + pxa_set_cken(CKEN8_I2S, 1); + + return 0; +} + +static int blueangel_playback_hw_free(struct snd_pcm_substream *substream) +{ + /* FIXME: DISABLE I2S */ + SACR0 &= ~SACR0_ENB; + SACR0 &= ~SACR0_BCKD; + pxa_set_cken(CKEN8_I2S, 0); + return 0; +} + +/* + * Magician uses I2S for capture. + */ +static int blueangel_capture_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_MSB | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); + if (ret < 0) + return ret; + + /* set cpu DAI configuration */ + ret = cpu_dai->dai_ops.set_fmt(cpu_dai, + SND_SOC_DAIFMT_MSB | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); + if (ret < 0) + return ret; + + /* set the I2S system clock as output */ + ret = cpu_dai->dai_ops.set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0, + SND_SOC_CLOCK_OUT); + if (ret < 0) + return ret; + + return 0; +} + +/* + * We have to enable the I2S port early so the UDA1380 can flush + * it's register cache. The UDA1380 can only write it's interpolator and + * decimator registers when the link is running. + */ +static int blueangel_capture_prepare(struct snd_pcm_substream *substream) +{ + SACR0 |= SACR0_ENB; + return 0; +} + +static struct snd_soc_ops blueangel_capture_ops = { .startup = blueangel_startup, - .shutdown = blueangel_shutdown, + .hw_params = blueangel_capture_hw_params, + .prepare = blueangel_capture_prepare, }; -static int blueangel_get_jack(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +static struct snd_soc_ops blueangel_playback_ops = { + .startup = blueangel_startup, + .hw_params = blueangel_playback_hw_params, + .prepare = blueangel_playback_prepare, + .hw_free = blueangel_playback_hw_free, +}; + +static int blueangel_get_jack(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) { ucontrol->value.integer.value[0] = blueangel_jack_func; return 0; } -static int blueangel_set_jack(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +static int blueangel_set_jack(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + + if (blueangel_jack_func == ucontrol->value.integer.value[0]) + return 0; + blueangel_jack_func = ucontrol->value.integer.value[0]; blueangel_ext_control(codec); - return 0; + return 1; } -static int blueangel_get_spk(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +static int blueangel_get_spk(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) { ucontrol->value.integer.value[0] = blueangel_spk_func; return 0; } -static int blueangel_set_spk(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +static int blueangel_set_spk(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + + if (blueangel_spk_func == ucontrol->value.integer.value[0]) + return 0; + blueangel_spk_func = ucontrol->value.integer.value[0]; blueangel_ext_control(codec); + return 1; +} + +static int blueangel_get_input(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + ucontrol->value.integer.value[0] = blueangel_in_sel; + return 0; +} + +static int blueangel_set_input(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + + if (blueangel_in_sel == ucontrol->value.integer.value[0]) + return 0; + + blueangel_in_sel = ucontrol->value.integer.value[0]; + + switch (blueangel_in_sel) { + case BLUEANGEL_MIC: + asic3_set_gpio_out_a(&blueangel_asic3.dev, 1<card, - snd_soc_cnew(&uda1380_blueangel_controls[i], codec, NULL))) < 0) + snd_soc_cnew(&uda1380_blueangel_controls[i], + codec, NULL))) < 0) return err; } /* Add blueangel specific widgets */ - for(i = 0; i < ARRAY_SIZE(uda1380_dapm_widgets); i++) { + for (i = 0; i < ARRAY_SIZE(uda1380_dapm_widgets); i++) { snd_soc_dapm_new_control(codec, &uda1380_dapm_widgets[i]); } /* Set up blueangel 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]); + 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_sync_endpoints(codec); return 0; } -unsigned int blueangel_config_sysclk(struct snd_soc_pcm_runtime *rtd, - struct snd_soc_clock_info *info) -{ - -printk("snd-soc-blueangel config-sysclk rate=%d, fs=%d\n", info->rate, info->fs); - - /* config pxa i2s or SSP0 as master */ - return rtd->cpu_dai->config_sysclk(rtd->cpu_dai, info, 0); -} - /* blueangel digital audio interface glue - connects codec <--> CPU */ static struct snd_soc_dai_link blueangel_dai[] = { { .name = "uda1380", .stream_name = "UDA1380 Playback", .cpu_dai = &pxa_ssp_dai[0], - .codec_dai = &uda1380_dai[1], + .codec_dai = &uda1380_dai[UDA1380_DAI_PLAYBACK], .init = blueangel_uda1380_init, - .config_sysclk = blueangel_config_sysclk, + .ops = &blueangel_playback_ops, }, { .name = "uda1380", .stream_name = "UDA1380 Capture", .cpu_dai = &pxa_i2s_dai, - .codec_dai = &uda1380_dai[2], - .config_sysclk = blueangel_config_sysclk, -}, + .codec_dai = &uda1380_dai[UDA1380_DAI_CAPTURE], + .ops = &blueangel_capture_ops, +} }; /* blueangel audio machine driver */ static struct snd_soc_machine snd_soc_machine_blueangel = { - .name = "Blueangel", + .name = "Magician", .dai_link = blueangel_dai, .num_links = ARRAY_SIZE(blueangel_dai), - .ops = &blueangel_ops, }; /* blueangel audio private data */ @@ -335,10 +514,16 @@ if (!machine_is_blueangel()) return -ENODEV; + + asic3_set_gpio_out_b(&blueangel_asic3.dev, 1<id + 1; switch (div_id) { +#ifdef CONFIG_PXA27x case PXA2XX_SSP_AUDIO_DIV_ACDS: SSACD_P(port) &= ~ 0x7; SSACD_P(port) |= SSACD_ACDS(div); @@ -293,6 +294,7 @@ if (div == PXA2XX_SSP_CLK_SCDB_1) SSACD_P(port) |= SSACD_SCDB; break; +#endif case PXA2XX_SSP_DIV_SCR: SSCR0_P(port) &= ~SSCR0_SCR; SSCR0_P(port) |= SSCR0_SerClkDiv(div); @@ -310,6 +312,7 @@ static int pxa2xx_ssp_set_dai_pll(struct snd_soc_cpu_dai *cpu_dai, int pll_id, unsigned int freq_in, unsigned int freq_out) { +#ifdef CONFIG_PXA27x int port = cpu_dai->id + 1; SSACD_P(port) &= ~0x70; @@ -332,6 +335,7 @@ SSACD_P(port) |= (0x5 << 4); break; } +#endif return 0; } @@ -341,6 +345,7 @@ static int pxa2xx_ssp_set_dai_tdm_slot(struct snd_soc_cpu_dai *cpu_dai, unsigned int mask, int slots) { +#ifdef CONFIG_PXA27x int port = cpu_dai->id + 1; SSCR0_P(port) &= ~SSCR0_SlotsPerFrm(7); @@ -351,6 +356,7 @@ /* set active slot mask */ SSTSA_P(port) = mask; SSRSA_P(port) = mask; +#endif return 0; }