diff options
Diffstat (limited to 'sound')
125 files changed, 3427 insertions, 1619 deletions
diff --git a/sound/arm/aaci.c b/sound/arm/aaci.c index dc78272fc39..1f0f8213e2d 100644 --- a/sound/arm/aaci.c +++ b/sound/arm/aaci.c @@ -937,6 +937,7 @@ static int __devinit aaci_probe_ac97(struct aaci *aaci) struct snd_ac97 *ac97; int ret; + writel(0, aaci->base + AC97_POWERDOWN); /* * Assert AACIRESET for 2us */ diff --git a/sound/core/pcm.c b/sound/core/pcm.c index 0c1440121c2..c69c60b2a48 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -953,11 +953,12 @@ static int snd_pcm_dev_register(struct snd_device *device) struct snd_pcm_substream *substream; struct snd_pcm_notify *notify; char str[16]; - struct snd_pcm *pcm = device->device_data; + struct snd_pcm *pcm; struct device *dev; - if (snd_BUG_ON(!pcm || !device)) + if (snd_BUG_ON(!device || !device->device_data)) return -ENXIO; + pcm = device->device_data; mutex_lock(®ister_mutex); err = snd_pcm_add(pcm); if (err) { diff --git a/sound/drivers/dummy.c b/sound/drivers/dummy.c index 6ba066c41d2..252e04ce602 100644 --- a/sound/drivers/dummy.c +++ b/sound/drivers/dummy.c @@ -165,7 +165,7 @@ MODULE_PARM_DESC(enable, "Enable this dummy soundcard."); module_param_array(pcm_devs, int, NULL, 0444); MODULE_PARM_DESC(pcm_devs, "PCM devices # (0-4) for dummy driver."); module_param_array(pcm_substreams, int, NULL, 0444); -MODULE_PARM_DESC(pcm_substreams, "PCM substreams # (1-16) for dummy driver."); +MODULE_PARM_DESC(pcm_substreams, "PCM substreams # (1-128) for dummy driver."); //module_param_array(midi_devs, int, NULL, 0444); //MODULE_PARM_DESC(midi_devs, "MIDI devices # (0-2) for dummy driver."); module_param(fake_buffer, bool, 0444); @@ -808,8 +808,6 @@ static int __devinit snd_card_dummy_new_mixer(struct snd_dummy *dummy) unsigned int idx; int err; - if (snd_BUG_ON(!dummy)) - return -EINVAL; spin_lock_init(&dummy->mixer_lock); strcpy(card->mixername, "Dummy Mixer"); diff --git a/sound/drivers/opl3/opl3_midi.c b/sound/drivers/opl3/opl3_midi.c index 6e7d09ae0e8..7d722a025d0 100644 --- a/sound/drivers/opl3/opl3_midi.c +++ b/sound/drivers/opl3/opl3_midi.c @@ -29,6 +29,8 @@ extern char snd_opl3_regmap[MAX_OPL2_VOICES][4]; extern int use_internal_drums; +static void snd_opl3_note_off_unsafe(void *p, int note, int vel, + struct snd_midi_channel *chan); /* * The next table looks magical, but it certainly is not. Its values have * been calculated as table[i]=8*log(i/64)/log(2) with an obvious exception @@ -242,16 +244,20 @@ void snd_opl3_timer_func(unsigned long data) int again = 0; int i; - spin_lock_irqsave(&opl3->sys_timer_lock, flags); + spin_lock_irqsave(&opl3->voice_lock, flags); for (i = 0; i < opl3->max_voices; i++) { struct snd_opl3_voice *vp = &opl3->voices[i]; if (vp->state > 0 && vp->note_off_check) { if (vp->note_off == jiffies) - snd_opl3_note_off(opl3, vp->note, 0, vp->chan); + snd_opl3_note_off_unsafe(opl3, vp->note, 0, + vp->chan); else again++; } } + spin_unlock_irqrestore(&opl3->voice_lock, flags); + + spin_lock_irqsave(&opl3->sys_timer_lock, flags); if (again) { opl3->tlist.expires = jiffies + 1; /* invoke again */ add_timer(&opl3->tlist); @@ -658,15 +664,14 @@ static void snd_opl3_kill_voice(struct snd_opl3 *opl3, int voice) /* * Release a note in response to a midi note off. */ -void snd_opl3_note_off(void *p, int note, int vel, struct snd_midi_channel *chan) +static void snd_opl3_note_off_unsafe(void *p, int note, int vel, + struct snd_midi_channel *chan) { struct snd_opl3 *opl3; int voice; struct snd_opl3_voice *vp; - unsigned long flags; - opl3 = p; #ifdef DEBUG_MIDI @@ -674,12 +679,9 @@ void snd_opl3_note_off(void *p, int note, int vel, struct snd_midi_channel *chan chan->number, chan->midi_program, note); #endif - spin_lock_irqsave(&opl3->voice_lock, flags); - if (opl3->synth_mode == SNDRV_OPL3_MODE_SEQ) { if (chan->drum_channel && use_internal_drums) { snd_opl3_drum_switch(opl3, note, vel, 0, chan); - spin_unlock_irqrestore(&opl3->voice_lock, flags); return; } /* this loop will hopefully kill all extra voices, because @@ -697,6 +699,16 @@ void snd_opl3_note_off(void *p, int note, int vel, struct snd_midi_channel *chan snd_opl3_kill_voice(opl3, voice); } } +} + +void snd_opl3_note_off(void *p, int note, int vel, + struct snd_midi_channel *chan) +{ + struct snd_opl3 *opl3 = p; + unsigned long flags; + + spin_lock_irqsave(&opl3->voice_lock, flags); + snd_opl3_note_off_unsafe(p, note, vel, chan); spin_unlock_irqrestore(&opl3->voice_lock, flags); } diff --git a/sound/drivers/pcsp/pcsp_lib.c b/sound/drivers/pcsp/pcsp_lib.c index 84cc2658c05..e1145ac6e90 100644 --- a/sound/drivers/pcsp/pcsp_lib.c +++ b/sound/drivers/pcsp/pcsp_lib.c @@ -39,25 +39,20 @@ static DECLARE_TASKLET(pcsp_pcm_tasklet, pcsp_call_pcm_elapsed, 0); /* write the port and returns the next expire time in ns; * called at the trigger-start and in hrtimer callback */ -static unsigned long pcsp_timer_update(struct hrtimer *handle) +static u64 pcsp_timer_update(struct snd_pcsp *chip) { unsigned char timer_cnt, val; u64 ns; struct snd_pcm_substream *substream; struct snd_pcm_runtime *runtime; - struct snd_pcsp *chip = container_of(handle, struct snd_pcsp, timer); unsigned long flags; if (chip->thalf) { outb(chip->val61, 0x61); chip->thalf = 0; - if (!atomic_read(&chip->timer_active)) - return 0; return chip->ns_rem; } - if (!atomic_read(&chip->timer_active)) - return 0; substream = chip->playback_substream; if (!substream) return 0; @@ -88,24 +83,17 @@ static unsigned long pcsp_timer_update(struct hrtimer *handle) return ns; } -enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle) +static void pcsp_pointer_update(struct snd_pcsp *chip) { - struct snd_pcsp *chip = container_of(handle, struct snd_pcsp, timer); struct snd_pcm_substream *substream; - int periods_elapsed, pointer_update; size_t period_bytes, buffer_bytes; - unsigned long ns; + int periods_elapsed; unsigned long flags; - pointer_update = !chip->thalf; - ns = pcsp_timer_update(handle); - if (!ns) - return HRTIMER_NORESTART; - /* update the playback position */ substream = chip->playback_substream; if (!substream) - return HRTIMER_NORESTART; + return; period_bytes = snd_pcm_lib_period_bytes(substream); buffer_bytes = snd_pcm_lib_buffer_bytes(substream); @@ -134,6 +122,26 @@ enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle) if (periods_elapsed) tasklet_schedule(&pcsp_pcm_tasklet); +} + +enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle) +{ + struct snd_pcsp *chip = container_of(handle, struct snd_pcsp, timer); + int pointer_update; + u64 ns; + + if (!atomic_read(&chip->timer_active) || !chip->playback_substream) + return HRTIMER_NORESTART; + + pointer_update = !chip->thalf; + ns = pcsp_timer_update(chip); + if (!ns) { + printk(KERN_WARNING "PCSP: unexpected stop\n"); + return HRTIMER_NORESTART; + } + + if (pointer_update) + pcsp_pointer_update(chip); hrtimer_forward(handle, hrtimer_get_expires(handle), ns_to_ktime(ns)); @@ -142,8 +150,6 @@ enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle) static int pcsp_start_playing(struct snd_pcsp *chip) { - unsigned long ns; - #if PCSP_DEBUG printk(KERN_INFO "PCSP: start_playing called\n"); #endif @@ -159,11 +165,7 @@ static int pcsp_start_playing(struct snd_pcsp *chip) atomic_set(&chip->timer_active, 1); chip->thalf = 0; - ns = pcsp_timer_update(&pcsp_chip.timer); - if (!ns) - return -EIO; - - hrtimer_start(&pcsp_chip.timer, ktime_set(0, ns), HRTIMER_MODE_REL); + hrtimer_start(&pcsp_chip.timer, ktime_set(0, 0), HRTIMER_MODE_REL); return 0; } @@ -232,21 +234,22 @@ static int snd_pcsp_playback_hw_free(struct snd_pcm_substream *substream) static int snd_pcsp_playback_prepare(struct snd_pcm_substream *substream) { struct snd_pcsp *chip = snd_pcm_substream_chip(substream); + pcsp_sync_stop(chip); + chip->playback_ptr = 0; + chip->period_ptr = 0; + chip->fmt_size = + snd_pcm_format_physical_width(substream->runtime->format) >> 3; + chip->is_signed = snd_pcm_format_signed(substream->runtime->format); #if PCSP_DEBUG printk(KERN_INFO "PCSP: prepare called, " - "size=%zi psize=%zi f=%zi f1=%i\n", + "size=%zi psize=%zi f=%zi f1=%i fsize=%i\n", snd_pcm_lib_buffer_bytes(substream), snd_pcm_lib_period_bytes(substream), snd_pcm_lib_buffer_bytes(substream) / snd_pcm_lib_period_bytes(substream), - substream->runtime->periods); + substream->runtime->periods, + chip->fmt_size); #endif - pcsp_sync_stop(chip); - chip->playback_ptr = 0; - chip->period_ptr = 0; - chip->fmt_size = - snd_pcm_format_physical_width(substream->runtime->format) >> 3; - chip->is_signed = snd_pcm_format_signed(substream->runtime->format); return 0; } diff --git a/sound/drivers/pcsp/pcsp_mixer.c b/sound/drivers/pcsp/pcsp_mixer.c index 199b0337714..903bc846763 100644 --- a/sound/drivers/pcsp/pcsp_mixer.c +++ b/sound/drivers/pcsp/pcsp_mixer.c @@ -72,7 +72,7 @@ static int pcsp_treble_put(struct snd_kcontrol *kcontrol, if (treble != chip->treble) { chip->treble = treble; #if PCSP_DEBUG - printk(KERN_INFO "PCSP: rate set to %i\n", PCSP_RATE()); + printk(KERN_INFO "PCSP: rate set to %li\n", PCSP_RATE()); #endif changed = 1; } diff --git a/sound/parisc/harmony.c b/sound/parisc/harmony.c index e924492df21..f47f9e226b0 100644 --- a/sound/parisc/harmony.c +++ b/sound/parisc/harmony.c @@ -624,6 +624,9 @@ snd_harmony_pcm_init(struct snd_harmony *h) struct snd_pcm *pcm; int err; + if (snd_BUG_ON(!h)) + return -EINVAL; + harmony_disable_interrupts(h); err = snd_pcm_new(h->card, "harmony", 0, 1, 1, &pcm); @@ -865,11 +868,12 @@ snd_harmony_mixer_reset(struct snd_harmony *h) static int __devinit snd_harmony_mixer_init(struct snd_harmony *h) { - struct snd_card *card = h->card; + struct snd_card *card; int idx, err; if (snd_BUG_ON(!h)) return -EINVAL; + card = h->card; strcpy(card->mixername, "Harmony Gain control interface"); for (idx = 0; idx < HARMONY_CONTROLS; idx++) { diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig index fb5ee3cc396..75c602b5b13 100644 --- a/sound/pci/Kconfig +++ b/sound/pci/Kconfig @@ -259,7 +259,6 @@ config SND_CS5530 config SND_CS5535AUDIO tristate "CS5535/CS5536 Audio" - depends on X86 && !X86_64 select SND_PCM select SND_AC97_CODEC help diff --git a/sound/pci/ali5451/ali5451.c b/sound/pci/ali5451/ali5451.c index b458d208720..aaf4da68969 100644 --- a/sound/pci/ali5451/ali5451.c +++ b/sound/pci/ali5451/ali5451.c @@ -973,7 +973,7 @@ static void snd_ali_free_voice(struct snd_ali * codec, void *private_data; snd_ali_printk("free_voice: channel=%d\n",pvoice->number); - if (pvoice == NULL || !pvoice->use) + if (!pvoice->use) return; snd_ali_clear_voices(codec, pvoice->number, pvoice->number); spin_lock_irq(&codec->voice_alloc); diff --git a/sound/pci/bt87x.c b/sound/pci/bt87x.c index 24585c6c6d0..4e2b925a94c 100644 --- a/sound/pci/bt87x.c +++ b/sound/pci/bt87x.c @@ -808,6 +808,8 @@ static struct pci_device_id snd_bt87x_ids[] = { BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_878, 0x1002, 0x0001, GENERIC), /* Leadtek Winfast tv 2000xp delux */ BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_878, 0x107d, 0x6606, GENERIC), + /* Pinnacle PCTV */ + BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_878, 0x11bd, 0x0012, GENERIC), /* Voodoo TV 200 */ BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_878, 0x121a, 0x3000, GENERIC), /* Askey Computer Corp. MagicTView'99 */ diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index c9ad182e1b4..e340792f6cb 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2674,6 +2674,7 @@ static struct pci_device_id azx_ids[] = { { PCI_DEVICE(0x10de, 0x044b), .driver_data = AZX_DRIVER_NVIDIA }, { PCI_DEVICE(0x10de, 0x055c), .driver_data = AZX_DRIVER_NVIDIA }, { PCI_DEVICE(0x10de, 0x055d), .driver_data = AZX_DRIVER_NVIDIA }, + { PCI_DEVICE(0x10de, 0x0590), .driver_data = AZX_DRIVER_NVIDIA }, { PCI_DEVICE(0x10de, 0x0774), .driver_data = AZX_DRIVER_NVIDIA }, { PCI_DEVICE(0x10de, 0x0775), .driver_data = AZX_DRIVER_NVIDIA }, { PCI_DEVICE(0x10de, 0x0776), .driver_data = AZX_DRIVER_NVIDIA }, diff --git a/sound/pci/hda/patch_nvhdmi.c b/sound/pci/hda/patch_nvhdmi.c index c8435c9a97f..9fb60276f5c 100644 --- a/sound/pci/hda/patch_nvhdmi.c +++ b/sound/pci/hda/patch_nvhdmi.c @@ -29,6 +29,9 @@ #include "hda_codec.h" #include "hda_local.h" +/* define below to restrict the supported rates and formats */ +/* #define LIMITED_RATE_FMT_SUPPORT */ + struct nvhdmi_spec { struct hda_multi_out multiout; @@ -60,6 +63,22 @@ static struct hda_verb nvhdmi_basic_init[] = { {} /* terminator */ }; +#ifdef LIMITED_RATE_FMT_SUPPORT +/* support only the safe format and rate */ +#define SUPPORTED_RATES SNDRV_PCM_RATE_48000 +#define SUPPORTED_MAXBPS 16 +#define SUPPORTED_FORMATS SNDRV_PCM_FMTBIT_S16_LE +#else +/* support all rates and formats */ +#define SUPPORTED_RATES \ + (SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |\ + SNDRV_PCM_RATE_192000) +#define SUPPORTED_MAXBPS 24 +#define SUPPORTED_FORMATS \ + (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE) +#endif + /* * Controls */ @@ -258,9 +277,9 @@ static struct hda_pcm_stream nvhdmi_pcm_digital_playback_8ch = { .channels_min = 2, .channels_max = 8, .nid = Nv_Master_Convert_nid, - .rates = SNDRV_PCM_RATE_48000, - .maxbps = 16, - .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rates = SUPPORTED_RATES, + .maxbps = SUPPORTED_MAXBPS, + .formats = SUPPORTED_FORMATS, .ops = { .open = nvhdmi_dig_playback_pcm_open, .close = nvhdmi_dig_playback_pcm_close_8ch, @@ -273,9 +292,9 @@ static struct hda_pcm_stream nvhdmi_pcm_digital_playback_2ch = { .channels_min = 2, .channels_max = 2, .nid = Nv_Master_Convert_nid, - .rates = SNDRV_PCM_RATE_48000, - .maxbps = 16, - .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rates = SUPPORTED_RATES, + .maxbps = SUPPORTED_MAXBPS, + .formats = SUPPORTED_FORMATS, .ops = { .open = nvhdmi_dig_playback_pcm_open, .close = nvhdmi_dig_playback_pcm_close_2ch, diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 7810d3dcad8..ff20048504b 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -275,7 +275,7 @@ struct alc_spec { struct snd_kcontrol_new *cap_mixer; /* capture mixer */ unsigned int beep_amp; /* beep amp value, set via set_beep_amp() */ - const struct hda_verb *init_verbs[5]; /* initialization verbs + const struct hda_verb *init_verbs[10]; /* initialization verbs * don't forget NULL * termination! */ @@ -965,6 +965,8 @@ static void alc_automute_pin(struct hda_codec *codec) unsigned int nid = spec->autocfg.hp_pins[0]; int i; + if (!nid) + return; pincap = snd_hda_query_pin_caps(codec, nid); if (pincap & AC_PINCAP_TRIG_REQ) /* need trigger? */ snd_hda_codec_read(codec, nid, 0, AC_VERB_SET_PIN_SENSE, 0); @@ -1332,15 +1334,20 @@ do_sku: * when the external headphone out jack is plugged" */ if (!spec->autocfg.hp_pins[0]) { + hda_nid_t nid; tmp = (ass >> 11) & 0x3; /* HP to chassis */ if (tmp == 0) - spec->autocfg.hp_pins[0] = porta; + nid = porta; else if (tmp == 1) - spec->autocfg.hp_pins[0] = porte; + nid = porte; else if (tmp == 2) - spec->autocfg.hp_pins[0] = portd; + nid = portd; else return 1; + for (i = 0; i < spec->autocfg.line_outs; i++) + if (spec->autocfg.line_out_pins[i] == nid) + return 1; + spec->autocfg.hp_pins[0] = nid; } alc_init_auto_hp(codec); @@ -1362,7 +1369,7 @@ static void alc_ssid_check(struct hda_codec *codec, } /* - * Fix-up pin default configurations + * Fix-up pin default configurations and add default verbs */ struct alc_pincfg { @@ -1370,9 +1377,14 @@ struct alc_pincfg { u32 val; }; -static void alc_fix_pincfg(struct hda_codec *codec, +struct alc_fixup { + const struct alc_pincfg *pins; + const struct hda_verb *verbs; +}; + +static void alc_pick_fixup(struct hda_codec *codec, const struct snd_pci_quirk *quirk, - const struct alc_pincfg **pinfix) + const struct alc_fixup *fix) { const struct alc_pincfg *cfg; @@ -1380,9 +1392,14 @@ static void alc_fix_pincfg(struct hda_codec *codec, if (!quirk) return; - cfg = pinfix[quirk->value]; - for (; cfg->nid; cfg++) - snd_hda_codec_set_pincfg(codec, cfg->nid, cfg->val); + fix += quirk->value; + cfg = fix->pins; + if (cfg) { + for (; cfg->nid; cfg++) + snd_hda_codec_set_pincfg(codec, cfg->nid, cfg->val); + } + if (fix->verbs) + add_verb(codec->spec, fix->verbs); } /* @@ -9593,11 +9610,13 @@ static struct alc_pincfg alc882_abit_aw9d_pinfix[] = { { } }; -static const struct alc_pincfg *alc882_pin_fixes[] = { - [PINFIX_ABIT_AW9D_MAX] = alc882_abit_aw9d_pinfix, +static const struct alc_fixup alc882_fixups[] = { + [PINFIX_ABIT_AW9D_MAX] = { + .pins = alc882_abit_aw9d_pinfix + }, }; -static struct snd_pci_quirk alc882_pinfix_tbl[] = { +static struct snd_pci_quirk alc882_fixup_tbl[] = { SND_PCI_QUIRK(0x147b, 0x107a, "Abit AW9D-MAX", PINFIX_ABIT_AW9D_MAX), {} }; @@ -9869,7 +9888,7 @@ static int patch_alc882(struct hda_codec *codec) board_config = ALC882_AUTO; } - alc_fix_pincfg(codec, alc882_pinfix_tbl, alc882_pin_fixes); + alc_pick_fixup(codec, alc882_fixup_tbl, alc882_fixups); if (board_config == ALC882_AUTO) { /* automatic parse from the BIOS config */ @@ -12585,7 +12604,8 @@ static struct snd_pci_quirk alc268_cfg_tbl[] = { SND_PCI_QUIRK(0x1025, 0x015b, "Acer Aspire One", ALC268_ACER_ASPIRE_ONE), SND_PCI_QUIRK(0x1028, 0x0253, "Dell OEM", ALC268_DELL), - SND_PCI_QUIRK(0x1028, 0x02b0, "Dell Inspiron Mini9", ALC268_DELL), + SND_PCI_QUIRK_MASK(0x1028, 0xfff0, 0x02b0, + "Dell Inspiron Mini9/Vostro A90", ALC268_DELL), /* almost compatible with toshiba but with optional digital outs; * auto-probing seems working fine */ @@ -12842,12 +12862,15 @@ static int patch_alc268(struct hda_codec *codec) unsigned int wcap = get_wcaps(codec, 0x07); int i; + spec->capsrc_nids = alc268_capsrc_nids; /* get type */ wcap = get_wcaps_type(wcap); if (spec->auto_mic || wcap != AC_WID_AUD_IN || spec->input_mux->num_items == 1) { spec->adc_nids = alc268_adc_nids_alt; spec->num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt); + if (spec->auto_mic) + fixup_automic_adc(codec); if (spec->auto_mic || spec->input_mux->num_items == 1) add_mixer(spec, alc268_capture_nosrc_mixer); else @@ -12857,7 +12880,6 @@ static int patch_alc268(struct hda_codec *codec) spec->num_adc_nids = ARRAY_SIZE(alc268_adc_nids); add_mixer(spec, alc268_capture_mixer); } - spec->capsrc_nids = alc268_capsrc_nids; /* set default input source */ for (i = 0; i < spec->num_adc_nids; i++) snd_hda_codec_write_cache(codec, alc268_capsrc_nids[i], @@ -14357,15 +14379,16 @@ static void alc861_auto_init_multi_out(struct hda_codec *codec) static void alc861_auto_init_hp_out(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - hda_nid_t pin; - pin = spec->autocfg.hp_pins[0]; - if (pin) - alc861_auto_set_output_and_unmute(codec, pin, PIN_HP, + if (spec->autocfg.hp_outs) + alc861_auto_set_output_and_unmute(codec, + spec->autocfg.hp_pins[0], + PIN_HP, spec->multiout.hp_nid); - pin = spec->autocfg.speaker_pins[0]; - if (pin) - alc861_auto_set_output_and_unmute(codec, pin, PIN_OUT, + if (spec->autocfg.speaker_outs) + alc861_auto_set_output_and_unmute(codec, + spec->autocfg.speaker_pins[0], + PIN_OUT, spec->multiout.dac_nids[0]); } @@ -15158,7 +15181,7 @@ static struct snd_pci_quirk alc861vd_cfg_tbl[] = { SND_PCI_QUIRK(0x1019, 0xa88d, "Realtek ALC660 demo", ALC660VD_3ST), SND_PCI_QUIRK(0x103c, 0x30bf, "HP TX1000", ALC861VD_HP), SND_PCI_QUIRK(0x1043, 0x12e2, "Asus z35m", ALC660VD_3ST), - SND_PCI_QUIRK(0x1043, 0x1339, "Asus G1", ALC660VD_3ST), + /*SND_PCI_QUIRK(0x1043, 0x1339, "Asus G1", ALC660VD_3ST),*/ /* auto */ SND_PCI_QUIRK(0x1043, 0x1633, "Asus V1Sn", ALC660VD_ASUS_V1S), SND_PCI_QUIRK(0x1043, 0x81e7, "ASUS", ALC660VD_3ST_DIG), SND_PCI_QUIRK(0x10de, 0x03f0, "Realtek ALC660 demo", ALC660VD_3ST), @@ -15551,6 +15574,29 @@ static void alc861vd_auto_init(struct hda_codec *codec) alc_inithook(codec); } +enum { + ALC660VD_FIX_ASUS_GPIO1 +}; + +/* reset GPIO1 */ +static const struct hda_verb alc660vd_fix_asus_gpio1_verbs[] = { + {0x01, AC_VERB_SET_GPIO_MASK, 0x03}, + {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01}, + {0x01, AC_VERB_SET_GPIO_DATA, 0x01}, + { } +}; + +static const struct alc_fixup alc861vd_fixups[] = { + [ALC660VD_FIX_ASUS_GPIO1] = { + .verbs = alc660vd_fix_asus_gpio1_verbs, + }, +}; + +static struct snd_pci_quirk alc861vd_fixup_tbl[] = { + SND_PCI_QUIRK(0x1043, 0x1339, "ASUS A7-K", ALC660VD_FIX_ASUS_GPIO1), + {} +}; + static int patch_alc861vd(struct hda_codec *codec) { struct alc_spec *spec; @@ -15572,6 +15618,8 @@ static int patch_alc861vd(struct hda_codec *codec) board_config = ALC861VD_AUTO; } + alc_pick_fixup(codec, alc861vd_fixup_tbl, alc861vd_fixups); + if (board_config == ALC861VD_AUTO) { /* automatic parse from the BIOS config */ err = alc861vd_parse_auto_config(codec); @@ -17329,7 +17377,7 @@ static int alc662_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin, /* create playback/capture controls for input pins */ #define alc662_auto_create_input_ctls \ - alc880_auto_create_input_ctls + alc882_auto_create_input_ctls static void alc662_auto_set_output_and_unmute(struct hda_codec *codec, hda_nid_t nid, int pin_type, diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index a9b26828a65..66c0876bf73 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -158,6 +158,7 @@ enum { STAC_D965_5ST_NO_FP, STAC_DELL_3ST, STAC_DELL_BIOS, + STAC_927X_VOLKNOB, STAC_927X_MODELS }; @@ -907,6 +908,16 @@ static struct hda_verb d965_core_init[] = { {} }; +static struct hda_verb dell_3st_core_init[] = { + /* don't set delta bit */ + {0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0x7f}, + /* unmute node 0x1b */ + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, + /* select node 0x03 as DAC */ + {0x0b, AC_VERB_SET_CONNECT_SEL, 0x01}, + {} +}; + static struct hda_verb stac927x_core_init[] = { /* set master volume and direct control */ { 0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, @@ -915,6 +926,14 @@ static struct hda_verb stac927x_core_init[] = { {} }; +static struct hda_verb stac927x_volknob_core_init[] = { + /* don't set delta bit */ + {0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0x7f}, + /* enable analog pc beep path */ + {0x01, AC_VERB_SET_DIGI_CONVERT_2, 1 << 5}, + {} +}; + static struct hda_verb stac9205_core_init[] = { /* set master volume and direct control */ { 0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, @@ -1999,6 +2018,7 @@ static unsigned int *stac927x_brd_tbl[STAC_927X_MODELS] = { [STAC_D965_5ST_NO_FP] = d965_5st_no_fp_pin_configs, [STAC_DELL_3ST] = dell_3st_pin_configs, [STAC_DELL_BIOS] = NULL, + [STAC_927X_VOLKNOB] = NULL, }; static const char *stac927x_models[STAC_927X_MODELS] = { @@ -2010,6 +2030,7 @@ static const char *stac927x_models[STAC_927X_MODELS] = { [STAC_D965_5ST_NO_FP] = "5stack-no-fp", [STAC_DELL_3ST] = "dell-3stack", [STAC_DELL_BIOS] = "dell-bios", + [STAC_927X_VOLKNOB] = "volknob", }; static struct snd_pci_quirk stac927x_cfg_tbl[] = { @@ -2045,6 +2066,8 @@ static struct snd_pci_quirk stac927x_cfg_tbl[] = { "Intel D965", STAC_D965_5ST), SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_INTEL, 0xff00, 0x2500, "Intel D965", STAC_D965_5ST), + /* volume-knob fixes */ + SND_PCI_QUIRK_VENDOR(0x10cf, "FSC", STAC_927X_VOLKNOB), {} /* terminator */ }; @@ -5612,10 +5635,14 @@ static int patch_stac927x(struct hda_codec *codec) spec->dmic_nids = stac927x_dmic_nids; spec->num_dmics = STAC927X_NUM_DMICS; - spec->init = d965_core_init; + spec->init = dell_3st_core_init; spec->dmux_nids = stac927x_dmux_nids; spec->num_dmuxes = ARRAY_SIZE(stac927x_dmux_nids); break; + case STAC_927X_VOLKNOB: + spec->num_dmics = 0; + spec->init = stac927x_volknob_core_init; + break; default: spec->num_dmics = 0; spec->init = stac927x_core_init; diff --git a/sound/pci/ice1712/amp.c b/sound/pci/ice1712/amp.c index 37564300b50..6da21a2bcad 100644 --- a/sound/pci/ice1712/amp.c +++ b/sound/pci/ice1712/amp.c @@ -52,11 +52,13 @@ static int __devinit snd_vt1724_amp_init(struct snd_ice1712 *ice) /* only use basic functionality for now */ - ice->num_total_dacs = 2; /* only PSDOUT0 is connected */ + /* VT1616 6ch codec connected to PSDOUT0 using packed mode */ + ice->num_total_dacs = 6; ice->num_total_adcs = 2; - /* Chaintech AV-710 has another codecs, which need initialization */ - /* initialize WM8728 codec */ + /* Chaintech AV-710 has another WM8728 codec connected to PSDOUT4 + (shared with the SPDIF output). Mixer control for this codec + is not yet supported. */ if (ice->eeprom.subvendor == VT1724_SUBDEVICE_AV710) { for (i = 0; i < ARRAY_SIZE(wm_inits); i += 2) wm_put(ice, wm_inits[i], wm_inits[i+1]); diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c index cecf1ffeeaa..d74033a2cfb 100644 --- a/sound/pci/ice1712/ice1712.c +++ b/sound/pci/ice1712/ice1712.c @@ -2259,7 +2259,7 @@ static int snd_ice1712_pro_peak_get(struct snd_kcontrol *kcontrol, } static struct snd_kcontrol_new snd_ice1712_mixer_pro_peak __devinitdata = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = "Multi Track Peak", .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, .info = snd_ice1712_pro_peak_info, diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c index af6e0014862..10fc92c0557 100644 --- a/sound/pci/ice1712/ice1724.c +++ b/sound/pci/ice1712/ice1724.c @@ -648,7 +648,7 @@ static int snd_vt1724_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate, (inb(ICEMT1724(ice, DMA_PAUSE)) & DMA_PAUSES)) { /* running? we cannot change the rate now... */ spin_unlock_irqrestore(&ice->reg_lock, flags); - return -EBUSY; + return ((rate == ice->cur_rate) && !force) ? 0 : -EBUSY; } if (!force && is_pro_rate_locked(ice)) { spin_unlock_irqrestore(&ice->reg_lock, flags); @@ -1294,7 +1294,7 @@ static int __devinit snd_vt1724_pcm_spdif(struct snd_ice1712 *ice, int device) snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(ice->pci), - 64*1024, 64*1024); + 256*1024, 256*1024); ice->pcm = pcm; @@ -1408,7 +1408,7 @@ static int __devinit snd_vt1724_pcm_indep(struct snd_ice1712 *ice, int device) snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(ice->pci), - 64*1024, 64*1024); + 256*1024, 256*1024); ice->pcm_ds = pcm; @@ -2110,7 +2110,7 @@ static int snd_vt1724_pro_peak_get(struct snd_kcontrol *kcontrol, } static struct snd_kcontrol_new snd_vt1724_mixer_pro_peak __devinitdata = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = "Multi Track Peak", .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, .info = snd_vt1724_pro_peak_info, diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c index acfa4760da4..8a332d2f615 100644 --- a/sound/pci/via82xx.c +++ b/sound/pci/via82xx.c @@ -386,6 +386,7 @@ struct via82xx { struct snd_pcm *pcms[2]; struct snd_rawmidi *rmidi; + struct snd_kcontrol *dxs_controls[4]; struct snd_ac97_bus *ac97_bus; struct snd_ac97 *ac97; @@ -1216,9 +1217,9 @@ static int snd_via82xx_pcm_open(struct via82xx *chip, struct viadev *viadev, /* - * open callback for playback on via686 and via823x DSX + * open callback for playback on via686 */ -static int snd_via82xx_playback_open(struct snd_pcm_substream *substream) +static int snd_via686_playback_open(struct snd_pcm_substream *substream) { struct via82xx *chip = snd_pcm_substream_chip(substream); struct viadev *viadev = &chip->devs[chip->playback_devno + substream->number]; @@ -1230,6 +1231,32 @@ static int snd_via82xx_playback_open(struct snd_pcm_substream *substream) } /* + * open callback for playback on via823x DXS + */ +static int snd_via8233_playback_open(struct snd_pcm_substream *substream) +{ + struct via82xx *chip = snd_pcm_substream_chip(substream); + struct viadev *viadev; + unsigned int stream; + int err; + + viadev = &chip->devs[chip->playback_devno + substream->number]; + if ((err = snd_via82xx_pcm_open(chip, viadev, substream)) < 0) + return err; + stream = viadev->reg_offset / 0x10; + if (chip->dxs_controls[stream]) { + chip->playback_volume[stream][0] = 0; + chip->playback_volume[stream][1] = 0; + chip->dxs_controls[stream]->vd[0].access &= + ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE | + SNDRV_CTL_EVENT_MASK_INFO, + &chip->dxs_controls[stream]->id); + } + return 0; +} + +/* * open callback for playback on via823x multi-channel */ static int snd_via8233_multi_open(struct snd_pcm_substream *substream) @@ -1302,10 +1329,26 @@ static int snd_via82xx_pcm_close(struct snd_pcm_substream *substream) return 0; } +static int snd_via8233_playback_close(struct snd_pcm_substream *substream) +{ + struct via82xx *chip = snd_pcm_substream_chip(substream); + struct viadev *viadev = substream->runtime->private_data; + unsigned int stream; + + stream = viadev->reg_offset / 0x10; + if (chip->dxs_controls[stream]) { + chip->dxs_controls[stream]->vd[0].access |= + SNDRV_CTL_ELEM_ACCESS_INACTIVE; + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_INFO, + &chip->dxs_controls[stream]->id); + } + return snd_via82xx_pcm_close(substream); +} + /* via686 playback callbacks */ static struct snd_pcm_ops snd_via686_playback_ops = { - .open = snd_via82xx_playback_open, + .open = snd_via686_playback_open, .close = snd_via82xx_pcm_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_via82xx_hw_params, @@ -1331,8 +1374,8 @@ static struct snd_pcm_ops snd_via686_capture_ops = { /* via823x DSX playback callbacks */ static struct snd_pcm_ops snd_via8233_playback_ops = { - .open = snd_via82xx_playback_open, - .close = snd_via82xx_pcm_close, + .open = snd_via8233_playback_open, + .close = snd_via8233_playback_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_via82xx_hw_params, .hw_free = snd_via82xx_hw_free, @@ -1626,7 +1669,7 @@ static int snd_via8233_dxs_volume_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct via82xx *chip = snd_kcontrol_chip(kcontrol); - unsigned int idx = snd_ctl_get_ioff(kcontrol, &ucontrol->id); + unsigned int idx = kcontrol->id.subdevice; ucontrol->value.integer.value[0] = VIA_DXS_MAX_VOLUME - chip->playback_volume[idx][0]; ucontrol->value.integer.value[1] = VIA_DXS_MAX_VOLUME - chip->playback_volume[idx][1]; @@ -1646,7 +1689,7 @@ static int snd_via8233_dxs_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct via82xx *chip = snd_kcontrol_chip(kcontrol); - unsigned int idx = snd_ctl_get_ioff(kcontrol, &ucontrol->id); + unsigned int idx = kcontrol->id.subdevice; unsigned long port = chip->port + 0x10 * idx; unsigned char val; int i, change = 0; @@ -1705,11 +1748,13 @@ static struct snd_kcontrol_new snd_via8233_pcmdxs_volume_control __devinitdata = }; static struct snd_kcontrol_new snd_via8233_dxs_volume_control __devinitdata = { - .name = "VIA DXS Playback Volume", - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | - SNDRV_CTL_ELEM_ACCESS_TLV_READ), - .count = 4, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .device = 0, + /* .subdevice set later */ + .name = "PCM Playback Volume", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ | + SNDRV_CTL_ELEM_ACCESS_INACTIVE, .info = snd_via8233_dxs_volume_info, .get = snd_via8233_dxs_volume_get, .put = snd_via8233_dxs_volume_put, @@ -1936,10 +1981,19 @@ static int __devinit snd_via8233_init_misc(struct via82xx *chip) } else /* Using DXS when PCM emulation is enabled is really weird */ { - /* Standalone DXS controls */ - err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_via8233_dxs_volume_control, chip)); - if (err < 0) - return err; + for (i = 0; i < 4; ++i) { + struct snd_kcontrol *kctl; + + kctl = snd_ctl_new1( + &snd_via8233_dxs_volume_control, chip); + if (!kctl) + return -ENOMEM; + kctl->id.subdevice = i; + err = snd_ctl_add(chip->card, kctl); + if (err < 0) + return err; + chip->dxs_controls[i] = kctl; + } } } /* select spdif data slot 10/11 */ diff --git a/sound/pcmcia/pdaudiocf/pdaudiocf.c b/sound/pcmcia/pdaudiocf/pdaudiocf.c index 7dea74b71cf..64b859925c0 100644 --- a/sound/pcmcia/pdaudiocf/pdaudiocf.c +++ b/sound/pcmcia/pdaudiocf/pdaudiocf.c @@ -217,20 +217,25 @@ static void snd_pdacf_detach(struct pcmcia_device *link) * configuration callback */ -#define CS_CHECK(fn, ret) \ -do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0) - static int pdacf_config(struct pcmcia_device *link) { struct snd_pdacf *pdacf = link->priv; - int last_fn, last_ret; + int ret; snd_printdd(KERN_DEBUG "pdacf_config called\n"); link->conf.ConfigIndex = 0x5; - CS_CHECK(RequestIO, pcmcia_request_io(link, &link->io)); - CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq)); - CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf)); + ret = pcmcia_request_io(link, &link->io); + if (ret) + goto failed; + + ret = pcmcia_request_irq(link, &link->irq); + if (ret) + goto failed; + + ret = pcmcia_request_configuration(link, &link->conf); + if (ret) + goto failed; if (snd_pdacf_assign_resources(pdacf, link->io.BasePort1, link->irq.AssignedIRQ) < 0) goto failed; @@ -238,8 +243,6 @@ static int pdacf_config(struct pcmcia_device *link) link->dev_node = &pdacf->node; return 0; -cs_failed: - cs_error(link, last_fn, last_ret); failed: pcmcia_disable_device(link); return -ENODEV; diff --git a/sound/pcmcia/vx/vxpocket.c b/sound/pcmcia/vx/vxpocket.c index 7445cc8a47d..1492744ad67 100644 --- a/sound/pcmcia/vx/vxpocket.c +++ b/sound/pcmcia/vx/vxpocket.c @@ -213,14 +213,11 @@ static int snd_vxpocket_assign_resources(struct vx_core *chip, int port, int irq * configuration callback */ -#define CS_CHECK(fn, ret) \ -do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0) - static int vxpocket_config(struct pcmcia_device *link) { struct vx_core *chip = link->priv; struct snd_vxpocket *vxp = (struct snd_vxpocket *)chip; - int last_fn, last_ret; + int ret; snd_printdd(KERN_DEBUG "vxpocket_config called\n"); @@ -235,9 +232,17 @@ static int vxpocket_config(struct pcmcia_device *link) strcpy(chip->card->driver, vxp440_hw.name); } - CS_CHECK(RequestIO, pcmcia_request_io(link, &link->io)); - CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq)); - CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf)); + ret = pcmcia_request_io(link, &link->io); + if (ret) + goto failed; + + ret = pcmcia_request_irq(link, &link->irq); + if (ret) + goto failed; + + ret = pcmcia_request_configuration(link, &link->conf); + if (ret) + goto failed; chip->dev = &handle_to_dev(link); snd_card_set_dev(chip->card, chip->dev); @@ -248,8 +253,6 @@ static int vxpocket_config(struct pcmcia_device *link) link->dev_node = &vxp->node; return 0; -cs_failed: - cs_error(link, last_fn, last_ret); failed: pcmcia_disable_device(link); return -ENODEV; diff --git a/sound/ppc/Kconfig b/sound/ppc/Kconfig index bd2338ab2ce..0519c60f5be 100644 --- a/sound/ppc/Kconfig +++ b/sound/ppc/Kconfig @@ -2,7 +2,7 @@ menuconfig SND_PPC bool "PowerPC sound devices" - depends on PPC64 || PPC32 + depends on PPC default y help Support for sound devices specific to PowerPC architectures. diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 0c5eac01bf2..1470141d416 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -1,4 +1,4 @@ -snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o soc-cache.o +snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o soc-cache.o soc-utils.o obj-$(CONFIG_SND_SOC) += snd-soc-core.o obj-$(CONFIG_SND_SOC) += codecs/ diff --git a/sound/soc/au1x/dbdma2.c b/sound/soc/au1x/dbdma2.c index 594c6c5b783..fe9f4657c95 100644 --- a/sound/soc/au1x/dbdma2.c +++ b/sound/soc/au1x/dbdma2.c @@ -2,7 +2,7 @@ * Au12x0/Au1550 PSC ALSA ASoC audio support. * * (c) 2007-2008 MSC Vertriebsges.m.b.H., - * Manuel Lauss <mano@roarinelk.homelinux.net> + * Manuel Lauss <manuel.lauss@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 @@ -333,6 +333,30 @@ static int au1xpsc_pcm_new(struct snd_card *card, static int au1xpsc_pcm_probe(struct platform_device *pdev) { + if (!au1xpsc_audio_pcmdma[PCM_TX] || !au1xpsc_audio_pcmdma[PCM_RX]) + return -ENODEV; + + return 0; +} + +static int au1xpsc_pcm_remove(struct platform_device *pdev) +{ + return 0; +} + +/* au1xpsc audio platform */ +struct snd_soc_platform au1xpsc_soc_platform = { + .name = "au1xpsc-pcm-dbdma", + .probe = au1xpsc_pcm_probe, + .remove = au1xpsc_pcm_remove, + .pcm_ops = &au1xpsc_pcm_ops, + .pcm_new = au1xpsc_pcm_new, + .pcm_free = au1xpsc_pcm_free_dma_buffers, +}; +EXPORT_SYMBOL_GPL(au1xpsc_soc_platform); + +static int __devinit au1xpsc_pcm_drvprobe(struct platform_device *pdev) +{ struct resource *r; int ret; @@ -365,7 +389,9 @@ static int au1xpsc_pcm_probe(struct platform_device *pdev) } (au1xpsc_audio_pcmdma[PCM_RX])->ddma_id = r->start; - return 0; + ret = snd_soc_register_platform(&au1xpsc_soc_platform); + if (!ret) + return ret; out2: kfree(au1xpsc_audio_pcmdma[PCM_RX]); @@ -376,10 +402,12 @@ out1: return ret; } -static int au1xpsc_pcm_remove(struct platform_device *pdev) +static int __devexit au1xpsc_pcm_drvremove(struct platform_device *pdev) { int i; + snd_soc_unregister_platform(&au1xpsc_soc_platform); + for (i = 0; i < 2; i++) { if (au1xpsc_audio_pcmdma[i]) { au1x_pcm_dbdma_free(au1xpsc_audio_pcmdma[i]); @@ -391,32 +419,83 @@ static int au1xpsc_pcm_remove(struct platform_device *pdev) return 0; } -/* au1xpsc audio platform */ -struct snd_soc_platform au1xpsc_soc_platform = { - .name = "au1xpsc-pcm-dbdma", - .probe = au1xpsc_pcm_probe, - .remove = au1xpsc_pcm_remove, - .pcm_ops = &au1xpsc_pcm_ops, - .pcm_new = au1xpsc_pcm_new, - .pcm_free = au1xpsc_pcm_free_dma_buffers, +static struct platform_driver au1xpsc_pcm_driver = { + .driver = { + .name = "au1xpsc-pcm", + .owner = THIS_MODULE, + }, + .probe = au1xpsc_pcm_drvprobe, + .remove = __devexit_p(au1xpsc_pcm_drvremove), }; -EXPORT_SYMBOL_GPL(au1xpsc_soc_platform); -static int __init au1xpsc_audio_dbdma_init(void) +static int __init au1xpsc_audio_dbdma_load(void) { au1xpsc_audio_pcmdma[PCM_TX] = NULL; au1xpsc_audio_pcmdma[PCM_RX] = NULL; - return snd_soc_register_platform(&au1xpsc_soc_platform); + return platform_driver_register(&au1xpsc_pcm_driver); } -static void __exit au1xpsc_audio_dbdma_exit(void) +static void __exit au1xpsc_audio_dbdma_unload(void) { - snd_soc_unregister_platform(&au1xpsc_soc_platform); + platform_driver_unregister(&au1xpsc_pcm_driver); +} + +module_init(au1xpsc_audio_dbdma_load); +module_exit(au1xpsc_audio_dbdma_unload); + + +struct platform_device *au1xpsc_pcm_add(struct platform_device *pdev) +{ + struct resource *res, *r; + struct platform_device *pd; + int id[2]; + int ret; + + r = platform_get_resource(pdev, IORESOURCE_DMA, 0); + if (!r) + return NULL; + id[0] = r->start; + + r = platform_get_resource(pdev, IORESOURCE_DMA, 1); + if (!r) + return NULL; + id[1] = r->start; + + res = kzalloc(sizeof(struct resource) * 2, GFP_KERNEL); + if (!res) + return NULL; + + res[0].start = res[0].end = id[0]; + res[1].start = res[1].end = id[1]; + res[0].flags = res[1].flags = IORESOURCE_DMA; + + pd = platform_device_alloc("au1xpsc-pcm", -1); + if (!pd) + goto out; + + pd->resource = res; + pd->num_resources = 2; + + ret = platform_device_add(pd); + if (!ret) + return pd; + +out: + kfree(res); + return NULL; } +EXPORT_SYMBOL_GPL(au1xpsc_pcm_add); -module_init(au1xpsc_audio_dbdma_init); -module_exit(au1xpsc_audio_dbdma_exit); +void au1xpsc_pcm_destroy(struct platform_device *dmapd) +{ + if (dmapd) { + kfree(dmapd->resource); + dmapd->resource = NULL; + platform_device_unregister(dmapd); + } +} +EXPORT_SYMBOL_GPL(au1xpsc_pcm_destroy); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Au12x0/Au1550 PSC Audio DMA driver"); -MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>"); +MODULE_AUTHOR("Manuel Lauss"); diff --git a/sound/soc/au1x/psc-ac97.c b/sound/soc/au1x/psc-ac97.c index 2a06a9c548a..340311d7fed 100644 --- a/sound/soc/au1x/psc-ac97.c +++ b/sound/soc/au1x/psc-ac97.c @@ -317,19 +317,55 @@ static int au1xpsc_ac97_trigger(struct snd_pcm_substream *substream, static int au1xpsc_ac97_probe(struct platform_device *pdev, struct snd_soc_dai *dai) { + return au1xpsc_ac97_workdata ? 0 : -ENODEV; +} + +static void au1xpsc_ac97_remove(struct platform_device *pdev, + struct snd_soc_dai *dai) +{ +} + +static struct snd_soc_dai_ops au1xpsc_ac97_dai_ops = { + .trigger = au1xpsc_ac97_trigger, + .hw_params = au1xpsc_ac97_hw_params, +}; + +struct snd_soc_dai au1xpsc_ac97_dai = { + .name = "au1xpsc_ac97", + .ac97_control = 1, + .probe = au1xpsc_ac97_probe, + .remove = au1xpsc_ac97_remove, + .playback = { + .rates = AC97_RATES, + .formats = AC97_FMTS, + .channels_min = 2, + .channels_max = 2, + }, + .capture = { + .rates = AC97_RATES, + .formats = AC97_FMTS, + .channels_min = 2, + .channels_max = 2, + }, + .ops = &au1xpsc_ac97_dai_ops, +}; +EXPORT_SYMBOL_GPL(au1xpsc_ac97_dai); + +static int __devinit au1xpsc_ac97_drvprobe(struct platform_device *pdev) +{ int ret; struct resource *r; unsigned long sel; + struct au1xpsc_audio_data *wd; if (au1xpsc_ac97_workdata) return -EBUSY; - au1xpsc_ac97_workdata = - kzalloc(sizeof(struct au1xpsc_audio_data), GFP_KERNEL); - if (!au1xpsc_ac97_workdata) + wd = kzalloc(sizeof(struct au1xpsc_audio_data), GFP_KERNEL); + if (!wd) return -ENOMEM; - mutex_init(&au1xpsc_ac97_workdata->lock); + mutex_init(&wd->lock); r = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!r) { @@ -338,81 +374,95 @@ static int au1xpsc_ac97_probe(struct platform_device *pdev, } ret = -EBUSY; - au1xpsc_ac97_workdata->ioarea = - request_mem_region(r->start, r->end - r->start + 1, + wd->ioarea = request_mem_region(r->start, r->end - r->start + 1, "au1xpsc_ac97"); - if (!au1xpsc_ac97_workdata->ioarea) + if (!wd->ioarea) goto out0; - au1xpsc_ac97_workdata->mmio = ioremap(r->start, 0xffff); - if (!au1xpsc_ac97_workdata->mmio) + wd->mmio = ioremap(r->start, 0xffff); + if (!wd->mmio) goto out1; /* configuration: max dma trigger threshold, enable ac97 */ - au1xpsc_ac97_workdata->cfg = PSC_AC97CFG_RT_FIFO8 | - PSC_AC97CFG_TT_FIFO8 | - PSC_AC97CFG_DE_ENABLE; + wd->cfg = PSC_AC97CFG_RT_FIFO8 | PSC_AC97CFG_TT_FIFO8 | + PSC_AC97CFG_DE_ENABLE; - /* preserve PSC clock source set up by platform (dev.platform_data - * is already occupied by soc layer) - */ - sel = au_readl(PSC_SEL(au1xpsc_ac97_workdata)) & PSC_SEL_CLK_MASK; - au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_ac97_workdata)); + /* preserve PSC clock source set up by platform */ + sel = au_readl(PSC_SEL(wd)) & PSC_SEL_CLK_MASK; + au_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd)); au_sync(); - au_writel(0, PSC_SEL(au1xpsc_ac97_workdata)); + au_writel(0, PSC_SEL(wd)); au_sync(); - au_writel(PSC_SEL_PS_AC97MODE | sel, PSC_SEL(au1xpsc_ac97_workdata)); + au_writel(PSC_SEL_PS_AC97MODE | sel, PSC_SEL(wd)); au_sync(); - /* next up: cold reset. Dont check for PSC-ready now since - * there may not be any codec clock yet. - */ - return 0; + ret = snd_soc_register_dai(&au1xpsc_ac97_dai); + if (ret) + goto out1; + + wd->dmapd = au1xpsc_pcm_add(pdev); + if (wd->dmapd) { + platform_set_drvdata(pdev, wd); + au1xpsc_ac97_workdata = wd; /* MDEV */ + return 0; + } + snd_soc_unregister_dai(&au1xpsc_ac97_dai); out1: - release_resource(au1xpsc_ac97_workdata->ioarea); - kfree(au1xpsc_ac97_workdata->ioarea); + release_resource(wd->ioarea); + kfree(wd->ioarea); out0: - kfree(au1xpsc_ac97_workdata); - au1xpsc_ac97_workdata = NULL; + kfree(wd); return ret; } -static void au1xpsc_ac97_remove(struct platform_device *pdev, - struct snd_soc_dai *dai) +static int __devexit au1xpsc_ac97_drvremove(struct platform_device *pdev) { + struct au1xpsc_audio_data *wd = platform_get_drvdata(pdev); + + if (wd->dmapd) + au1xpsc_pcm_destroy(wd->dmapd); + + snd_soc_unregister_dai(&au1xpsc_ac97_dai); + /* disable PSC completely */ - au_writel(0, AC97_CFG(au1xpsc_ac97_workdata)); + au_writel(0, AC97_CFG(wd)); au_sync(); - au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_ac97_workdata)); + au_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd)); au_sync(); - iounmap(au1xpsc_ac97_workdata->mmio); - release_resource(au1xpsc_ac97_workdata->ioarea); - kfree(au1xpsc_ac97_workdata->ioarea); - kfree(au1xpsc_ac97_workdata); - au1xpsc_ac97_workdata = NULL; + iounmap(wd->mmio); + release_resource(wd->ioarea); + kfree(wd->ioarea); + kfree(wd); + + au1xpsc_ac97_workdata = NULL; /* MDEV */ + + return 0; } -static int au1xpsc_ac97_suspend(struct snd_soc_dai *dai) +#ifdef CONFIG_PM +static int au1xpsc_ac97_drvsuspend(struct device *dev) { + struct au1xpsc_audio_data *wd = dev_get_drvdata(dev); + /* save interesting registers and disable PSC */ - au1xpsc_ac97_workdata->pm[0] = - au_readl(PSC_SEL(au1xpsc_ac97_workdata)); + wd->pm[0] = au_readl(PSC_SEL(wd)); - au_writel(0, AC97_CFG(au1xpsc_ac97_workdata)); + au_writel(0, AC97_CFG(wd)); au_sync(); - au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_ac97_workdata)); + au_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd)); au_sync(); return 0; } -static int au1xpsc_ac97_resume(struct snd_soc_dai *dai) +static int au1xpsc_ac97_drvresume(struct device *dev) { + struct au1xpsc_audio_data *wd = dev_get_drvdata(dev); + /* restore PSC clock config */ - au_writel(au1xpsc_ac97_workdata->pm[0] | PSC_SEL_PS_AC97MODE, - PSC_SEL(au1xpsc_ac97_workdata)); + au_writel(wd->pm[0] | PSC_SEL_PS_AC97MODE, PSC_SEL(wd)); au_sync(); /* after this point the ac97 core will cold-reset the codec. @@ -422,48 +472,44 @@ static int au1xpsc_ac97_resume(struct snd_soc_dai *dai) return 0; } -static struct snd_soc_dai_ops au1xpsc_ac97_dai_ops = { - .trigger = au1xpsc_ac97_trigger, - .hw_params = au1xpsc_ac97_hw_params, +static struct dev_pm_ops au1xpscac97_pmops = { + .suspend = au1xpsc_ac97_drvsuspend, + .resume = au1xpsc_ac97_drvresume, }; -struct snd_soc_dai au1xpsc_ac97_dai = { - .name = "au1xpsc_ac97", - .ac97_control = 1, - .probe = au1xpsc_ac97_probe, - .remove = au1xpsc_ac97_remove, - .suspend = au1xpsc_ac97_suspend, - .resume = au1xpsc_ac97_resume, - .playback = { - .rates = AC97_RATES, - .formats = AC97_FMTS, - .channels_min = 2, - .channels_max = 2, - }, - .capture = { - .rates = AC97_RATES, - .formats = AC97_FMTS, - .channels_min = 2, - .channels_max = 2, +#define AU1XPSCAC97_PMOPS &au1xpscac97_pmops + +#else + +#define AU1XPSCAC97_PMOPS NULL + +#endif + +static struct platform_driver au1xpsc_ac97_driver = { + .driver = { + .name = "au1xpsc_ac97", + .owner = THIS_MODULE, + .pm = AU1XPSCAC97_PMOPS, }, - .ops = &au1xpsc_ac97_dai_ops, + .probe = au1xpsc_ac97_drvprobe, + .remove = __devexit_p(au1xpsc_ac97_drvremove), }; -EXPORT_SYMBOL_GPL(au1xpsc_ac97_dai); -static int __init au1xpsc_ac97_init(void) +static int __init au1xpsc_ac97_load(void) { au1xpsc_ac97_workdata = NULL; - return snd_soc_register_dai(&au1xpsc_ac97_dai); + return platform_driver_register(&au1xpsc_ac97_driver); } -static void __exit au1xpsc_ac97_exit(void) +static void __exit au1xpsc_ac97_unload(void) { - snd_soc_unregister_dai(&au1xpsc_ac97_dai); + platform_driver_unregister(&au1xpsc_ac97_driver); } -module_init(au1xpsc_ac97_init); -module_exit(au1xpsc_ac97_exit); +module_init(au1xpsc_ac97_load); +module_exit(au1xpsc_ac97_unload); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Au12x0/Au1550 PSC AC97 ALSA ASoC audio driver"); -MODULE_AUTHOR("Manuel Lauss <manuel.lauss@gmail.com>"); +MODULE_AUTHOR("Manuel Lauss"); + diff --git a/sound/soc/au1x/psc-i2s.c b/sound/soc/au1x/psc-i2s.c index bb589327ee3..0cf2ca61c77 100644 --- a/sound/soc/au1x/psc-i2s.c +++ b/sound/soc/au1x/psc-i2s.c @@ -2,7 +2,7 @@ * Au12x0/Au1550 PSC ALSA ASoC audio support. * * (c) 2007-2008 MSC Vertriebsges.m.b.H., - * Manuel Lauss <mano@roarinelk.homelinux.net> + * Manuel Lauss <manuel.lauss@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 @@ -265,16 +265,52 @@ static int au1xpsc_i2s_trigger(struct snd_pcm_substream *substream, int cmd, static int au1xpsc_i2s_probe(struct platform_device *pdev, struct snd_soc_dai *dai) { + return au1xpsc_i2s_workdata ? 0 : -ENODEV; +} + +static void au1xpsc_i2s_remove(struct platform_device *pdev, + struct snd_soc_dai *dai) +{ +} + +static struct snd_soc_dai_ops au1xpsc_i2s_dai_ops = { + .trigger = au1xpsc_i2s_trigger, + .hw_params = au1xpsc_i2s_hw_params, + .set_fmt = au1xpsc_i2s_set_fmt, +}; + +struct snd_soc_dai au1xpsc_i2s_dai = { + .name = "au1xpsc_i2s", + .probe = au1xpsc_i2s_probe, + .remove = au1xpsc_i2s_remove, + .playback = { + .rates = AU1XPSC_I2S_RATES, + .formats = AU1XPSC_I2S_FMTS, + .channels_min = 2, + .channels_max = 8, /* 2 without external help */ + }, + .capture = { + .rates = AU1XPSC_I2S_RATES, + .formats = AU1XPSC_I2S_FMTS, + .channels_min = 2, + .channels_max = 8, /* 2 without external help */ + }, + .ops = &au1xpsc_i2s_dai_ops, +}; +EXPORT_SYMBOL(au1xpsc_i2s_dai); + +static int __init au1xpsc_i2s_drvprobe(struct platform_device *pdev) +{ struct resource *r; unsigned long sel; int ret; + struct au1xpsc_audio_data *wd; if (au1xpsc_i2s_workdata) return -EBUSY; - au1xpsc_i2s_workdata = - kzalloc(sizeof(struct au1xpsc_audio_data), GFP_KERNEL); - if (!au1xpsc_i2s_workdata) + wd = kzalloc(sizeof(struct au1xpsc_audio_data), GFP_KERNEL); + if (!wd) return -ENOMEM; r = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -284,131 +320,146 @@ static int au1xpsc_i2s_probe(struct platform_device *pdev, } ret = -EBUSY; - au1xpsc_i2s_workdata->ioarea = - request_mem_region(r->start, r->end - r->start + 1, + wd->ioarea = request_mem_region(r->start, r->end - r->start + 1, "au1xpsc_i2s"); - if (!au1xpsc_i2s_workdata->ioarea) + if (!wd->ioarea) goto out0; - au1xpsc_i2s_workdata->mmio = ioremap(r->start, 0xffff); - if (!au1xpsc_i2s_workdata->mmio) + wd->mmio = ioremap(r->start, 0xffff); + if (!wd->mmio) goto out1; /* preserve PSC clock source set up by platform (dev.platform_data * is already occupied by soc layer) */ - sel = au_readl(PSC_SEL(au1xpsc_i2s_workdata)) & PSC_SEL_CLK_MASK; - au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_i2s_workdata)); + sel = au_readl(PSC_SEL(wd)) & PSC_SEL_CLK_MASK; + au_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd)); au_sync(); - au_writel(PSC_SEL_PS_I2SMODE | sel, PSC_SEL(au1xpsc_i2s_workdata)); - au_writel(0, I2S_CFG(au1xpsc_i2s_workdata)); + au_writel(PSC_SEL_PS_I2SMODE | sel, PSC_SEL(wd)); + au_writel(0, I2S_CFG(wd)); au_sync(); /* preconfigure: set max rx/tx fifo depths */ - au1xpsc_i2s_workdata->cfg |= - PSC_I2SCFG_RT_FIFO8 | PSC_I2SCFG_TT_FIFO8; + wd->cfg |= PSC_I2SCFG_RT_FIFO8 | PSC_I2SCFG_TT_FIFO8; /* don't wait for I2S core to become ready now; clocks may not * be running yet; depending on clock input for PSC a wait might * time out. */ - return 0; + ret = snd_soc_register_dai(&au1xpsc_i2s_dai); + if (ret) + goto out1; + /* finally add the DMA device for this PSC */ + wd->dmapd = au1xpsc_pcm_add(pdev); + if (wd->dmapd) { + platform_set_drvdata(pdev, wd); + au1xpsc_i2s_workdata = wd; + return 0; + } + + snd_soc_unregister_dai(&au1xpsc_i2s_dai); out1: - release_resource(au1xpsc_i2s_workdata->ioarea); - kfree(au1xpsc_i2s_workdata->ioarea); + release_resource(wd->ioarea); + kfree(wd->ioarea); out0: - kfree(au1xpsc_i2s_workdata); - au1xpsc_i2s_workdata = NULL; + kfree(wd); return ret; } -static void au1xpsc_i2s_remove(struct platform_device *pdev, - struct snd_soc_dai *dai) +static int __devexit au1xpsc_i2s_drvremove(struct platform_device *pdev) { - au_writel(0, I2S_CFG(au1xpsc_i2s_workdata)); + struct au1xpsc_audio_data *wd = platform_get_drvdata(pdev); + + if (wd->dmapd) + au1xpsc_pcm_destroy(wd->dmapd); + + snd_soc_unregister_dai(&au1xpsc_i2s_dai); + + au_writel(0, I2S_CFG(wd)); au_sync(); - au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_i2s_workdata)); + au_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd)); au_sync(); - iounmap(au1xpsc_i2s_workdata->mmio); - release_resource(au1xpsc_i2s_workdata->ioarea); - kfree(au1xpsc_i2s_workdata->ioarea); - kfree(au1xpsc_i2s_workdata); - au1xpsc_i2s_workdata = NULL; + iounmap(wd->mmio); + release_resource(wd->ioarea); + kfree(wd->ioarea); + kfree(wd); + + au1xpsc_i2s_workdata = NULL; /* MDEV */ + + return 0; } -static int au1xpsc_i2s_suspend(struct snd_soc_dai *cpu_dai) +#ifdef CONFIG_PM +static int au1xpsc_i2s_drvsuspend(struct device *dev) { + struct au1xpsc_audio_data *wd = dev_get_drvdata(dev); + /* save interesting register and disable PSC */ - au1xpsc_i2s_workdata->pm[0] = - au_readl(PSC_SEL(au1xpsc_i2s_workdata)); + wd->pm[0] = au_readl(PSC_SEL(wd)); - au_writel(0, I2S_CFG(au1xpsc_i2s_workdata)); + au_writel(0, I2S_CFG(wd)); au_sync(); - au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_i2s_workdata)); + au_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd)); au_sync(); return 0; } -static int au1xpsc_i2s_resume(struct snd_soc_dai *cpu_dai) +static int au1xpsc_i2s_drvresume(struct device *dev) { + struct au1xpsc_audio_data *wd = dev_get_drvdata(dev); + /* select I2S mode and PSC clock */ - au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_i2s_workdata)); + au_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd)); au_sync(); - au_writel(0, PSC_SEL(au1xpsc_i2s_workdata)); + au_writel(0, PSC_SEL(wd)); au_sync(); - au_writel(au1xpsc_i2s_workdata->pm[0], - PSC_SEL(au1xpsc_i2s_workdata)); + au_writel(wd->pm[0], PSC_SEL(wd)); au_sync(); return 0; } -static struct snd_soc_dai_ops au1xpsc_i2s_dai_ops = { - .trigger = au1xpsc_i2s_trigger, - .hw_params = au1xpsc_i2s_hw_params, - .set_fmt = au1xpsc_i2s_set_fmt, +static struct dev_pm_ops au1xpsci2s_pmops = { + .suspend = au1xpsc_i2s_drvsuspend, + .resume = au1xpsc_i2s_drvresume, }; -struct snd_soc_dai au1xpsc_i2s_dai = { - .name = "au1xpsc_i2s", - .probe = au1xpsc_i2s_probe, - .remove = au1xpsc_i2s_remove, - .suspend = au1xpsc_i2s_suspend, - .resume = au1xpsc_i2s_resume, - .playback = { - .rates = AU1XPSC_I2S_RATES, - .formats = AU1XPSC_I2S_FMTS, - .channels_min = 2, - .channels_max = 8, /* 2 without external help */ - }, - .capture = { - .rates = AU1XPSC_I2S_RATES, - .formats = AU1XPSC_I2S_FMTS, - .channels_min = 2, - .channels_max = 8, /* 2 without external help */ +#define AU1XPSCI2S_PMOPS &au1xpsci2s_pmops + +#else + +#define AU1XPSCI2S_PMOPS NULL + +#endif + +static struct platform_driver au1xpsc_i2s_driver = { + .driver = { + .name = "au1xpsc_i2s", + .owner = THIS_MODULE, + .pm = AU1XPSCI2S_PMOPS, }, - .ops = &au1xpsc_i2s_dai_ops, + .probe = au1xpsc_i2s_drvprobe, + .remove = __devexit_p(au1xpsc_i2s_drvremove), }; -EXPORT_SYMBOL(au1xpsc_i2s_dai); -static int __init au1xpsc_i2s_init(void) +static int __init au1xpsc_i2s_load(void) { au1xpsc_i2s_workdata = NULL; - return snd_soc_register_dai(&au1xpsc_i2s_dai); + return platform_driver_register(&au1xpsc_i2s_driver); } -static void __exit au1xpsc_i2s_exit(void) +static void __exit au1xpsc_i2s_unload(void) { - snd_soc_unregister_dai(&au1xpsc_i2s_dai); + platform_driver_unregister(&au1xpsc_i2s_driver); } -module_init(au1xpsc_i2s_init); -module_exit(au1xpsc_i2s_exit); +module_init(au1xpsc_i2s_load); +module_exit(au1xpsc_i2s_unload); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Au12x0/Au1550 PSC I2S ALSA ASoC audio driver"); -MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>"); +MODULE_AUTHOR("Manuel Lauss"); diff --git a/sound/soc/au1x/psc.h b/sound/soc/au1x/psc.h index 3f474e8ed4f..32d3807d3f5 100644 --- a/sound/soc/au1x/psc.h +++ b/sound/soc/au1x/psc.h @@ -2,7 +2,7 @@ * Au12x0/Au1550 PSC ALSA ASoC audio support. * * (c) 2007-2008 MSC Vertriebsges.m.b.H., - * Manuel Lauss <mano@roarinelk.homelinux.net> + * Manuel Lauss <manuel.lauss@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 @@ -21,6 +21,10 @@ extern struct snd_soc_dai au1xpsc_i2s_dai; extern struct snd_soc_platform au1xpsc_soc_platform; extern struct snd_ac97_bus_ops soc_ac97_ops; +/* DBDMA helpers */ +extern struct platform_device *au1xpsc_pcm_add(struct platform_device *pdev); +extern void au1xpsc_pcm_destroy(struct platform_device *dmapd); + struct au1xpsc_audio_data { void __iomem *mmio; @@ -30,6 +34,7 @@ struct au1xpsc_audio_data { unsigned long pm[2]; struct resource *ioarea; struct mutex lock; + struct platform_device *dmapd; }; #define PCM_TX 0 diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 3df3497335b..52b005f8fed 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -15,6 +15,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_AD1836 if SPI_MASTER select SND_SOC_AD1938 if SPI_MASTER select SND_SOC_AD1980 if SND_SOC_AC97_BUS + select SND_SOC_ADS117X select SND_SOC_AD73311 if I2C select SND_SOC_AK4104 if SPI_MASTER select SND_SOC_AK4535 if I2C @@ -40,6 +41,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_WM8523 if I2C select SND_SOC_WM8580 if I2C select SND_SOC_WM8711 if SND_SOC_I2C_AND_SPI + select SND_SOC_WM8727 select SND_SOC_WM8728 if SND_SOC_I2C_AND_SPI select SND_SOC_WM8731 if SND_SOC_I2C_AND_SPI select SND_SOC_WM8750 if SND_SOC_I2C_AND_SPI @@ -90,6 +92,9 @@ config SND_SOC_AD1980 config SND_SOC_AD73311 tristate + +config SND_SOC_ADS117X + tristate config SND_SOC_AK4104 tristate @@ -174,6 +179,9 @@ config SND_SOC_WM8580 config SND_SOC_WM8711 tristate +config SND_SOC_WM8727 + tristate + config SND_SOC_WM8728 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 8f519ee9600..dbaecb133ac 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -3,6 +3,7 @@ snd-soc-ad1836-objs := ad1836.o snd-soc-ad1938-objs := ad1938.o snd-soc-ad1980-objs := ad1980.o snd-soc-ad73311-objs := ad73311.o +snd-soc-ads117x-objs := ads117x.o snd-soc-ak4104-objs := ak4104.o snd-soc-ak4535-objs := ak4535.o snd-soc-ak4642-objs := ak4642.o @@ -27,6 +28,7 @@ snd-soc-wm8510-objs := wm8510.o snd-soc-wm8523-objs := wm8523.o snd-soc-wm8580-objs := wm8580.o snd-soc-wm8711-objs := wm8711.o +snd-soc-wm8727-objs := wm8727.o snd-soc-wm8728-objs := wm8728.o snd-soc-wm8731-objs := wm8731.o snd-soc-wm8750-objs := wm8750.o @@ -57,6 +59,7 @@ obj-$(CONFIG_SND_SOC_AD1836) += snd-soc-ad1836.o obj-$(CONFIG_SND_SOC_AD1938) += snd-soc-ad1938.o obj-$(CONFIG_SND_SOC_AD1980) += snd-soc-ad1980.o obj-$(CONFIG_SND_SOC_AD73311) += snd-soc-ad73311.o +obj-$(CONFIG_SND_SOC_ADS117X) += snd-soc-ads117x.o obj-$(CONFIG_SND_SOC_AK4104) += snd-soc-ak4104.o obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.o obj-$(CONFIG_SND_SOC_AK4642) += snd-soc-ak4642.o @@ -81,6 +84,7 @@ obj-$(CONFIG_SND_SOC_WM8510) += snd-soc-wm8510.o obj-$(CONFIG_SND_SOC_WM8523) += snd-soc-wm8523.o obj-$(CONFIG_SND_SOC_WM8580) += snd-soc-wm8580.o obj-$(CONFIG_SND_SOC_WM8711) += snd-soc-wm8711.o +obj-$(CONFIG_SND_SOC_WM8727) += snd-soc-wm8727.o obj-$(CONFIG_SND_SOC_WM8728) += snd-soc-wm8728.o obj-$(CONFIG_SND_SOC_WM8731) += snd-soc-wm8731.o obj-$(CONFIG_SND_SOC_WM8750) += snd-soc-wm8750.o diff --git a/sound/soc/codecs/ac97.c b/sound/soc/codecs/ac97.c index 932299bb5d1..69bd0acc81c 100644 --- a/sound/soc/codecs/ac97.c +++ b/sound/soc/codecs/ac97.c @@ -117,9 +117,6 @@ static int ac97_soc_probe(struct platform_device *pdev) if (ret < 0) goto bus_err; - ret = snd_soc_init_card(socdev); - if (ret < 0) - goto bus_err; return 0; bus_err: diff --git a/sound/soc/codecs/ad1836.c b/sound/soc/codecs/ad1836.c index c48485f2c55..2c18e3d1b71 100644 --- a/sound/soc/codecs/ad1836.c +++ b/sound/soc/codecs/ad1836.c @@ -385,19 +385,7 @@ static int ad1836_probe(struct platform_device *pdev) snd_soc_dapm_new_controls(codec, ad1836_dapm_widgets, ARRAY_SIZE(ad1836_dapm_widgets)); snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths)); - snd_soc_dapm_new_widgets(codec); - ret = snd_soc_init_card(socdev); - if (ret < 0) { - dev_err(codec->dev, "failed to register card: %d\n", ret); - goto card_err; - } - - return ret; - -card_err: - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); pcm_err: return ret; } diff --git a/sound/soc/codecs/ad1938.c b/sound/soc/codecs/ad1938.c index 34b30efc3cb..5d489186c05 100644 --- a/sound/soc/codecs/ad1938.c +++ b/sound/soc/codecs/ad1938.c @@ -592,21 +592,9 @@ static int ad1938_probe(struct platform_device *pdev) snd_soc_dapm_new_controls(codec, ad1938_dapm_widgets, ARRAY_SIZE(ad1938_dapm_widgets)); snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths)); - snd_soc_dapm_new_widgets(codec); ad1938_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - ret = snd_soc_init_card(socdev); - if (ret < 0) { - dev_err(codec->dev, "failed to register card: %d\n", ret); - goto card_err; - } - - return ret; - -card_err: - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); pcm_err: return ret; } diff --git a/sound/soc/codecs/ad1980.c b/sound/soc/codecs/ad1980.c index d7440a982d2..39c0f7584e6 100644 --- a/sound/soc/codecs/ad1980.c +++ b/sound/soc/codecs/ad1980.c @@ -257,11 +257,6 @@ static int ad1980_soc_probe(struct platform_device *pdev) snd_soc_add_controls(codec, ad1980_snd_ac97_controls, ARRAY_SIZE(ad1980_snd_ac97_controls)); - ret = snd_soc_init_card(socdev); - if (ret < 0) { - printk(KERN_ERR "ad1980: failed to register card\n"); - goto reset_err; - } return 0; diff --git a/sound/soc/codecs/ad73311.c b/sound/soc/codecs/ad73311.c index e61dac5e7b8..d2fcc601722 100644 --- a/sound/soc/codecs/ad73311.c +++ b/sound/soc/codecs/ad73311.c @@ -64,16 +64,8 @@ static int ad73311_soc_probe(struct platform_device *pdev) goto pcm_err; } - ret = snd_soc_init_card(socdev); - if (ret < 0) { - printk(KERN_ERR "ad73311: failed to register card\n"); - goto register_err; - } - return ret; -register_err: - snd_soc_free_pcms(socdev); pcm_err: kfree(socdev->card->codec); socdev->card->codec = NULL; diff --git a/sound/soc/codecs/ads117x.c b/sound/soc/codecs/ads117x.c new file mode 100644 index 00000000000..cc96411ca3e --- /dev/null +++ b/sound/soc/codecs/ads117x.c @@ -0,0 +1,123 @@ +/* + * ads117x.c -- Driver for ads1174/8 ADC chips + * + * Copyright 2009 ShotSpotter Inc. + * Author: Graeme Gregory <gg@slimlogic.co.uk> + * + * 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/kernel.h> +#include <linux/init.h> +#include <linux/device.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/initval.h> +#include <sound/soc.h> + +#include "ads117x.h" + +#define ADS117X_RATES (SNDRV_PCM_RATE_8000_48000) + +#define ADS117X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE) + +struct snd_soc_dai ads117x_dai = { +/* ADC */ + .name = "ADS117X ADC", + .id = 1, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 32, + .rates = ADS117X_RATES, + .formats = ADS117X_FORMATS,}, +}; +EXPORT_SYMBOL_GPL(ads117x_dai); + +static int ads117x_probe(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec; + int ret; + + codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); + if (codec == NULL) + return -ENOMEM; + + socdev->card->codec = codec; + mutex_init(&codec->mutex); + INIT_LIST_HEAD(&codec->dapm_widgets); + INIT_LIST_HEAD(&codec->dapm_paths); + codec->name = "ADS117X"; + codec->owner = THIS_MODULE; + codec->dai = &ads117x_dai; + codec->num_dai = 1; + + /* register pcms */ + ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); + if (ret < 0) { + printk(KERN_ERR "ads117x: failed to create pcms\n"); + kfree(codec); + return ret; + } + + return 0; +} + +static int ads117x_remove(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec = socdev->card->codec; + + snd_soc_free_pcms(socdev); + kfree(codec); + + return 0; +} + +struct snd_soc_codec_device soc_codec_dev_ads117x = { + .probe = ads117x_probe, + .remove = ads117x_remove, +}; +EXPORT_SYMBOL_GPL(soc_codec_dev_ads117x); + +static __devinit int ads117x_platform_probe(struct platform_device *pdev) +{ + ads117x_dai.dev = &pdev->dev; + return snd_soc_register_dai(&ads117x_dai); +} + +static int __devexit ads117x_platform_remove(struct platform_device *pdev) +{ + snd_soc_unregister_dai(&ads117x_dai); + return 0; +} + +static struct platform_driver ads117x_codec_driver = { + .driver = { + .name = "ads117x", + .owner = THIS_MODULE, + }, + + .probe = ads117x_platform_probe, + .remove = __devexit_p(ads117x_platform_remove), +}; + +static int __init ads117x_init(void) +{ + return platform_driver_register(&ads117x_codec_driver); +} +module_init(ads117x_init); + +static void __exit ads117x_exit(void) +{ + platform_driver_unregister(&ads117x_codec_driver); +} +module_exit(ads117x_exit); + +MODULE_DESCRIPTION("ASoC ads117x driver"); +MODULE_AUTHOR("Graeme Gregory"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/ads117x.h b/sound/soc/codecs/ads117x.h new file mode 100644 index 00000000000..dbcf50ec9bd --- /dev/null +++ b/sound/soc/codecs/ads117x.h @@ -0,0 +1,13 @@ +/* + * ads117x.h -- Driver for ads1174/8 ADC chips + * + * Copyright 2009 ShotSpotter Inc. + * Author: Graeme Gregory <gg@slimlogic.co.uk> + * + * 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. + */ +extern struct snd_soc_dai ads117x_dai; +extern struct snd_soc_codec_device soc_codec_dev_ads117x; diff --git a/sound/soc/codecs/ak4104.c b/sound/soc/codecs/ak4104.c index 4d47bc4f742..3a14c6fc4f5 100644 --- a/sound/soc/codecs/ak4104.c +++ b/sound/soc/codecs/ak4104.c @@ -313,14 +313,6 @@ static int ak4104_probe(struct platform_device *pdev) return ret; } - /* Register the socdev */ - ret = snd_soc_init_card(socdev); - if (ret < 0) { - dev_err(codec->dev, "failed to register card\n"); - snd_soc_free_pcms(socdev); - return ret; - } - return 0; } diff --git a/sound/soc/codecs/ak4535.c b/sound/soc/codecs/ak4535.c index 0abec0d29a9..ff966567e2b 100644 --- a/sound/soc/codecs/ak4535.c +++ b/sound/soc/codecs/ak4535.c @@ -294,7 +294,6 @@ static int ak4535_add_widgets(struct snd_soc_codec *codec) snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); - snd_soc_dapm_new_widgets(codec); return 0; } @@ -485,17 +484,9 @@ static int ak4535_init(struct snd_soc_device *socdev) snd_soc_add_controls(codec, ak4535_snd_controls, ARRAY_SIZE(ak4535_snd_controls)); ak4535_add_widgets(codec); - ret = snd_soc_init_card(socdev); - if (ret < 0) { - printk(KERN_ERR "ak4535: failed to register card\n"); - goto card_err; - } return ret; -card_err: - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); pcm_err: kfree(codec->reg_cache); diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c index e057c7b578d..b69861d5216 100644 --- a/sound/soc/codecs/ak4642.c +++ b/sound/soc/codecs/ak4642.c @@ -442,18 +442,9 @@ static int ak4642_probe(struct platform_device *pdev) goto pcm_err; } - ret = snd_soc_init_card(socdev); - if (ret < 0) { - printk(KERN_ERR "ak4642: failed to register card\n"); - goto card_err; - } - dev_info(&pdev->dev, "AK4642 Audio Codec %s", AK4642_VERSION); return ret; -card_err: - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); pcm_err: return ret; diff --git a/sound/soc/codecs/ak4671.c b/sound/soc/codecs/ak4671.c index b61214d1c5d..82fca284d00 100644 --- a/sound/soc/codecs/ak4671.c +++ b/sound/soc/codecs/ak4671.c @@ -441,7 +441,6 @@ static int ak4671_add_widgets(struct snd_soc_codec *codec) snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon)); - snd_soc_dapm_new_widgets(codec); return 0; } @@ -662,19 +661,10 @@ static int ak4671_probe(struct platform_device *pdev) ARRAY_SIZE(ak4671_snd_controls)); ak4671_add_widgets(codec); - ret = snd_soc_init_card(socdev); - if (ret < 0) { - dev_err(codec->dev, "failed to register card: %d\n", ret); - goto card_err; - } - ak4671_set_bias_level(codec, SND_SOC_BIAS_STANDBY); return ret; -card_err: - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); pcm_err: return ret; } diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c index 59bb16d033d..ffe122d1cd7 100644 --- a/sound/soc/codecs/cs4270.c +++ b/sound/soc/codecs/cs4270.c @@ -520,6 +520,7 @@ static const struct snd_kcontrol_new cs4270_snd_controls[] = { SOC_SINGLE("Digital Sidetone Switch", CS4270_FORMAT, 5, 1, 0), SOC_SINGLE("Soft Ramp Switch", CS4270_TRANS, 6, 1, 0), SOC_SINGLE("Zero Cross Switch", CS4270_TRANS, 5, 1, 0), + SOC_SINGLE("De-emphasis filter", CS4270_TRANS, 0, 1, 0), SOC_SINGLE("Popguard Switch", CS4270_MODE, 0, 1, 1), SOC_SINGLE("Auto-Mute Switch", CS4270_MUTE, 5, 1, 0), SOC_DOUBLE("Master Capture Switch", CS4270_MUTE, 3, 4, 1, 1), @@ -598,13 +599,6 @@ static int cs4270_probe(struct platform_device *pdev) goto error_free_pcms; } - /* And finally, register the socdev */ - ret = snd_soc_init_card(socdev); - if (ret < 0) { - dev_err(codec->dev, "failed to register card\n"); - goto error_free_pcms; - } - return 0; error_free_pcms: diff --git a/sound/soc/codecs/cx20442.c b/sound/soc/codecs/cx20442.c index 38eac9c866e..e000cdfec1e 100644 --- a/sound/soc/codecs/cx20442.c +++ b/sound/soc/codecs/cx20442.c @@ -93,7 +93,6 @@ static int cx20442_add_widgets(struct snd_soc_codec *codec) snd_soc_dapm_add_routes(codec, cx20442_audio_map, ARRAY_SIZE(cx20442_audio_map)); - snd_soc_dapm_new_widgets(codec); return 0; } @@ -355,17 +354,6 @@ static int cx20442_codec_probe(struct platform_device *pdev) cx20442_add_widgets(codec); - ret = snd_soc_init_card(socdev); - if (ret < 0) { - dev_err(&pdev->dev, "failed to register card\n"); - goto card_err; - } - - return ret; - -card_err: - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); pcm_err: return ret; } diff --git a/sound/soc/codecs/pcm3008.c b/sound/soc/codecs/pcm3008.c index 5cda9e6b5a7..2afcd0a8669 100644 --- a/sound/soc/codecs/pcm3008.c +++ b/sound/soc/codecs/pcm3008.c @@ -90,13 +90,6 @@ static int pcm3008_soc_probe(struct platform_device *pdev) goto pcm_err; } - /* Register Card. */ - ret = snd_soc_init_card(socdev); - if (ret < 0) { - printk(KERN_ERR "pcm3008: failed to register card\n"); - goto card_err; - } - /* DEM1 DEM0 DE-EMPHASIS_MODE * Low Low De-emphasis 44.1 kHz ON * Low High De-emphasis OFF @@ -136,8 +129,6 @@ static int pcm3008_soc_probe(struct platform_device *pdev) gpio_err: pcm3008_gpio_free(setup); -card_err: - snd_soc_free_pcms(socdev); pcm_err: kfree(socdev->card->codec); diff --git a/sound/soc/codecs/ssm2602.c b/sound/soc/codecs/ssm2602.c index c550750c79c..d2ff1cde688 100644 --- a/sound/soc/codecs/ssm2602.c +++ b/sound/soc/codecs/ssm2602.c @@ -210,7 +210,6 @@ static int ssm2602_add_widgets(struct snd_soc_codec *codec) snd_soc_dapm_add_routes(codec, audio_conn, ARRAY_SIZE(audio_conn)); - snd_soc_dapm_new_widgets(codec); return 0; } @@ -613,17 +612,9 @@ static int ssm2602_init(struct snd_soc_device *socdev) snd_soc_add_controls(codec, ssm2602_snd_controls, ARRAY_SIZE(ssm2602_snd_controls)); ssm2602_add_widgets(codec); - ret = snd_soc_init_card(socdev); - if (ret < 0) { - pr_err("ssm2602: failed to register card\n"); - goto card_err; - } return ret; -card_err: - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); pcm_err: kfree(codec->reg_cache); return ret; diff --git a/sound/soc/codecs/stac9766.c b/sound/soc/codecs/stac9766.c index befc6488c39..bbc72c2ddfc 100644 --- a/sound/soc/codecs/stac9766.c +++ b/sound/soc/codecs/stac9766.c @@ -418,9 +418,6 @@ static int stac9766_codec_probe(struct platform_device *pdev) snd_soc_add_controls(codec, stac9766_snd_ac97_controls, ARRAY_SIZE(stac9766_snd_ac97_controls)); - ret = snd_soc_init_card(socdev); - if (ret < 0) - goto reset_err; return 0; reset_err: diff --git a/sound/soc/codecs/tlv320aic23.c b/sound/soc/codecs/tlv320aic23.c index 0b8dcb5cd72..a091ce77810 100644 --- a/sound/soc/codecs/tlv320aic23.c +++ b/sound/soc/codecs/tlv320aic23.c @@ -265,8 +265,8 @@ static const int bosr_usb_divisor_table[] = { #define UPPER_GROUP ((1<<8) | (1<<9) | (1<<10) | (1<<11) | (1<<15)) static const unsigned short sr_valid_mask[] = { LOWER_GROUP|UPPER_GROUP, /* Normal, bosr - 0*/ - LOWER_GROUP|UPPER_GROUP, /* Normal, bosr - 1*/ LOWER_GROUP, /* Usb, bosr - 0*/ + LOWER_GROUP|UPPER_GROUP, /* Normal, bosr - 1*/ UPPER_GROUP, /* Usb, bosr - 1*/ }; /* @@ -395,7 +395,6 @@ static int tlv320aic23_add_widgets(struct snd_soc_codec *codec) /* set up audio path interconnects */ snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon)); - snd_soc_dapm_new_widgets(codec); return 0; } @@ -707,17 +706,9 @@ static int tlv320aic23_init(struct snd_soc_device *socdev) snd_soc_add_controls(codec, tlv320aic23_snd_controls, ARRAY_SIZE(tlv320aic23_snd_controls)); tlv320aic23_add_widgets(codec); - ret = snd_soc_init_card(socdev); - if (ret < 0) { - printk(KERN_ERR "tlv320aic23: failed to register card\n"); - goto card_err; - } return ret; -card_err: - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); pcm_err: kfree(codec->reg_cache); return ret; diff --git a/sound/soc/codecs/tlv320aic26.c b/sound/soc/codecs/tlv320aic26.c index 3387d9e736e..357b609196e 100644 --- a/sound/soc/codecs/tlv320aic26.c +++ b/sound/soc/codecs/tlv320aic26.c @@ -356,18 +356,7 @@ static int aic26_probe(struct platform_device *pdev) ARRAY_SIZE(aic26_snd_controls)); WARN_ON(err < 0); - /* CODEC is setup, we can register the card now */ - dev_dbg(&pdev->dev, "Registering card\n"); - ret = snd_soc_init_card(socdev); - if (ret < 0) { - dev_err(&pdev->dev, "aic26: failed to register card\n"); - goto card_err; - } return 0; - - card_err: - snd_soc_free_pcms(socdev); - return ret; } static int aic26_remove(struct platform_device *pdev) diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index 3395cf945d5..2b4dc2b0b01 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -753,7 +753,6 @@ static int aic3x_add_widgets(struct snd_soc_codec *codec) /* set up audio path interconnects */ snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon)); - snd_soc_dapm_new_widgets(codec); return 0; } @@ -1405,18 +1404,8 @@ static int aic3x_probe(struct platform_device *pdev) aic3x_add_widgets(codec); - ret = snd_soc_init_card(socdev); - if (ret < 0) { - printk(KERN_ERR "aic3x: failed to register card\n"); - goto card_err; - } - return ret; -card_err: - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); - pcm_err: kfree(codec->reg_cache); return ret; diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c index 3ca8934fc26..2a013e46ae1 100644 --- a/sound/soc/codecs/tlv320dac33.c +++ b/sound/soc/codecs/tlv320dac33.c @@ -462,7 +462,6 @@ static int dac33_add_widgets(struct snd_soc_codec *codec) /* set up audio path interconnects */ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); - snd_soc_dapm_new_widgets(codec); return 0; } @@ -960,16 +959,8 @@ static int dac33_soc_probe(struct platform_device *pdev) /* power on device */ dac33_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - ret = snd_soc_init_card(socdev); - if (ret < 0) { - dev_err(codec->dev, "failed to register card\n"); - goto card_err; - } - return 0; -card_err: - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); + pcm_err: dac33_hard_power(codec, 0); return ret; diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index 5c5a4c0a424..5f1681f6ca7 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -122,9 +122,8 @@ static const u8 twl4030_reg[TWL4030_CACHEREGNUM] = { struct twl4030_priv { struct snd_soc_codec codec; - unsigned int bypass_state; unsigned int codec_powered; - unsigned int codec_muted; + unsigned int apll_enabled; struct snd_pcm_substream *master_substream; struct snd_pcm_substream *slave_substream; @@ -215,29 +214,30 @@ static void twl4030_init_chip(struct snd_soc_codec *codec) /* set all audio section registers to reasonable defaults */ for (i = TWL4030_REG_OPTION; i <= TWL4030_REG_MISC_SET_2; i++) - twl4030_write(codec, i, cache[i]); + if (i != TWL4030_REG_APLL_CTL) + twl4030_write(codec, i, cache[i]); } -static void twl4030_codec_mute(struct snd_soc_codec *codec, int mute) +static void twl4030_apll_enable(struct snd_soc_codec *codec, int enable) { struct twl4030_priv *twl4030 = codec->private_data; int status; - if (mute == twl4030->codec_muted) + if (enable == twl4030->apll_enabled) return; - if (mute) - /* Disable PLL */ - status = twl4030_codec_disable_resource(TWL4030_CODEC_RES_APLL); - else + if (enable) /* Enable PLL */ status = twl4030_codec_enable_resource(TWL4030_CODEC_RES_APLL); + else + /* Disable PLL */ + status = twl4030_codec_disable_resource(TWL4030_CODEC_RES_APLL); if (status >= 0) twl4030_write_reg_cache(codec, TWL4030_REG_APLL_CTL, status); - twl4030->codec_muted = mute; + twl4030->apll_enabled = enable; } static void twl4030_power_up(struct snd_soc_codec *codec) @@ -614,6 +614,27 @@ static int handsfreerpga_event(struct snd_soc_dapm_widget *w, return 0; } +static int vibramux_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + twl4030_write(w->codec, TWL4030_REG_VIBRA_SET, 0xff); + return 0; +} + +static int apll_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + twl4030_apll_enable(w->codec, 1); + break; + case SND_SOC_DAPM_POST_PMD: + twl4030_apll_enable(w->codec, 0); + break; + } + return 0; +} + static void headset_ramp(struct snd_soc_codec *codec, int ramp) { struct snd_soc_device *socdev = codec->socdev; @@ -725,67 +746,6 @@ static int headsetrpga_event(struct snd_soc_dapm_widget *w, return 0; } -static int bypass_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) -{ - struct soc_mixer_control *m = - (struct soc_mixer_control *)w->kcontrols->private_value; - struct twl4030_priv *twl4030 = w->codec->private_data; - unsigned char reg, misc; - - reg = twl4030_read_reg_cache(w->codec, m->reg); - - /* - * bypass_state[0:3] - analog HiFi bypass - * bypass_state[4] - analog voice bypass - * bypass_state[5] - digital voice bypass - * bypass_state[6:7] - digital HiFi bypass - */ - if (m->reg == TWL4030_REG_VSTPGA) { - /* Voice digital bypass */ - if (reg) - twl4030->bypass_state |= (1 << 5); - else - twl4030->bypass_state &= ~(1 << 5); - } else if (m->reg <= TWL4030_REG_ARXR2_APGA_CTL) { - /* Analog bypass */ - if (reg & (1 << m->shift)) - twl4030->bypass_state |= - (1 << (m->reg - TWL4030_REG_ARXL1_APGA_CTL)); - else - twl4030->bypass_state &= - ~(1 << (m->reg - TWL4030_REG_ARXL1_APGA_CTL)); - } else if (m->reg == TWL4030_REG_VDL_APGA_CTL) { - /* Analog voice bypass */ - if (reg & (1 << m->shift)) - twl4030->bypass_state |= (1 << 4); - else - twl4030->bypass_state &= ~(1 << 4); - } else { - /* Digital bypass */ - if (reg & (0x7 << m->shift)) - twl4030->bypass_state |= (1 << (m->shift ? 7 : 6)); - else - twl4030->bypass_state &= ~(1 << (m->shift ? 7 : 6)); - } - - /* Enable master analog loopback mode if any analog switch is enabled*/ - misc = twl4030_read_reg_cache(w->codec, TWL4030_REG_MISC_SET_1); - if (twl4030->bypass_state & 0x1F) - misc |= TWL4030_FMLOOP_EN; - else - misc &= ~TWL4030_FMLOOP_EN; - twl4030_write(w->codec, TWL4030_REG_MISC_SET_1, misc); - - if (w->codec->bias_level == SND_SOC_BIAS_STANDBY) { - if (twl4030->bypass_state) - twl4030_codec_mute(w->codec, 0); - else - twl4030_codec_mute(w->codec, 1); - } - return 0; -} - /* * Some of the gain controls in TWL (mostly those which are associated with * the outputs) are implemented in an interesting way: @@ -1193,32 +1153,28 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = { SND_SOC_NOPM, 0, 0), /* Analog bypasses */ - SND_SOC_DAPM_SWITCH_E("Right1 Analog Loopback", SND_SOC_NOPM, 0, 0, - &twl4030_dapm_abypassr1_control, bypass_event, - SND_SOC_DAPM_POST_REG), - SND_SOC_DAPM_SWITCH_E("Left1 Analog Loopback", SND_SOC_NOPM, 0, 0, - &twl4030_dapm_abypassl1_control, - bypass_event, SND_SOC_DAPM_POST_REG), - SND_SOC_DAPM_SWITCH_E("Right2 Analog Loopback", SND_SOC_NOPM, 0, 0, - &twl4030_dapm_abypassr2_control, - bypass_event, SND_SOC_DAPM_POST_REG), - SND_SOC_DAPM_SWITCH_E("Left2 Analog Loopback", SND_SOC_NOPM, 0, 0, - &twl4030_dapm_abypassl2_control, - bypass_event, SND_SOC_DAPM_POST_REG), - SND_SOC_DAPM_SWITCH_E("Voice Analog Loopback", SND_SOC_NOPM, 0, 0, - &twl4030_dapm_abypassv_control, - bypass_event, SND_SOC_DAPM_POST_REG), + SND_SOC_DAPM_SWITCH("Right1 Analog Loopback", SND_SOC_NOPM, 0, 0, + &twl4030_dapm_abypassr1_control), + SND_SOC_DAPM_SWITCH("Left1 Analog Loopback", SND_SOC_NOPM, 0, 0, + &twl4030_dapm_abypassl1_control), + SND_SOC_DAPM_SWITCH("Right2 Analog Loopback", SND_SOC_NOPM, 0, 0, + &twl4030_dapm_abypassr2_control), + SND_SOC_DAPM_SWITCH("Left2 Analog Loopback", SND_SOC_NOPM, 0, 0, + &twl4030_dapm_abypassl2_control), + SND_SOC_DAPM_SWITCH("Voice Analog Loopback", SND_SOC_NOPM, 0, 0, + &twl4030_dapm_abypassv_control), + + /* Master analog loopback switch */ + SND_SOC_DAPM_SUPPLY("FM Loop Enable", TWL4030_REG_MISC_SET_1, 5, 0, + NULL, 0), /* Digital bypasses */ - SND_SOC_DAPM_SWITCH_E("Left Digital Loopback", SND_SOC_NOPM, 0, 0, - &twl4030_dapm_dbypassl_control, bypass_event, - SND_SOC_DAPM_POST_REG), - SND_SOC_DAPM_SWITCH_E("Right Digital Loopback", SND_SOC_NOPM, 0, 0, - &twl4030_dapm_dbypassr_control, bypass_event, - SND_SOC_DAPM_POST_REG), - SND_SOC_DAPM_SWITCH_E("Voice Digital Loopback", SND_SOC_NOPM, 0, 0, - &twl4030_dapm_dbypassv_control, bypass_event, - SND_SOC_DAPM_POST_REG), + SND_SOC_DAPM_SWITCH("Left Digital Loopback", SND_SOC_NOPM, 0, 0, + &twl4030_dapm_dbypassl_control), + SND_SOC_DAPM_SWITCH("Right Digital Loopback", SND_SOC_NOPM, 0, 0, + &twl4030_dapm_dbypassr_control), + SND_SOC_DAPM_SWITCH("Voice Digital Loopback", SND_SOC_NOPM, 0, 0, + &twl4030_dapm_dbypassv_control), /* Digital mixers, power control for the physical DACs */ SND_SOC_DAPM_MIXER("Digital R1 Playback Mixer", @@ -1244,6 +1200,9 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = { SND_SOC_DAPM_MIXER("Analog Voice Playback Mixer", TWL4030_REG_VDL_APGA_CTL, 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("APLL Enable", SND_SOC_NOPM, 0, 0, apll_event, + SND_SOC_DAPM_PRE_PMU|SND_SOC_DAPM_POST_PMD), + /* Output MIXER controls */ /* Earpiece */ SND_SOC_DAPM_MIXER("Earpiece Mixer", SND_SOC_NOPM, 0, 0, @@ -1309,8 +1268,9 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = { 0, 0, NULL, 0, handsfreerpga_event, SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD), /* Vibra */ - SND_SOC_DAPM_MUX("Vibra Mux", TWL4030_REG_VIBRA_CTL, 0, 0, - &twl4030_dapm_vibra_control), + SND_SOC_DAPM_MUX_E("Vibra Mux", TWL4030_REG_VIBRA_CTL, 0, 0, + &twl4030_dapm_vibra_control, vibramux_event, + SND_SOC_DAPM_PRE_PMU), SND_SOC_DAPM_MUX("Vibra Route", SND_SOC_NOPM, 0, 0, &twl4030_dapm_vibrapath_control), @@ -1370,6 +1330,13 @@ static const struct snd_soc_dapm_route intercon[] = { {"Digital R2 Playback Mixer", NULL, "DAC Right2"}, {"Digital Voice Playback Mixer", NULL, "DAC Voice"}, + /* Supply for the digital part (APLL) */ + {"Digital R1 Playback Mixer", NULL, "APLL Enable"}, + {"Digital L1 Playback Mixer", NULL, "APLL Enable"}, + {"Digital R2 Playback Mixer", NULL, "APLL Enable"}, + {"Digital L2 Playback Mixer", NULL, "APLL Enable"}, + {"Digital Voice Playback Mixer", NULL, "APLL Enable"}, + {"Analog L1 Playback Mixer", NULL, "Digital L1 Playback Mixer"}, {"Analog R1 Playback Mixer", NULL, "Digital R1 Playback Mixer"}, {"Analog L2 Playback Mixer", NULL, "Digital L2 Playback Mixer"}, @@ -1483,6 +1450,11 @@ static const struct snd_soc_dapm_route intercon[] = { {"ADC Virtual Left2", NULL, "TX2 Capture Route"}, {"ADC Virtual Right2", NULL, "TX2 Capture Route"}, + {"ADC Virtual Left1", NULL, "APLL Enable"}, + {"ADC Virtual Right1", NULL, "APLL Enable"}, + {"ADC Virtual Left2", NULL, "APLL Enable"}, + {"ADC Virtual Right2", NULL, "APLL Enable"}, + /* Analog bypass routes */ {"Right1 Analog Loopback", "Switch", "Analog Right"}, {"Left1 Analog Loopback", "Switch", "Analog Left"}, @@ -1490,6 +1462,13 @@ static const struct snd_soc_dapm_route intercon[] = { {"Left2 Analog Loopback", "Switch", "Analog Left"}, {"Voice Analog Loopback", "Switch", "Analog Left"}, + /* Supply for the Analog loopbacks */ + {"Right1 Analog Loopback", NULL, "FM Loop Enable"}, + {"Left1 Analog Loopback", NULL, "FM Loop Enable"}, + {"Right2 Analog Loopback", NULL, "FM Loop Enable"}, + {"Left2 Analog Loopback", NULL, "FM Loop Enable"}, + {"Voice Analog Loopback", NULL, "FM Loop Enable"}, + {"Analog R1 Playback Mixer", NULL, "Right1 Analog Loopback"}, {"Analog L1 Playback Mixer", NULL, "Left1 Analog Loopback"}, {"Analog R2 Playback Mixer", NULL, "Right2 Analog Loopback"}, @@ -1514,32 +1493,20 @@ static int twl4030_add_widgets(struct snd_soc_codec *codec) snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon)); - snd_soc_dapm_new_widgets(codec); return 0; } static int twl4030_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { - struct twl4030_priv *twl4030 = codec->private_data; - switch (level) { case SND_SOC_BIAS_ON: - twl4030_codec_mute(codec, 0); break; case SND_SOC_BIAS_PREPARE: - twl4030_power_up(codec); - if (twl4030->bypass_state) - twl4030_codec_mute(codec, 0); - else - twl4030_codec_mute(codec, 1); break; case SND_SOC_BIAS_STANDBY: - twl4030_power_up(codec); - if (twl4030->bypass_state) - twl4030_codec_mute(codec, 0); - else - twl4030_codec_mute(codec, 1); + if (codec->bias_level == SND_SOC_BIAS_OFF) + twl4030_power_up(codec); break; case SND_SOC_BIAS_OFF: twl4030_power_down(codec); @@ -1786,30 +1753,23 @@ static int twl4030_set_dai_sysclk(struct snd_soc_dai *codec_dai, { struct snd_soc_codec *codec = codec_dai->codec; struct twl4030_priv *twl4030 = codec->private_data; - u8 apll_ctrl; - apll_ctrl = twl4030_read_reg_cache(codec, TWL4030_REG_APLL_CTL); - apll_ctrl &= ~TWL4030_APLL_INFREQ; switch (freq) { case 19200000: - apll_ctrl |= TWL4030_APLL_INFREQ_19200KHZ; - twl4030->sysclk = 19200; - break; case 26000000: - apll_ctrl |= TWL4030_APLL_INFREQ_26000KHZ; - twl4030->sysclk = 26000; - break; case 38400000: - apll_ctrl |= TWL4030_APLL_INFREQ_38400KHZ; - twl4030->sysclk = 38400; break; default: - printk(KERN_ERR "TWL4030 set sysclk: unknown rate %d\n", - freq); + dev_err(codec->dev, "Unsupported APLL mclk: %u\n", freq); return -EINVAL; } - twl4030_write(codec, TWL4030_REG_APLL_CTL, apll_ctrl); + if ((freq / 1000) != twl4030->sysclk) { + dev_err(codec->dev, + "Mismatch in APLL mclk: %u (configured: %u)\n", + freq, twl4030->sysclk * 1000); + return -EINVAL; + } return 0; } @@ -1907,18 +1867,16 @@ static int twl4030_voice_startup(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_device *socdev = rtd->socdev; struct snd_soc_codec *codec = socdev->card->codec; - u8 infreq; + struct twl4030_priv *twl4030 = codec->private_data; u8 mode; /* If the system master clock is not 26MHz, the voice PCM interface is * not avilable. */ - infreq = twl4030_read_reg_cache(codec, TWL4030_REG_APLL_CTL) - & TWL4030_APLL_INFREQ; - - if (infreq != TWL4030_APLL_INFREQ_26000KHZ) { - printk(KERN_ERR "TWL4030 voice startup: " - "MCLK is not 26MHz, call set_sysclk() on init\n"); + if (twl4030->sysclk != 26000) { + dev_err(codec->dev, "The board is configured for %u Hz, while" + "the Voice interface needs 26MHz APLL mclk\n", + twl4030->sysclk * 1000); return -EINVAL; } @@ -1991,22 +1949,19 @@ static int twl4030_voice_set_dai_sysclk(struct snd_soc_dai *codec_dai, int clk_id, unsigned int freq, int dir) { struct snd_soc_codec *codec = codec_dai->codec; - u8 apll_ctrl; + struct twl4030_priv *twl4030 = codec->private_data; - apll_ctrl = twl4030_read_reg_cache(codec, TWL4030_REG_APLL_CTL); - apll_ctrl &= ~TWL4030_APLL_INFREQ; - switch (freq) { - case 26000000: - apll_ctrl |= TWL4030_APLL_INFREQ_26000KHZ; - break; - default: - printk(KERN_ERR "TWL4030 voice set sysclk: unknown rate %d\n", - freq); + if (freq != 26000000) { + dev_err(codec->dev, "Unsupported APLL mclk: %u, the Voice" + "interface needs 26MHz APLL mclk\n", freq); + return -EINVAL; + } + if ((freq / 1000) != twl4030->sysclk) { + dev_err(codec->dev, + "Mismatch in APLL mclk: %u (configured: %u)\n", + freq, twl4030->sysclk * 1000); return -EINVAL; } - - twl4030_write(codec, TWL4030_REG_APLL_CTL, apll_ctrl); - return 0; } @@ -2164,17 +2119,15 @@ static int twl4030_soc_probe(struct platform_device *pdev) if (setup) { unsigned char hs_pop; - if (setup->sysclk) - twl4030->sysclk = setup->sysclk; - else - twl4030->sysclk = 26000; + if (setup->sysclk != twl4030->sysclk) + dev_warn(&pdev->dev, + "Mismatch in APLL mclk: %u (configured: %u)\n", + setup->sysclk, twl4030->sysclk); hs_pop = twl4030_read_reg_cache(codec, TWL4030_REG_HS_POPN_SET); hs_pop &= ~TWL4030_RAMP_DELAY; hs_pop |= (setup->ramp_delay_value << 2); twl4030_write_reg_cache(codec, TWL4030_REG_HS_POPN_SET, hs_pop); - } else { - twl4030->sysclk = 26000; } /* register pcms */ @@ -2188,19 +2141,7 @@ static int twl4030_soc_probe(struct platform_device *pdev) ARRAY_SIZE(twl4030_snd_controls)); twl4030_add_widgets(codec); - ret = snd_soc_init_card(socdev); - if (ret < 0) { - dev_err(&pdev->dev, "failed to register card\n"); - goto card_err; - } - return 0; - -card_err: - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); - - return ret; } static int twl4030_soc_remove(struct platform_device *pdev) @@ -2224,10 +2165,8 @@ static int __devinit twl4030_codec_probe(struct platform_device *pdev) struct twl4030_priv *twl4030; int ret; - if (!pdata || !(pdata->audio_mclk == 19200000 || - pdata->audio_mclk == 26000000 || - pdata->audio_mclk == 38400000)) { - dev_err(&pdev->dev, "Invalid platform_data\n"); + if (!pdata) { + dev_err(&pdev->dev, "platform_data is missing\n"); return -EINVAL; } @@ -2266,7 +2205,9 @@ static int __devinit twl4030_codec_probe(struct platform_device *pdev) twl4030_codec = codec; /* Set the defaults, and power up the codec */ + twl4030->sysclk = twl4030_codec_get_mclk() / 1000; twl4030_init_chip(codec); + codec->bias_level = SND_SOC_BIAS_OFF; twl4030_set_bias_level(codec, SND_SOC_BIAS_STANDBY); ret = snd_soc_register_codec(codec); diff --git a/sound/soc/codecs/uda134x.c b/sound/soc/codecs/uda134x.c index c33b92edbde..aa40d985138 100644 --- a/sound/soc/codecs/uda134x.c +++ b/sound/soc/codecs/uda134x.c @@ -562,17 +562,8 @@ static int uda134x_soc_probe(struct platform_device *pdev) goto pcm_err; } - ret = snd_soc_init_card(socdev); - if (ret < 0) { - printk(KERN_ERR "UDA134X: failed to register card\n"); - goto card_err; - } - return 0; -card_err: - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); pcm_err: kfree(codec->reg_cache); reg_err: diff --git a/sound/soc/codecs/uda1380.c b/sound/soc/codecs/uda1380.c index 92ec0344215..a2763c2e734 100644 --- a/sound/soc/codecs/uda1380.c +++ b/sound/soc/codecs/uda1380.c @@ -378,7 +378,6 @@ static int uda1380_add_widgets(struct snd_soc_codec *codec) snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); - snd_soc_dapm_new_widgets(codec); return 0; } @@ -713,17 +712,9 @@ static int uda1380_probe(struct platform_device *pdev) snd_soc_add_controls(codec, uda1380_snd_controls, ARRAY_SIZE(uda1380_snd_controls)); uda1380_add_widgets(codec); - ret = snd_soc_init_card(socdev); - if (ret < 0) { - dev_err(codec->dev, "failed to register card: %d\n", ret); - goto card_err; - } return ret; -card_err: - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); pcm_err: return ret; } diff --git a/sound/soc/codecs/wm8350.c b/sound/soc/codecs/wm8350.c index 714114b50d1..f82125d9e85 100644 --- a/sound/soc/codecs/wm8350.c +++ b/sound/soc/codecs/wm8350.c @@ -800,7 +800,7 @@ static int wm8350_add_widgets(struct snd_soc_codec *codec) return ret; } - return snd_soc_dapm_new_widgets(codec); + return 0; } static int wm8350_set_dai_sysclk(struct snd_soc_dai *codec_dai, @@ -1501,18 +1501,7 @@ static int wm8350_probe(struct platform_device *pdev) wm8350_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - ret = snd_soc_init_card(socdev); - if (ret < 0) { - dev_err(&pdev->dev, "failed to register card\n"); - goto card_err; - } - return 0; - -card_err: - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); - return ret; } static int wm8350_remove(struct platform_device *pdev) diff --git a/sound/soc/codecs/wm8400.c b/sound/soc/codecs/wm8400.c index bd7eecba20f..b432f4d4a32 100644 --- a/sound/soc/codecs/wm8400.c +++ b/sound/soc/codecs/wm8400.c @@ -915,7 +915,6 @@ static int wm8400_add_widgets(struct snd_soc_codec *codec) snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); - snd_soc_dapm_new_widgets(codec); return 0; } @@ -1400,17 +1399,6 @@ static int wm8400_probe(struct platform_device *pdev) wm8400_add_controls(codec); wm8400_add_widgets(codec); - ret = snd_soc_init_card(socdev); - if (ret < 0) { - dev_err(&pdev->dev, "failed to register card\n"); - goto card_err; - } - - return ret; - -card_err: - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); pcm_err: return ret; } diff --git a/sound/soc/codecs/wm8510.c b/sound/soc/codecs/wm8510.c index 5702435af81..265e68c75df 100644 --- a/sound/soc/codecs/wm8510.c +++ b/sound/soc/codecs/wm8510.c @@ -219,7 +219,6 @@ static int wm8510_add_widgets(struct snd_soc_codec *codec) snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); - snd_soc_dapm_new_widgets(codec); return 0; } @@ -604,16 +603,9 @@ static int wm8510_init(struct snd_soc_device *socdev, snd_soc_add_controls(codec, wm8510_snd_controls, ARRAY_SIZE(wm8510_snd_controls)); wm8510_add_widgets(codec); - ret = snd_soc_init_card(socdev); - if (ret < 0) { - printk(KERN_ERR "wm8510: failed to register card\n"); - goto card_err; - } + return ret; -card_err: - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); err: kfree(codec->reg_cache); return ret; diff --git a/sound/soc/codecs/wm8523.c b/sound/soc/codecs/wm8523.c index 268cab21c2c..d3a61d7ea0c 100644 --- a/sound/soc/codecs/wm8523.c +++ b/sound/soc/codecs/wm8523.c @@ -117,7 +117,6 @@ static int wm8523_add_widgets(struct snd_soc_codec *codec) snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon)); - snd_soc_dapm_new_widgets(codec); return 0; } @@ -448,17 +447,9 @@ static int wm8523_probe(struct platform_device *pdev) snd_soc_add_controls(codec, wm8523_snd_controls, ARRAY_SIZE(wm8523_snd_controls)); wm8523_add_widgets(codec); - ret = snd_soc_init_card(socdev); - if (ret < 0) { - dev_err(codec->dev, "failed to register card: %d\n", ret); - goto card_err; - } return ret; -card_err: - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); pcm_err: return ret; } diff --git a/sound/soc/codecs/wm8580.c b/sound/soc/codecs/wm8580.c index a09b23e0366..d077df6f5e7 100644 --- a/sound/soc/codecs/wm8580.c +++ b/sound/soc/codecs/wm8580.c @@ -315,7 +315,6 @@ static int wm8580_add_widgets(struct snd_soc_codec *codec) snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); - snd_soc_dapm_new_widgets(codec); return 0; } @@ -800,17 +799,9 @@ static int wm8580_probe(struct platform_device *pdev) snd_soc_add_controls(codec, wm8580_snd_controls, ARRAY_SIZE(wm8580_snd_controls)); wm8580_add_widgets(codec); - ret = snd_soc_init_card(socdev); - if (ret < 0) { - dev_err(codec->dev, "failed to register card: %d\n", ret); - goto card_err; - } return ret; -card_err: - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); pcm_err: return ret; } diff --git a/sound/soc/codecs/wm8711.c b/sound/soc/codecs/wm8711.c index 54189fbf9e9..24a35603bcf 100644 --- a/sound/soc/codecs/wm8711.c +++ b/sound/soc/codecs/wm8711.c @@ -99,7 +99,6 @@ static int wm8711_add_widgets(struct snd_soc_codec *codec) snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon)); - snd_soc_dapm_new_widgets(codec); return 0; } @@ -404,17 +403,9 @@ static int wm8711_probe(struct platform_device *pdev) snd_soc_add_controls(codec, wm8711_snd_controls, ARRAY_SIZE(wm8711_snd_controls)); wm8711_add_widgets(codec); - ret = snd_soc_init_card(socdev); - if (ret < 0) { - dev_err(codec->dev, "failed to register card: %d\n", ret); - goto card_err; - } return ret; -card_err: - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); pcm_err: return ret; } diff --git a/sound/soc/codecs/wm8727.c b/sound/soc/codecs/wm8727.c new file mode 100644 index 00000000000..d8ffbd641d7 --- /dev/null +++ b/sound/soc/codecs/wm8727.c @@ -0,0 +1,135 @@ +/* + * wm8727.c + * + * Created on: 15-Oct-2009 + * Author: neil.jones@imgtec.com + * + * Copyright (C) 2009 Imagination Technologies Ltd. + * + * 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/init.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/device.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/ac97_codec.h> +#include <sound/initval.h> +#include <sound/soc.h> + +#include "wm8727.h" +/* + * Note this is a simple chip with no configuration interface, sample rate is + * determined automatically by examining the Master clock and Bit clock ratios + */ +#define WM8727_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\ + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |\ + SNDRV_PCM_RATE_192000) + + +struct snd_soc_dai wm8727_dai = { + .name = "WM8727", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = WM8727_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, + }, +}; +EXPORT_SYMBOL_GPL(wm8727_dai); + +static int wm8727_soc_probe(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec; + int ret = 0; + + codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); + if (codec == NULL) + return -ENOMEM; + mutex_init(&codec->mutex); + codec->name = "WM8727"; + codec->owner = THIS_MODULE; + codec->dai = &wm8727_dai; + codec->num_dai = 1; + socdev->card->codec = codec; + INIT_LIST_HEAD(&codec->dapm_widgets); + INIT_LIST_HEAD(&codec->dapm_paths); + + /* register pcms */ + ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); + if (ret < 0) { + printk(KERN_ERR "wm8727: failed to create pcms\n"); + goto pcm_err; + } + + return ret; + +pcm_err: + kfree(socdev->card->codec); + socdev->card->codec = NULL; + return ret; +} + +static int wm8727_soc_remove(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec = socdev->card->codec; + + if (codec == NULL) + return 0; + snd_soc_free_pcms(socdev); + kfree(codec); + return 0; +} + +struct snd_soc_codec_device soc_codec_dev_wm8727 = { + .probe = wm8727_soc_probe, + .remove = wm8727_soc_remove, +}; +EXPORT_SYMBOL_GPL(soc_codec_dev_wm8727); + + +static __devinit int wm8727_platform_probe(struct platform_device *pdev) +{ + wm8727_dai.dev = &pdev->dev; + return snd_soc_register_dai(&wm8727_dai); +} + +static int __devexit wm8727_platform_remove(struct platform_device *pdev) +{ + snd_soc_unregister_dai(&wm8727_dai); + return 0; +} + +static struct platform_driver wm8727_codec_driver = { + .driver = { + .name = "wm8727-codec", + .owner = THIS_MODULE, + }, + + .probe = wm8727_platform_probe, + .remove = __devexit_p(wm8727_platform_remove), +}; + +static int __init wm8727_init(void) +{ + return platform_driver_register(&wm8727_codec_driver); +} +module_init(wm8727_init); + +static void __exit wm8727_exit(void) +{ + platform_driver_unregister(&wm8727_codec_driver); +} +module_exit(wm8727_exit); + +MODULE_DESCRIPTION("ASoC wm8727 driver"); +MODULE_AUTHOR("Neil Jones"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm8727.h b/sound/soc/codecs/wm8727.h new file mode 100644 index 00000000000..ee19aa71bcd --- /dev/null +++ b/sound/soc/codecs/wm8727.h @@ -0,0 +1,21 @@ +/* + * wm8727.h + * + * Created on: 15-Oct-2009 + * Author: neil.jones@imgtec.com + * + * Copyright (C) 2009 Imagination Technologies Ltd. + * + * 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. + */ + +#ifndef WM8727_H_ +#define WM8727_H_ + +extern struct snd_soc_dai wm8727_dai; +extern struct snd_soc_codec_device soc_codec_dev_wm8727; + +#endif /* WM8727_H_ */ diff --git a/sound/soc/codecs/wm8728.c b/sound/soc/codecs/wm8728.c index 16e969a762c..3fb653ba363 100644 --- a/sound/soc/codecs/wm8728.c +++ b/sound/soc/codecs/wm8728.c @@ -74,8 +74,6 @@ static int wm8728_add_widgets(struct snd_soc_codec *codec) snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon)); - snd_soc_dapm_new_widgets(codec); - return 0; } @@ -287,17 +285,9 @@ static int wm8728_init(struct snd_soc_device *socdev, snd_soc_add_controls(codec, wm8728_snd_controls, ARRAY_SIZE(wm8728_snd_controls)); wm8728_add_widgets(codec); - ret = snd_soc_init_card(socdev); - if (ret < 0) { - printk(KERN_ERR "wm8728: failed to register card\n"); - goto card_err; - } return ret; -card_err: - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); err: kfree(codec->reg_cache); return ret; diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c index 0e59219a59f..3a497810f93 100644 --- a/sound/soc/codecs/wm8731.c +++ b/sound/soc/codecs/wm8731.c @@ -19,6 +19,7 @@ #include <linux/pm.h> #include <linux/i2c.h> #include <linux/platform_device.h> +#include <linux/regulator/consumer.h> #include <linux/spi/spi.h> #include <sound/core.h> #include <sound/pcm.h> @@ -33,9 +34,18 @@ static struct snd_soc_codec *wm8731_codec; struct snd_soc_codec_device soc_codec_dev_wm8731; +#define WM8731_NUM_SUPPLIES 4 +static const char *wm8731_supply_names[WM8731_NUM_SUPPLIES] = { + "AVDD", + "HPVDD", + "DCVDD", + "DBVDD", +}; + /* codec private data */ struct wm8731_priv { struct snd_soc_codec codec; + struct regulator_bulk_data supplies[WM8731_NUM_SUPPLIES]; u16 reg_cache[WM8731_CACHEREGNUM]; unsigned int sysclk; }; @@ -149,7 +159,6 @@ static int wm8731_add_widgets(struct snd_soc_codec *codec) snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon)); - snd_soc_dapm_new_widgets(codec); return 0; } @@ -422,9 +431,12 @@ static int wm8731_suspend(struct platform_device *pdev, pm_message_t state) { struct snd_soc_device *socdev = platform_get_drvdata(pdev); struct snd_soc_codec *codec = socdev->card->codec; + struct wm8731_priv *wm8731 = codec->private_data; snd_soc_write(codec, WM8731_ACTIVE, 0x0); wm8731_set_bias_level(codec, SND_SOC_BIAS_OFF); + regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), + wm8731->supplies); return 0; } @@ -432,10 +444,16 @@ static int wm8731_resume(struct platform_device *pdev) { struct snd_soc_device *socdev = platform_get_drvdata(pdev); struct snd_soc_codec *codec = socdev->card->codec; - int i; + struct wm8731_priv *wm8731 = codec->private_data; + int i, ret; u8 data[2]; u16 *cache = codec->reg_cache; + ret = regulator_bulk_enable(ARRAY_SIZE(wm8731->supplies), + wm8731->supplies); + if (ret != 0) + return ret; + /* Sync reg_cache with the hardware */ for (i = 0; i < ARRAY_SIZE(wm8731_reg); i++) { data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001); @@ -444,6 +462,7 @@ static int wm8731_resume(struct platform_device *pdev) } wm8731_set_bias_level(codec, SND_SOC_BIAS_STANDBY); wm8731_set_bias_level(codec, codec->suspend_bias_level); + return 0; } #else @@ -475,17 +494,9 @@ static int wm8731_probe(struct platform_device *pdev) snd_soc_add_controls(codec, wm8731_snd_controls, ARRAY_SIZE(wm8731_snd_controls)); wm8731_add_widgets(codec); - ret = snd_soc_init_card(socdev); - if (ret < 0) { - dev_err(codec->dev, "failed to register card: %d\n", ret); - goto card_err; - } return ret; -card_err: - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); pcm_err: return ret; } @@ -512,7 +523,7 @@ EXPORT_SYMBOL_GPL(soc_codec_dev_wm8731); static int wm8731_register(struct wm8731_priv *wm8731, enum snd_soc_control_type control) { - int ret; + int ret, i; struct snd_soc_codec *codec = &wm8731->codec; if (wm8731_codec) { @@ -543,10 +554,27 @@ static int wm8731_register(struct wm8731_priv *wm8731, goto err; } + for (i = 0; i < ARRAY_SIZE(wm8731->supplies); i++) + wm8731->supplies[i].supply = wm8731_supply_names[i]; + + ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8731->supplies), + wm8731->supplies); + if (ret != 0) { + dev_err(codec->dev, "Failed to request supplies: %d\n", ret); + goto err; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(wm8731->supplies), + wm8731->supplies); + if (ret != 0) { + dev_err(codec->dev, "Failed to enable supplies: %d\n", ret); + goto err_regulator_get; + } + ret = wm8731_reset(codec); if (ret < 0) { dev_err(codec->dev, "Failed to issue reset: %d\n", ret); - goto err; + goto err_regulator_enable; } wm8731_dai.dev = codec->dev; @@ -567,7 +595,7 @@ static int wm8731_register(struct wm8731_priv *wm8731, ret = snd_soc_register_codec(codec); if (ret != 0) { dev_err(codec->dev, "Failed to register codec: %d\n", ret); - goto err; + goto err_regulator_enable; } ret = snd_soc_register_dai(&wm8731_dai); @@ -581,6 +609,10 @@ static int wm8731_register(struct wm8731_priv *wm8731, err_codec: snd_soc_unregister_codec(codec); +err_regulator_enable: + regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), wm8731->supplies); +err_regulator_get: + regulator_bulk_free(ARRAY_SIZE(wm8731->supplies), wm8731->supplies); err: kfree(wm8731); return ret; @@ -591,6 +623,8 @@ static void wm8731_unregister(struct wm8731_priv *wm8731) wm8731_set_bias_level(&wm8731->codec, SND_SOC_BIAS_OFF); snd_soc_unregister_dai(&wm8731_dai); snd_soc_unregister_codec(&wm8731->codec); + regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), wm8731->supplies); + regulator_bulk_free(ARRAY_SIZE(wm8731->supplies), wm8731->supplies); kfree(wm8731); wm8731_codec = NULL; } diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c index 4ba1e7e93fb..475c67ac781 100644 --- a/sound/soc/codecs/wm8750.c +++ b/sound/soc/codecs/wm8750.c @@ -403,7 +403,6 @@ static int wm8750_add_widgets(struct snd_soc_codec *codec) snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); - snd_soc_dapm_new_widgets(codec); return 0; } @@ -772,16 +771,8 @@ static int wm8750_init(struct snd_soc_device *socdev, snd_soc_add_controls(codec, wm8750_snd_controls, ARRAY_SIZE(wm8750_snd_controls)); wm8750_add_widgets(codec); - ret = snd_soc_init_card(socdev); - if (ret < 0) { - printk(KERN_ERR "wm8750: failed to register card\n"); - goto card_err; - } return ret; -card_err: - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); err: kfree(codec->reg_cache); return ret; diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c index 8f7305257d2..d6850dacda2 100644 --- a/sound/soc/codecs/wm8753.c +++ b/sound/soc/codecs/wm8753.c @@ -673,7 +673,6 @@ static int wm8753_add_widgets(struct snd_soc_codec *codec) snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); - snd_soc_dapm_new_widgets(codec); return 0; } @@ -1583,18 +1582,9 @@ static int wm8753_probe(struct platform_device *pdev) snd_soc_add_controls(codec, wm8753_snd_controls, ARRAY_SIZE(wm8753_snd_controls)); wm8753_add_widgets(codec); - ret = snd_soc_init_card(socdev); - if (ret < 0) { - printk(KERN_ERR "wm8753: failed to register card\n"); - goto card_err; - } return 0; -card_err: - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); - pcm_err: return ret; } diff --git a/sound/soc/codecs/wm8776.c b/sound/soc/codecs/wm8776.c index a0bbb28eed7..ab2c0da1809 100644 --- a/sound/soc/codecs/wm8776.c +++ b/sound/soc/codecs/wm8776.c @@ -447,17 +447,8 @@ static int wm8776_probe(struct platform_device *pdev) ARRAY_SIZE(wm8776_dapm_widgets)); snd_soc_dapm_add_routes(codec, routes, ARRAY_SIZE(routes)); - ret = snd_soc_init_card(socdev); - if (ret < 0) { - dev_err(codec->dev, "failed to register card: %d\n", ret); - goto card_err; - } - return ret; -card_err: - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); pcm_err: return ret; } diff --git a/sound/soc/codecs/wm8900.c b/sound/soc/codecs/wm8900.c index b48804b5cac..c9438dd62df 100644 --- a/sound/soc/codecs/wm8900.c +++ b/sound/soc/codecs/wm8900.c @@ -618,8 +618,6 @@ static int wm8900_add_widgets(struct snd_soc_codec *codec) snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); - snd_soc_dapm_new_widgets(codec); - return 0; } @@ -1353,17 +1351,6 @@ static int wm8900_probe(struct platform_device *pdev) ARRAY_SIZE(wm8900_snd_controls)); wm8900_add_widgets(codec); - ret = snd_soc_init_card(socdev); - if (ret < 0) { - dev_err(&pdev->dev, "Failed to register card\n"); - goto card_err; - } - - return ret; - -card_err: - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); pcm_err: return ret; } diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c index 94cdb813041..b8cae175864 100644 --- a/sound/soc/codecs/wm8903.c +++ b/sound/soc/codecs/wm8903.c @@ -919,8 +919,6 @@ static int wm8903_add_widgets(struct snd_soc_codec *codec) snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon)); - snd_soc_dapm_new_widgets(codec); - return 0; } @@ -1695,17 +1693,8 @@ static int wm8903_probe(struct platform_device *pdev) ARRAY_SIZE(wm8903_snd_controls)); wm8903_add_widgets(socdev->card->codec); - ret = snd_soc_init_card(socdev); - if (ret < 0) { - dev_err(&pdev->dev, "wm8903: failed to register card\n"); - goto card_err; - } - return ret; -card_err: - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); err: return ret; } diff --git a/sound/soc/codecs/wm8940.c b/sound/soc/codecs/wm8940.c index 8d4fd3c08c0..3d850b97037 100644 --- a/sound/soc/codecs/wm8940.c +++ b/sound/soc/codecs/wm8940.c @@ -298,7 +298,6 @@ static int wm8940_add_widgets(struct snd_soc_codec *codec) ret = snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); if (ret) goto error_ret; - ret = snd_soc_dapm_new_widgets(codec); error_ret: return ret; @@ -731,12 +730,6 @@ static int wm8940_probe(struct platform_device *pdev) if (ret) goto error_free_pcms; - ret = snd_soc_init_card(socdev); - if (ret < 0) { - dev_err(codec->dev, "failed to register card: %d\n", ret); - goto error_free_pcms; - } - return ret; error_free_pcms: diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c index b9b096a8539..d07bcc1e1c6 100644 --- a/sound/soc/codecs/wm8960.c +++ b/sound/soc/codecs/wm8960.c @@ -307,7 +307,6 @@ static int wm8960_add_widgets(struct snd_soc_codec *codec) snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths)); - snd_soc_dapm_new_widgets(codec); return 0; } @@ -713,17 +712,9 @@ static int wm8960_probe(struct platform_device *pdev) snd_soc_add_controls(codec, wm8960_snd_controls, ARRAY_SIZE(wm8960_snd_controls)); wm8960_add_widgets(codec); - ret = snd_soc_init_card(socdev); - if (ret < 0) { - dev_err(codec->dev, "failed to register card: %d\n", ret); - goto card_err; - } return ret; -card_err: - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); pcm_err: return ret; } diff --git a/sound/soc/codecs/wm8961.c b/sound/soc/codecs/wm8961.c index b5c6f2cd5ae..a8007d58813 100644 --- a/sound/soc/codecs/wm8961.c +++ b/sound/soc/codecs/wm8961.c @@ -986,19 +986,9 @@ static int wm8961_probe(struct platform_device *pdev) snd_soc_dapm_new_controls(codec, wm8961_dapm_widgets, ARRAY_SIZE(wm8961_dapm_widgets)); snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths)); - snd_soc_dapm_new_widgets(codec); - - ret = snd_soc_init_card(socdev); - if (ret < 0) { - dev_err(codec->dev, "failed to register card: %d\n", ret); - goto card_err; - } return ret; -card_err: - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); pcm_err: return ret; } diff --git a/sound/soc/codecs/wm8971.c b/sound/soc/codecs/wm8971.c index d66efb0546e..d9540d55fc8 100644 --- a/sound/soc/codecs/wm8971.c +++ b/sound/soc/codecs/wm8971.c @@ -338,8 +338,6 @@ static int wm8971_add_widgets(struct snd_soc_codec *codec) snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); - snd_soc_dapm_new_widgets(codec); - return 0; } @@ -703,16 +701,9 @@ static int wm8971_init(struct snd_soc_device *socdev, snd_soc_add_controls(codec, wm8971_snd_controls, ARRAY_SIZE(wm8971_snd_controls)); wm8971_add_widgets(codec); - ret = snd_soc_init_card(socdev); - if (ret < 0) { - printk(KERN_ERR "wm8971: failed to register card\n"); - goto card_err; - } + return ret; -card_err: - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); err: kfree(codec->reg_cache); return ret; diff --git a/sound/soc/codecs/wm8974.c b/sound/soc/codecs/wm8974.c index eff29331235..81c57b5c591 100644 --- a/sound/soc/codecs/wm8974.c +++ b/sound/soc/codecs/wm8974.c @@ -276,7 +276,6 @@ static int wm8974_add_widgets(struct snd_soc_codec *codec) snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); - snd_soc_dapm_new_widgets(codec); return 0; } @@ -641,17 +640,9 @@ static int wm8974_probe(struct platform_device *pdev) snd_soc_add_controls(codec, wm8974_snd_controls, ARRAY_SIZE(wm8974_snd_controls)); wm8974_add_widgets(codec); - ret = snd_soc_init_card(socdev); - if (ret < 0) { - dev_err(codec->dev, "failed to register card: %d\n", ret); - goto card_err; - } return ret; -card_err: - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); pcm_err: return ret; } diff --git a/sound/soc/codecs/wm8988.c b/sound/soc/codecs/wm8988.c index d8d8f68b81e..2862e4dced2 100644 --- a/sound/soc/codecs/wm8988.c +++ b/sound/soc/codecs/wm8988.c @@ -790,19 +790,9 @@ static int wm8988_probe(struct platform_device *pdev) snd_soc_dapm_new_controls(codec, wm8988_dapm_widgets, ARRAY_SIZE(wm8988_dapm_widgets)); snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); - snd_soc_dapm_new_widgets(codec); - - ret = snd_soc_init_card(socdev); - if (ret < 0) { - dev_err(codec->dev, "failed to register card: %d\n", ret); - goto card_err; - } return ret; -card_err: - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); pcm_err: return ret; } diff --git a/sound/soc/codecs/wm8990.c b/sound/soc/codecs/wm8990.c index f657e9a5fe2..341481e0e83 100644 --- a/sound/soc/codecs/wm8990.c +++ b/sound/soc/codecs/wm8990.c @@ -920,7 +920,6 @@ static int wm8990_add_widgets(struct snd_soc_codec *codec) /* set up the WM8990 audio map */ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); - snd_soc_dapm_new_widgets(codec); return 0; } @@ -1409,16 +1408,9 @@ static int wm8990_init(struct snd_soc_device *socdev) snd_soc_add_controls(codec, wm8990_snd_controls, ARRAY_SIZE(wm8990_snd_controls)); wm8990_add_widgets(codec); - ret = snd_soc_init_card(socdev); - if (ret < 0) { - printk(KERN_ERR "wm8990: failed to register card\n"); - goto card_err; - } + return ret; -card_err: - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); pcm_err: kfree(codec->reg_cache); return ret; diff --git a/sound/soc/codecs/wm8993.c b/sound/soc/codecs/wm8993.c index dac39771214..5e32f2ed5fc 100644 --- a/sound/soc/codecs/wm8993.c +++ b/sound/soc/codecs/wm8993.c @@ -1464,19 +1464,8 @@ static int wm8993_probe(struct platform_device *pdev) wm_hubs_add_analogue_routes(codec, wm8993->pdata.lineout1_diff, wm8993->pdata.lineout2_diff); - snd_soc_dapm_new_widgets(codec); - - ret = snd_soc_init_card(socdev); - if (ret < 0) { - dev_err(codec->dev, "failed to register card\n"); - goto card_err; - } - return ret; -card_err: - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); err: return ret; } diff --git a/sound/soc/codecs/wm9081.c b/sound/soc/codecs/wm9081.c index 4cb6b104b72..c468497314b 100644 --- a/sound/soc/codecs/wm9081.c +++ b/sound/soc/codecs/wm9081.c @@ -1262,19 +1262,9 @@ static int wm9081_probe(struct platform_device *pdev) snd_soc_dapm_new_controls(codec, wm9081_dapm_widgets, ARRAY_SIZE(wm9081_dapm_widgets)); snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths)); - snd_soc_dapm_new_widgets(codec); - - ret = snd_soc_init_card(socdev); - if (ret < 0) { - dev_err(codec->dev, "failed to register card: %d\n", ret); - goto card_err; - } return ret; -card_err: - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); pcm_err: return ret; } diff --git a/sound/soc/codecs/wm9705.c b/sound/soc/codecs/wm9705.c index e7d2840d9e5..dfffc6c778c 100644 --- a/sound/soc/codecs/wm9705.c +++ b/sound/soc/codecs/wm9705.c @@ -205,7 +205,6 @@ static int wm9705_add_widgets(struct snd_soc_codec *codec) snd_soc_dapm_new_controls(codec, wm9705_dapm_widgets, ARRAY_SIZE(wm9705_dapm_widgets)); snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); - snd_soc_dapm_new_widgets(codec); return 0; } @@ -403,16 +402,8 @@ static int wm9705_soc_probe(struct platform_device *pdev) ARRAY_SIZE(wm9705_snd_ac97_controls)); wm9705_add_widgets(codec); - ret = snd_soc_init_card(socdev); - if (ret < 0) { - printk(KERN_ERR "wm9705: failed to register card\n"); - goto reset_err; - } - return 0; -reset_err: - snd_soc_free_pcms(socdev); pcm_err: snd_soc_free_ac97_codec(codec); codec_err: diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c index 1fd4e88f50c..2a087227300 100644 --- a/sound/soc/codecs/wm9712.c +++ b/sound/soc/codecs/wm9712.c @@ -436,7 +436,6 @@ static int wm9712_add_widgets(struct snd_soc_codec *codec) snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); - snd_soc_dapm_new_widgets(codec); return 0; } @@ -695,17 +694,9 @@ static int wm9712_soc_probe(struct platform_device *pdev) snd_soc_add_controls(codec, wm9712_snd_ac97_controls, ARRAY_SIZE(wm9712_snd_ac97_controls)); wm9712_add_widgets(codec); - ret = snd_soc_init_card(socdev); - if (ret < 0) { - printk(KERN_ERR "wm9712: failed to register card\n"); - goto reset_err; - } return 0; -reset_err: - snd_soc_free_pcms(socdev); - pcm_err: snd_soc_free_ac97_codec(codec); diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c index ca3d449ed89..00bac315fb3 100644 --- a/sound/soc/codecs/wm9713.c +++ b/sound/soc/codecs/wm9713.c @@ -625,7 +625,6 @@ static int wm9713_add_widgets(struct snd_soc_codec *codec) snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); - snd_soc_dapm_new_widgets(codec); return 0; } @@ -1247,13 +1246,8 @@ static int wm9713_soc_probe(struct platform_device *pdev) snd_soc_add_controls(codec, wm9713_snd_ac97_controls, ARRAY_SIZE(wm9713_snd_ac97_controls)); wm9713_add_widgets(codec); - ret = snd_soc_init_card(socdev); - if (ret < 0) - goto reset_err; - return 0; -reset_err: - snd_soc_free_pcms(socdev); + return 0; pcm_err: snd_soc_free_ac97_codec(codec); diff --git a/sound/soc/davinci/davinci-i2s.c b/sound/soc/davinci/davinci-i2s.c index 2ab809359c0..6362ca05506 100644 --- a/sound/soc/davinci/davinci-i2s.c +++ b/sound/soc/davinci/davinci-i2s.c @@ -97,12 +97,24 @@ enum { DAVINCI_MCBSP_WORD_32, }; +static const unsigned char data_type[SNDRV_PCM_FORMAT_S32_LE + 1] = { + [SNDRV_PCM_FORMAT_S8] = 1, + [SNDRV_PCM_FORMAT_S16_LE] = 2, + [SNDRV_PCM_FORMAT_S32_LE] = 4, +}; + +static const unsigned char asp_word_length[SNDRV_PCM_FORMAT_S32_LE + 1] = { + [SNDRV_PCM_FORMAT_S8] = DAVINCI_MCBSP_WORD_8, + [SNDRV_PCM_FORMAT_S16_LE] = DAVINCI_MCBSP_WORD_16, + [SNDRV_PCM_FORMAT_S32_LE] = DAVINCI_MCBSP_WORD_32, +}; + +static const unsigned char double_fmt[SNDRV_PCM_FORMAT_S32_LE + 1] = { + [SNDRV_PCM_FORMAT_S8] = SNDRV_PCM_FORMAT_S16_LE, + [SNDRV_PCM_FORMAT_S16_LE] = SNDRV_PCM_FORMAT_S32_LE, +}; + struct davinci_mcbsp_dev { - /* - * dma_params must be first because rtd->dai->cpu_dai->private_data - * is cast to a pointer of an array of struct davinci_pcm_dma_params in - * davinci_pcm_open. - */ struct davinci_pcm_dma_params dma_params[2]; void __iomem *base; #define MOD_DSP_A 0 @@ -110,6 +122,27 @@ struct davinci_mcbsp_dev { int mode; u32 pcr; struct clk *clk; + /* + * Combining both channels into 1 element will at least double the + * amount of time between servicing the dma channel, increase + * effiency, and reduce the chance of overrun/underrun. But, + * it will result in the left & right channels being swapped. + * + * If relabeling the left and right channels is not possible, + * you may want to let the codec know to swap them back. + * + * It may allow x10 the amount of time to service dma requests, + * if the codec is master and is using an unnecessarily fast bit clock + * (ie. tlvaic23b), independent of the sample rate. So, having an + * entire frame at once means it can be serviced at the sample rate + * instead of the bit clock rate. + * + * In the now unlikely case that an underrun still + * occurs, both the left and right samples will be repeated + * so that no pops are heard, and the left and right channels + * won't end up being swapped because of the underrun. + */ + unsigned enable_channel_combine:1; }; static inline void davinci_mcbsp_write_reg(struct davinci_mcbsp_dev *dev, @@ -349,6 +382,8 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream, int mcbsp_word_length; unsigned int rcr, xcr, srgr; u32 spcr; + snd_pcm_format_t fmt; + unsigned element_cnt = 1; /* general line settings */ spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG); @@ -378,29 +413,24 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream, xcr |= DAVINCI_MCBSP_XCR_XDATDLY(1); } /* Determine xfer data type */ - switch (params_format(params)) { - case SNDRV_PCM_FORMAT_S8: - dma_params->data_type = 1; - mcbsp_word_length = DAVINCI_MCBSP_WORD_8; - break; - case SNDRV_PCM_FORMAT_S16_LE: - dma_params->data_type = 2; - mcbsp_word_length = DAVINCI_MCBSP_WORD_16; - break; - case SNDRV_PCM_FORMAT_S32_LE: - dma_params->data_type = 4; - mcbsp_word_length = DAVINCI_MCBSP_WORD_32; - break; - default: + fmt = params_format(params); + if ((fmt > SNDRV_PCM_FORMAT_S32_LE) || !data_type[fmt]) { printk(KERN_WARNING "davinci-i2s: unsupported PCM format\n"); return -EINVAL; } - dma_params->acnt = dma_params->data_type; + if (params_channels(params) == 2) { + element_cnt = 2; + if (double_fmt[fmt] && dev->enable_channel_combine) { + element_cnt = 1; + fmt = double_fmt[fmt]; + } + } + dma_params->acnt = dma_params->data_type = data_type[fmt]; dma_params->fifo_level = 0; - - rcr |= DAVINCI_MCBSP_RCR_RFRLEN1(1); - xcr |= DAVINCI_MCBSP_XCR_XFRLEN1(1); + mcbsp_word_length = asp_word_length[fmt]; + rcr |= DAVINCI_MCBSP_RCR_RFRLEN1(element_cnt - 1); + xcr |= DAVINCI_MCBSP_XCR_XFRLEN1(element_cnt - 1); rcr |= DAVINCI_MCBSP_RCR_RWDLEN1(mcbsp_word_length) | DAVINCI_MCBSP_RCR_RWDLEN2(mcbsp_word_length); @@ -515,7 +545,13 @@ static int davinci_i2s_probe(struct platform_device *pdev) ret = -ENOMEM; goto err_release_region; } - + if (pdata) { + dev->enable_channel_combine = pdata->enable_channel_combine; + dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK].sram_size = + pdata->sram_size_playback; + dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].sram_size = + pdata->sram_size_capture; + } dev->clk = clk_get(&pdev->dev, NULL); if (IS_ERR(dev->clk)) { ret = -ENODEV; @@ -549,6 +585,7 @@ static int davinci_i2s_probe(struct platform_device *pdev) dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].channel = res->start; davinci_i2s_dai.private_data = dev; + davinci_i2s_dai.dma_data = dev->dma_params; ret = snd_soc_register_dai(&davinci_i2s_dai); if (ret != 0) goto err_free_mem; diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index 50ad0519a8f..0a302e1080d 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -904,6 +904,7 @@ static int davinci_mcasp_probe(struct platform_device *pdev) dma_data->channel = res->start; davinci_mcasp_dai[pdata->op_mode].private_data = dev; + davinci_mcasp_dai[pdata->op_mode].dma_data = dev->dma_params; davinci_mcasp_dai[pdata->op_mode].dev = &pdev->dev; ret = snd_soc_register_dai(&davinci_mcasp_dai[pdata->op_mode]); diff --git a/sound/soc/davinci/davinci-mcasp.h b/sound/soc/davinci/davinci-mcasp.h index 9d179cc88f7..582c9249ef0 100644 --- a/sound/soc/davinci/davinci-mcasp.h +++ b/sound/soc/davinci/davinci-mcasp.h @@ -39,11 +39,6 @@ enum { }; struct davinci_audio_dev { - /* - * dma_params must be first because rtd->dai->cpu_dai->private_data - * is cast to a pointer of an array of struct davinci_pcm_dma_params in - * davinci_pcm_open. - */ struct davinci_pcm_dma_params dma_params[2]; void __iomem *base; int sample_rate; diff --git a/sound/soc/davinci/davinci-pcm.c b/sound/soc/davinci/davinci-pcm.c index fb10f1d63fd..ad4d7f47a86 100644 --- a/sound/soc/davinci/davinci-pcm.c +++ b/sound/soc/davinci/davinci-pcm.c @@ -3,6 +3,7 @@ * * Author: Vladimir Barinov, <vbarinov@embeddedalley.com> * Copyright: (C) 2007 MontaVista Software, Inc., <source@mvista.com> + * added SRAM ping/pong (C) 2008 Troy Kisky <troy.kisky@boundarydevices.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 @@ -23,10 +24,51 @@ #include <asm/dma.h> #include <mach/edma.h> +#include <mach/sram.h> #include "davinci-pcm.h" -static struct snd_pcm_hardware davinci_pcm_hardware = { +#ifdef DEBUG +static void print_buf_info(int slot, char *name) +{ + struct edmacc_param p; + if (slot < 0) + return; + edma_read_slot(slot, &p); + printk(KERN_DEBUG "%s: 0x%x, opt=%x, src=%x, a_b_cnt=%x dst=%x\n", + name, slot, p.opt, p.src, p.a_b_cnt, p.dst); + printk(KERN_DEBUG " src_dst_bidx=%x link_bcntrld=%x src_dst_cidx=%x ccnt=%x\n", + p.src_dst_bidx, p.link_bcntrld, p.src_dst_cidx, p.ccnt); +} +#else +static void print_buf_info(int slot, char *name) +{ +} +#endif + +static struct snd_pcm_hardware pcm_hardware_playback = { + .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE), + .formats = (SNDRV_PCM_FMTBIT_S16_LE), + .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_KNOT), + .rate_min = 8000, + .rate_max = 96000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = 128 * 1024, + .period_bytes_min = 32, + .period_bytes_max = 8 * 1024, + .periods_min = 16, + .periods_max = 255, + .fifo_size = 0, +}; + +static struct snd_pcm_hardware pcm_hardware_capture = { .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE), @@ -48,19 +90,63 @@ static struct snd_pcm_hardware davinci_pcm_hardware = { .fifo_size = 0, }; +/* + * How ping/pong works.... + * + * Playback: + * ram_params - copys 2*ping_size from start of SDRAM to iram, + * links to ram_link2 + * ram_link2 - copys rest of SDRAM to iram in ping_size units, + * links to ram_link + * ram_link - copys entire SDRAM to iram in ping_size uints, + * links to self + * + * asp_params - same as asp_link[0] + * asp_link[0] - copys from lower half of iram to asp port + * links to asp_link[1], triggers iram copy event on completion + * asp_link[1] - copys from upper half of iram to asp port + * links to asp_link[0], triggers iram copy event on completion + * triggers interrupt only needed to let upper SOC levels update position + * in stream on completion + * + * When playback is started: + * ram_params started + * asp_params started + * + * Capture: + * ram_params - same as ram_link, + * links to ram_link + * ram_link - same as playback + * links to self + * + * asp_params - same as playback + * asp_link[0] - same as playback + * asp_link[1] - same as playback + * + * When capture is started: + * asp_params started + */ struct davinci_runtime_data { spinlock_t lock; int period; /* current DMA period */ - int master_lch; /* Master DMA channel */ - int slave_lch; /* linked parameter RAM reload slot */ + int asp_channel; /* Master DMA channel */ + int asp_link[2]; /* asp parameter link channel, ping/pong */ struct davinci_pcm_dma_params *params; /* DMA params */ + int ram_channel; + int ram_link; + int ram_link2; + struct edmacc_param asp_params; + struct edmacc_param ram_params; }; +/* + * Not used with ping/pong + */ static void davinci_pcm_enqueue_dma(struct snd_pcm_substream *substream) { struct davinci_runtime_data *prtd = substream->runtime->private_data; struct snd_pcm_runtime *runtime = substream->runtime; - int lch = prtd->slave_lch; + int link = prtd->asp_link[0]; unsigned int period_size; unsigned int dma_offset; dma_addr_t dma_pos; @@ -78,7 +164,7 @@ static void davinci_pcm_enqueue_dma(struct snd_pcm_substream *substream) fifo_level = prtd->params->fifo_level; pr_debug("davinci_pcm: audio_set_dma_params_play channel = %d " - "dma_ptr = %x period_size=%x\n", lch, dma_pos, period_size); + "dma_ptr = %x period_size=%x\n", link, dma_pos, period_size); data_type = prtd->params->data_type; count = period_size / data_type; @@ -102,16 +188,16 @@ static void davinci_pcm_enqueue_dma(struct snd_pcm_substream *substream) } acnt = prtd->params->acnt; - edma_set_src(lch, src, INCR, W8BIT); - edma_set_dest(lch, dst, INCR, W8BIT); + edma_set_src(link, src, INCR, W8BIT); + edma_set_dest(link, dst, INCR, W8BIT); - edma_set_src_index(lch, src_bidx, src_cidx); - edma_set_dest_index(lch, dst_bidx, dst_cidx); + edma_set_src_index(link, src_bidx, src_cidx); + edma_set_dest_index(link, dst_bidx, dst_cidx); if (!fifo_level) - edma_set_transfer_params(lch, acnt, count, 1, 0, ASYNC); + edma_set_transfer_params(link, acnt, count, 1, 0, ASYNC); else - edma_set_transfer_params(lch, acnt, fifo_level, count, + edma_set_transfer_params(link, acnt, fifo_level, count, fifo_level, ABSYNC); prtd->period++; @@ -119,46 +205,295 @@ static void davinci_pcm_enqueue_dma(struct snd_pcm_substream *substream) prtd->period = 0; } -static void davinci_pcm_dma_irq(unsigned lch, u16 ch_status, void *data) +static void davinci_pcm_dma_irq(unsigned link, u16 ch_status, void *data) { struct snd_pcm_substream *substream = data; struct davinci_runtime_data *prtd = substream->runtime->private_data; - pr_debug("davinci_pcm: lch=%d, status=0x%x\n", lch, ch_status); + print_buf_info(prtd->ram_channel, "i ram_channel"); + pr_debug("davinci_pcm: link=%d, status=0x%x\n", link, ch_status); if (unlikely(ch_status != DMA_COMPLETE)) return; if (snd_pcm_running(substream)) { + if (prtd->ram_channel < 0) { + /* No ping/pong must fix up link dma data*/ + spin_lock(&prtd->lock); + davinci_pcm_enqueue_dma(substream); + spin_unlock(&prtd->lock); + } snd_pcm_period_elapsed(substream); + } +} + +static int allocate_sram(struct snd_pcm_substream *substream, unsigned size, + struct snd_pcm_hardware *ppcm) +{ + struct snd_dma_buffer *buf = &substream->dma_buffer; + struct snd_dma_buffer *iram_dma = NULL; + dma_addr_t iram_phys = 0; + void *iram_virt = NULL; + + if (buf->private_data || !size) + return 0; + + ppcm->period_bytes_max = size; + iram_virt = sram_alloc(size, &iram_phys); + if (!iram_virt) + goto exit1; + iram_dma = kzalloc(sizeof(*iram_dma), GFP_KERNEL); + if (!iram_dma) + goto exit2; + iram_dma->area = iram_virt; + iram_dma->addr = iram_phys; + memset(iram_dma->area, 0, size); + iram_dma->bytes = size; + buf->private_data = iram_dma; + return 0; +exit2: + if (iram_virt) + sram_free(iram_virt, size); +exit1: + return -ENOMEM; +} - spin_lock(&prtd->lock); - davinci_pcm_enqueue_dma(substream); - spin_unlock(&prtd->lock); +/* + * Only used with ping/pong. + * This is called after runtime->dma_addr, period_bytes and data_type are valid + */ +static int ping_pong_dma_setup(struct snd_pcm_substream *substream) +{ + unsigned short ram_src_cidx, ram_dst_cidx; + struct snd_pcm_runtime *runtime = substream->runtime; + struct davinci_runtime_data *prtd = runtime->private_data; + struct snd_dma_buffer *iram_dma = + (struct snd_dma_buffer *)substream->dma_buffer.private_data; + struct davinci_pcm_dma_params *params = prtd->params; + unsigned int data_type = params->data_type; + unsigned int acnt = params->acnt; + /* divide by 2 for ping/pong */ + unsigned int ping_size = snd_pcm_lib_period_bytes(substream) >> 1; + int link = prtd->asp_link[1]; + unsigned int fifo_level = prtd->params->fifo_level; + unsigned int count; + if ((data_type == 0) || (data_type > 4)) { + printk(KERN_ERR "%s: data_type=%i\n", __func__, data_type); + return -EINVAL; + } + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + dma_addr_t asp_src_pong = iram_dma->addr + ping_size; + ram_src_cidx = ping_size; + ram_dst_cidx = -ping_size; + edma_set_src(link, asp_src_pong, INCR, W8BIT); + + link = prtd->asp_link[0]; + edma_set_src_index(link, data_type, data_type * fifo_level); + link = prtd->asp_link[1]; + edma_set_src_index(link, data_type, data_type * fifo_level); + + link = prtd->ram_link; + edma_set_src(link, runtime->dma_addr, INCR, W32BIT); + } else { + dma_addr_t asp_dst_pong = iram_dma->addr + ping_size; + ram_src_cidx = -ping_size; + ram_dst_cidx = ping_size; + edma_set_dest(link, asp_dst_pong, INCR, W8BIT); + + link = prtd->asp_link[0]; + edma_set_dest_index(link, data_type, data_type * fifo_level); + link = prtd->asp_link[1]; + edma_set_dest_index(link, data_type, data_type * fifo_level); + + link = prtd->ram_link; + edma_set_dest(link, runtime->dma_addr, INCR, W32BIT); + } + + if (!fifo_level) { + count = ping_size / data_type; + edma_set_transfer_params(prtd->asp_link[0], acnt, count, + 1, 0, ASYNC); + edma_set_transfer_params(prtd->asp_link[1], acnt, count, + 1, 0, ASYNC); + } else { + count = ping_size / (data_type * fifo_level); + edma_set_transfer_params(prtd->asp_link[0], acnt, fifo_level, + count, fifo_level, ABSYNC); + edma_set_transfer_params(prtd->asp_link[1], acnt, fifo_level, + count, fifo_level, ABSYNC); + } + + link = prtd->ram_link; + edma_set_src_index(link, ping_size, ram_src_cidx); + edma_set_dest_index(link, ping_size, ram_dst_cidx); + edma_set_transfer_params(link, ping_size, 2, + runtime->periods, 2, ASYNC); + + /* init master params */ + edma_read_slot(prtd->asp_link[0], &prtd->asp_params); + edma_read_slot(prtd->ram_link, &prtd->ram_params); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + struct edmacc_param p_ram; + /* Copy entire iram buffer before playback started */ + prtd->ram_params.a_b_cnt = (1 << 16) | (ping_size << 1); + /* 0 dst_bidx */ + prtd->ram_params.src_dst_bidx = (ping_size << 1); + /* 0 dst_cidx */ + prtd->ram_params.src_dst_cidx = (ping_size << 1); + prtd->ram_params.ccnt = 1; + + /* Skip 1st period */ + edma_read_slot(prtd->ram_link, &p_ram); + p_ram.src += (ping_size << 1); + p_ram.ccnt -= 1; + edma_write_slot(prtd->ram_link2, &p_ram); + /* + * When 1st started, ram -> iram dma channel will fill the + * entire iram. Then, whenever a ping/pong asp buffer finishes, + * 1/2 iram will be filled. + */ + prtd->ram_params.link_bcntrld = + EDMA_CHAN_SLOT(prtd->ram_link2) << 5; } + return 0; +} + +/* 1 asp tx or rx channel using 2 parameter channels + * 1 ram to/from iram channel using 1 parameter channel + * + * Playback + * ram copy channel kicks off first, + * 1st ram copy of entire iram buffer completion kicks off asp channel + * asp tcc always kicks off ram copy of 1/2 iram buffer + * + * Record + * asp channel starts, tcc kicks off ram copy + */ +static int request_ping_pong(struct snd_pcm_substream *substream, + struct davinci_runtime_data *prtd, + struct snd_dma_buffer *iram_dma) +{ + dma_addr_t asp_src_ping; + dma_addr_t asp_dst_ping; + int link; + struct davinci_pcm_dma_params *params = prtd->params; + + /* Request ram master channel */ + link = prtd->ram_channel = edma_alloc_channel(EDMA_CHANNEL_ANY, + davinci_pcm_dma_irq, substream, + EVENTQ_1); + if (link < 0) + goto exit1; + + /* Request ram link channel */ + link = prtd->ram_link = edma_alloc_slot( + EDMA_CTLR(prtd->ram_channel), EDMA_SLOT_ANY); + if (link < 0) + goto exit2; + + link = prtd->asp_link[1] = edma_alloc_slot( + EDMA_CTLR(prtd->asp_channel), EDMA_SLOT_ANY); + if (link < 0) + goto exit3; + + prtd->ram_link2 = -1; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + link = prtd->ram_link2 = edma_alloc_slot( + EDMA_CTLR(prtd->ram_channel), EDMA_SLOT_ANY); + if (link < 0) + goto exit4; + } + /* circle ping-pong buffers */ + edma_link(prtd->asp_link[0], prtd->asp_link[1]); + edma_link(prtd->asp_link[1], prtd->asp_link[0]); + /* circle ram buffers */ + edma_link(prtd->ram_link, prtd->ram_link); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + asp_src_ping = iram_dma->addr; + asp_dst_ping = params->dma_addr; /* fifo */ + } else { + asp_src_ping = params->dma_addr; /* fifo */ + asp_dst_ping = iram_dma->addr; + } + /* ping */ + link = prtd->asp_link[0]; + edma_set_src(link, asp_src_ping, INCR, W16BIT); + edma_set_dest(link, asp_dst_ping, INCR, W16BIT); + edma_set_src_index(link, 0, 0); + edma_set_dest_index(link, 0, 0); + + edma_read_slot(link, &prtd->asp_params); + prtd->asp_params.opt &= ~(TCCMODE | EDMA_TCC(0x3f) | TCINTEN); + prtd->asp_params.opt |= TCCHEN | EDMA_TCC(prtd->ram_channel & 0x3f); + edma_write_slot(link, &prtd->asp_params); + + /* pong */ + link = prtd->asp_link[1]; + edma_set_src(link, asp_src_ping, INCR, W16BIT); + edma_set_dest(link, asp_dst_ping, INCR, W16BIT); + edma_set_src_index(link, 0, 0); + edma_set_dest_index(link, 0, 0); + + edma_read_slot(link, &prtd->asp_params); + prtd->asp_params.opt &= ~(TCCMODE | EDMA_TCC(0x3f)); + /* interrupt after every pong completion */ + prtd->asp_params.opt |= TCINTEN | TCCHEN | + EDMA_TCC(EDMA_CHAN_SLOT(prtd->ram_channel)); + edma_write_slot(link, &prtd->asp_params); + + /* ram */ + link = prtd->ram_link; + edma_set_src(link, iram_dma->addr, INCR, W32BIT); + edma_set_dest(link, iram_dma->addr, INCR, W32BIT); + pr_debug("%s: audio dma channels/slots in use for ram:%u %u %u," + "for asp:%u %u %u\n", __func__, + prtd->ram_channel, prtd->ram_link, prtd->ram_link2, + prtd->asp_channel, prtd->asp_link[0], + prtd->asp_link[1]); + return 0; +exit4: + edma_free_channel(prtd->asp_link[1]); + prtd->asp_link[1] = -1; +exit3: + edma_free_channel(prtd->ram_link); + prtd->ram_link = -1; +exit2: + edma_free_channel(prtd->ram_channel); + prtd->ram_channel = -1; +exit1: + return link; } static int davinci_pcm_dma_request(struct snd_pcm_substream *substream) { + struct snd_dma_buffer *iram_dma; struct davinci_runtime_data *prtd = substream->runtime->private_data; - struct edmacc_param p_ram; - int ret; + struct davinci_pcm_dma_params *params = prtd->params; + int link; - /* Request master DMA channel */ - ret = edma_alloc_channel(prtd->params->channel, - davinci_pcm_dma_irq, substream, - EVENTQ_0); - if (ret < 0) - return ret; - prtd->master_lch = ret; + if (!params) + return -ENODEV; - /* Request parameter RAM reload slot */ - ret = edma_alloc_slot(EDMA_CTLR(prtd->master_lch), EDMA_SLOT_ANY); - if (ret < 0) { - edma_free_channel(prtd->master_lch); - return ret; + /* Request asp master DMA channel */ + link = prtd->asp_channel = edma_alloc_channel(params->channel, + davinci_pcm_dma_irq, substream, EVENTQ_0); + if (link < 0) + goto exit1; + + /* Request asp link channels */ + link = prtd->asp_link[0] = edma_alloc_slot( + EDMA_CTLR(prtd->asp_channel), EDMA_SLOT_ANY); + if (link < 0) + goto exit2; + + iram_dma = (struct snd_dma_buffer *)substream->dma_buffer.private_data; + if (iram_dma) { + if (request_ping_pong(substream, prtd, iram_dma) == 0) + return 0; + printk(KERN_WARNING "%s: dma channel allocation failed," + "not using sram\n", __func__); } - prtd->slave_lch = ret; /* Issue transfer completion IRQ when the channel completes a * transfer, then always reload from the same slot (by a kind @@ -169,12 +504,17 @@ static int davinci_pcm_dma_request(struct snd_pcm_substream *substream) * the buffer and its length (ccnt) ... use it as a template * so davinci_pcm_enqueue_dma() takes less time in IRQ. */ - edma_read_slot(prtd->slave_lch, &p_ram); - p_ram.opt |= TCINTEN | EDMA_TCC(EDMA_CHAN_SLOT(prtd->master_lch)); - p_ram.link_bcntrld = EDMA_CHAN_SLOT(prtd->slave_lch) << 5; - edma_write_slot(prtd->slave_lch, &p_ram); - + edma_read_slot(link, &prtd->asp_params); + prtd->asp_params.opt |= TCINTEN | + EDMA_TCC(EDMA_CHAN_SLOT(prtd->asp_channel)); + prtd->asp_params.link_bcntrld = EDMA_CHAN_SLOT(link) << 5; + edma_write_slot(link, &prtd->asp_params); return 0; +exit2: + edma_free_channel(prtd->asp_channel); + prtd->asp_channel = -1; +exit1: + return link; } static int davinci_pcm_trigger(struct snd_pcm_substream *substream, int cmd) @@ -188,12 +528,12 @@ static int davinci_pcm_trigger(struct snd_pcm_substream *substream, int cmd) case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - edma_start(prtd->master_lch); + edma_resume(prtd->asp_channel); break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - edma_stop(prtd->master_lch); + edma_pause(prtd->asp_channel); break; default: ret = -EINVAL; @@ -208,15 +548,37 @@ static int davinci_pcm_trigger(struct snd_pcm_substream *substream, int cmd) static int davinci_pcm_prepare(struct snd_pcm_substream *substream) { struct davinci_runtime_data *prtd = substream->runtime->private_data; - struct edmacc_param temp; + if (prtd->ram_channel >= 0) { + int ret = ping_pong_dma_setup(substream); + if (ret < 0) + return ret; + + edma_write_slot(prtd->ram_channel, &prtd->ram_params); + edma_write_slot(prtd->asp_channel, &prtd->asp_params); + + print_buf_info(prtd->ram_channel, "ram_channel"); + print_buf_info(prtd->ram_link, "ram_link"); + print_buf_info(prtd->ram_link2, "ram_link2"); + print_buf_info(prtd->asp_channel, "asp_channel"); + print_buf_info(prtd->asp_link[0], "asp_link[0]"); + print_buf_info(prtd->asp_link[1], "asp_link[1]"); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + /* copy 1st iram buffer */ + edma_start(prtd->ram_channel); + } + edma_start(prtd->asp_channel); + return 0; + } prtd->period = 0; davinci_pcm_enqueue_dma(substream); /* Copy self-linked parameter RAM entry into master channel */ - edma_read_slot(prtd->slave_lch, &temp); - edma_write_slot(prtd->master_lch, &temp); + edma_read_slot(prtd->asp_link[0], &prtd->asp_params); + edma_write_slot(prtd->asp_channel, &prtd->asp_params); davinci_pcm_enqueue_dma(substream); + edma_start(prtd->asp_channel); return 0; } @@ -227,20 +589,53 @@ davinci_pcm_pointer(struct snd_pcm_substream *substream) struct snd_pcm_runtime *runtime = substream->runtime; struct davinci_runtime_data *prtd = runtime->private_data; unsigned int offset; - dma_addr_t count; - dma_addr_t src, dst; + int asp_count; + dma_addr_t asp_src, asp_dst; spin_lock(&prtd->lock); - - edma_get_position(prtd->master_lch, &src, &dst); - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - count = src - runtime->dma_addr; - else - count = dst - runtime->dma_addr; - + if (prtd->ram_channel >= 0) { + int ram_count; + int mod_ram; + dma_addr_t ram_src, ram_dst; + unsigned int period_size = snd_pcm_lib_period_bytes(substream); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + /* reading ram before asp should be safe + * as long as the asp transfers less than a ping size + * of bytes between the 2 reads + */ + edma_get_position(prtd->ram_channel, + &ram_src, &ram_dst); + edma_get_position(prtd->asp_channel, + &asp_src, &asp_dst); + asp_count = asp_src - prtd->asp_params.src; + ram_count = ram_src - prtd->ram_params.src; + mod_ram = ram_count % period_size; + mod_ram -= asp_count; + if (mod_ram < 0) + mod_ram += period_size; + else if (mod_ram == 0) { + if (snd_pcm_running(substream)) + mod_ram += period_size; + } + ram_count -= mod_ram; + if (ram_count < 0) + ram_count += period_size * runtime->periods; + } else { + edma_get_position(prtd->ram_channel, + &ram_src, &ram_dst); + ram_count = ram_dst - prtd->ram_params.dst; + } + asp_count = ram_count; + } else { + edma_get_position(prtd->asp_channel, &asp_src, &asp_dst); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + asp_count = asp_src - runtime->dma_addr; + else + asp_count = asp_dst - runtime->dma_addr; + } spin_unlock(&prtd->lock); - offset = bytes_to_frames(runtime, count); + offset = bytes_to_frames(runtime, asp_count); if (offset >= runtime->buffer_size) offset = 0; @@ -251,14 +646,19 @@ static int davinci_pcm_open(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct davinci_runtime_data *prtd; + struct snd_pcm_hardware *ppcm; int ret = 0; struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct davinci_pcm_dma_params *pa = rtd->dai->cpu_dai->private_data; - struct davinci_pcm_dma_params *params = &pa[substream->stream]; - if (!params) + struct davinci_pcm_dma_params *pa = rtd->dai->cpu_dai->dma_data; + struct davinci_pcm_dma_params *params; + if (!pa) return -ENODEV; + params = &pa[substream->stream]; - snd_soc_set_runtime_hwparams(substream, &davinci_pcm_hardware); + ppcm = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? + &pcm_hardware_playback : &pcm_hardware_capture; + allocate_sram(substream, params->sram_size, ppcm); + snd_soc_set_runtime_hwparams(substream, ppcm); /* ensure that buffer size is a multiple of period size */ ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); @@ -271,6 +671,11 @@ static int davinci_pcm_open(struct snd_pcm_substream *substream) spin_lock_init(&prtd->lock); prtd->params = params; + prtd->asp_channel = -1; + prtd->asp_link[0] = prtd->asp_link[1] = -1; + prtd->ram_channel = -1; + prtd->ram_link = -1; + prtd->ram_link2 = -1; runtime->private_data = prtd; @@ -288,10 +693,29 @@ static int davinci_pcm_close(struct snd_pcm_substream *substream) struct snd_pcm_runtime *runtime = substream->runtime; struct davinci_runtime_data *prtd = runtime->private_data; - edma_unlink(prtd->slave_lch); - - edma_free_slot(prtd->slave_lch); - edma_free_channel(prtd->master_lch); + if (prtd->ram_channel >= 0) + edma_stop(prtd->ram_channel); + if (prtd->asp_channel >= 0) + edma_stop(prtd->asp_channel); + if (prtd->asp_link[0] >= 0) + edma_unlink(prtd->asp_link[0]); + if (prtd->asp_link[1] >= 0) + edma_unlink(prtd->asp_link[1]); + if (prtd->ram_link >= 0) + edma_unlink(prtd->ram_link); + + if (prtd->asp_link[0] >= 0) + edma_free_slot(prtd->asp_link[0]); + if (prtd->asp_link[1] >= 0) + edma_free_slot(prtd->asp_link[1]); + if (prtd->asp_channel >= 0) + edma_free_channel(prtd->asp_channel); + if (prtd->ram_link >= 0) + edma_free_slot(prtd->ram_link); + if (prtd->ram_link2 >= 0) + edma_free_slot(prtd->ram_link2); + if (prtd->ram_channel >= 0) + edma_free_channel(prtd->ram_channel); kfree(prtd); @@ -333,11 +757,11 @@ static struct snd_pcm_ops davinci_pcm_ops = { .mmap = davinci_pcm_mmap, }; -static int davinci_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) +static int davinci_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream, + size_t size) { struct snd_pcm_substream *substream = pcm->streams[stream].substream; struct snd_dma_buffer *buf = &substream->dma_buffer; - size_t size = davinci_pcm_hardware.buffer_bytes_max; buf->dev.type = SNDRV_DMA_TYPE_DEV; buf->dev.dev = pcm->card->dev; @@ -362,6 +786,7 @@ static void davinci_pcm_free(struct snd_pcm *pcm) int stream; for (stream = 0; stream < 2; stream++) { + struct snd_dma_buffer *iram_dma; substream = pcm->streams[stream].substream; if (!substream) continue; @@ -373,6 +798,11 @@ static void davinci_pcm_free(struct snd_pcm *pcm) dma_free_writecombine(pcm->card->dev, buf->bytes, buf->area, buf->addr); buf->area = NULL; + iram_dma = (struct snd_dma_buffer *)buf->private_data; + if (iram_dma) { + sram_free(iram_dma->area, iram_dma->bytes); + kfree(iram_dma); + } } } @@ -390,14 +820,16 @@ static int davinci_pcm_new(struct snd_card *card, if (dai->playback.channels_min) { ret = davinci_pcm_preallocate_dma_buffer(pcm, - SNDRV_PCM_STREAM_PLAYBACK); + SNDRV_PCM_STREAM_PLAYBACK, + pcm_hardware_playback.buffer_bytes_max); if (ret) return ret; } if (dai->capture.channels_min) { ret = davinci_pcm_preallocate_dma_buffer(pcm, - SNDRV_PCM_STREAM_CAPTURE); + SNDRV_PCM_STREAM_CAPTURE, + pcm_hardware_capture.buffer_bytes_max); if (ret) return ret; } diff --git a/sound/soc/davinci/davinci-pcm.h b/sound/soc/davinci/davinci-pcm.h index c8b0d2baf05..0764944cf10 100644 --- a/sound/soc/davinci/davinci-pcm.h +++ b/sound/soc/davinci/davinci-pcm.h @@ -20,6 +20,7 @@ struct davinci_pcm_dma_params { int channel; /* sync dma channel ID */ unsigned short acnt; dma_addr_t dma_addr; /* device physical address for DMA */ + unsigned sram_size; enum dma_event_q eventq_no; /* event queue number */ unsigned char data_type; /* xfer data type */ unsigned char convert_mono_stereo; diff --git a/sound/soc/fsl/mpc5200_dma.c b/sound/soc/fsl/mpc5200_dma.c index 6096d22283e..30ed568afb2 100644 --- a/sound/soc/fsl/mpc5200_dma.c +++ b/sound/soc/fsl/mpc5200_dma.c @@ -58,47 +58,15 @@ static void psc_dma_bcom_enqueue_next_buffer(struct psc_dma_stream *s) /* Prepare and enqueue the next buffer descriptor */ bd = bcom_prepare_next_buffer(s->bcom_task); bd->status = s->period_bytes; - bd->data[0] = s->period_next_pt; + bd->data[0] = s->runtime->dma_addr + (s->period_next * s->period_bytes); bcom_submit_next_buffer(s->bcom_task, NULL); /* Update for next period */ - s->period_next_pt += s->period_bytes; - if (s->period_next_pt >= s->period_end) - s->period_next_pt = s->period_start; -} - -static void psc_dma_bcom_enqueue_tx(struct psc_dma_stream *s) -{ - if (s->appl_ptr > s->runtime->control->appl_ptr) { - /* - * In this case s->runtime->control->appl_ptr has wrapped around. - * Play the data to the end of the boundary, then wrap our own - * appl_ptr back around. - */ - while (s->appl_ptr < s->runtime->boundary) { - if (bcom_queue_full(s->bcom_task)) - return; - - s->appl_ptr += s->period_size; - - psc_dma_bcom_enqueue_next_buffer(s); - } - s->appl_ptr -= s->runtime->boundary; - } - - while (s->appl_ptr < s->runtime->control->appl_ptr) { - - if (bcom_queue_full(s->bcom_task)) - return; - - s->appl_ptr += s->period_size; - - psc_dma_bcom_enqueue_next_buffer(s); - } + s->period_next = (s->period_next + 1) % s->runtime->periods; } /* Bestcomm DMA irq handler */ -static irqreturn_t psc_dma_bcom_irq_tx(int irq, void *_psc_dma_stream) +static irqreturn_t psc_dma_bcom_irq(int irq, void *_psc_dma_stream) { struct psc_dma_stream *s = _psc_dma_stream; @@ -108,34 +76,8 @@ static irqreturn_t psc_dma_bcom_irq_tx(int irq, void *_psc_dma_stream) while (bcom_buffer_done(s->bcom_task)) { bcom_retrieve_buffer(s->bcom_task, NULL, NULL); - s->period_current_pt += s->period_bytes; - if (s->period_current_pt >= s->period_end) - s->period_current_pt = s->period_start; - } - psc_dma_bcom_enqueue_tx(s); - spin_unlock(&s->psc_dma->lock); - - /* If the stream is active, then also inform the PCM middle layer - * of the period finished event. */ - if (s->active) - snd_pcm_period_elapsed(s->stream); - - return IRQ_HANDLED; -} - -static irqreturn_t psc_dma_bcom_irq_rx(int irq, void *_psc_dma_stream) -{ - struct psc_dma_stream *s = _psc_dma_stream; - - spin_lock(&s->psc_dma->lock); - /* For each finished period, dequeue the completed period buffer - * and enqueue a new one in it's place. */ - while (bcom_buffer_done(s->bcom_task)) { - bcom_retrieve_buffer(s->bcom_task, NULL, NULL); - - s->period_current_pt += s->period_bytes; - if (s->period_current_pt >= s->period_end) - s->period_current_pt = s->period_start; + s->period_current = (s->period_current+1) % s->runtime->periods; + s->period_count++; psc_dma_bcom_enqueue_next_buffer(s); } @@ -166,54 +108,38 @@ static int psc_dma_trigger(struct snd_pcm_substream *substream, int cmd) struct snd_soc_pcm_runtime *rtd = substream->private_data; struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data; struct snd_pcm_runtime *runtime = substream->runtime; - struct psc_dma_stream *s; + struct psc_dma_stream *s = to_psc_dma_stream(substream, psc_dma); struct mpc52xx_psc __iomem *regs = psc_dma->psc_regs; u16 imr; unsigned long flags; int i; - if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) - s = &psc_dma->capture; - else - s = &psc_dma->playback; - - dev_dbg(psc_dma->dev, "psc_dma_trigger(substream=%p, cmd=%i)" - " stream_id=%i\n", - substream, cmd, substream->pstr->stream); - switch (cmd) { case SNDRV_PCM_TRIGGER_START: + dev_dbg(psc_dma->dev, "START: stream=%i fbits=%u ps=%u #p=%u\n", + substream->pstr->stream, runtime->frame_bits, + (int)runtime->period_size, runtime->periods); s->period_bytes = frames_to_bytes(runtime, runtime->period_size); - s->period_start = virt_to_phys(runtime->dma_area); - s->period_end = s->period_start + - (s->period_bytes * runtime->periods); - s->period_next_pt = s->period_start; - s->period_current_pt = s->period_start; - s->period_size = runtime->period_size; + s->period_next = 0; + s->period_current = 0; s->active = 1; - - /* track appl_ptr so that we have a better chance of detecting - * end of stream and not over running it. - */ + s->period_count = 0; s->runtime = runtime; - s->appl_ptr = s->runtime->control->appl_ptr - - (runtime->period_size * runtime->periods); /* Fill up the bestcomm bd queue and enable DMA. * This will begin filling the PSC's fifo. */ spin_lock_irqsave(&psc_dma->lock, flags); - if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) { + if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) bcom_gen_bd_rx_reset(s->bcom_task); - for (i = 0; i < runtime->periods; i++) - if (!bcom_queue_full(s->bcom_task)) - psc_dma_bcom_enqueue_next_buffer(s); - } else { + else bcom_gen_bd_tx_reset(s->bcom_task); - psc_dma_bcom_enqueue_tx(s); - } + + for (i = 0; i < runtime->periods; i++) + if (!bcom_queue_full(s->bcom_task)) + psc_dma_bcom_enqueue_next_buffer(s); bcom_enable(s->bcom_task); spin_unlock_irqrestore(&psc_dma->lock, flags); @@ -223,6 +149,8 @@ static int psc_dma_trigger(struct snd_pcm_substream *substream, int cmd) break; case SNDRV_PCM_TRIGGER_STOP: + dev_dbg(psc_dma->dev, "STOP: stream=%i periods_count=%i\n", + substream->pstr->stream, s->period_count); s->active = 0; spin_lock_irqsave(&psc_dma->lock, flags); @@ -236,7 +164,8 @@ static int psc_dma_trigger(struct snd_pcm_substream *substream, int cmd) break; default: - dev_dbg(psc_dma->dev, "invalid command\n"); + dev_dbg(psc_dma->dev, "unhandled trigger: stream=%i cmd=%i\n", + substream->pstr->stream, cmd); return -EINVAL; } @@ -343,7 +272,7 @@ psc_dma_pointer(struct snd_pcm_substream *substream) else s = &psc_dma->playback; - count = s->period_current_pt - s->period_start; + count = s->period_current * s->period_bytes; return bytes_to_frames(substream->runtime, count); } @@ -532,11 +461,9 @@ int mpc5200_audio_dma_create(struct of_device *op) rc = request_irq(psc_dma->irq, &psc_dma_status_irq, IRQF_SHARED, "psc-dma-status", psc_dma); - rc |= request_irq(psc_dma->capture.irq, - &psc_dma_bcom_irq_rx, IRQF_SHARED, + rc |= request_irq(psc_dma->capture.irq, &psc_dma_bcom_irq, IRQF_SHARED, "psc-dma-capture", &psc_dma->capture); - rc |= request_irq(psc_dma->playback.irq, - &psc_dma_bcom_irq_tx, IRQF_SHARED, + rc |= request_irq(psc_dma->playback.irq, &psc_dma_bcom_irq, IRQF_SHARED, "psc-dma-playback", &psc_dma->playback); if (rc) { ret = -ENODEV; diff --git a/sound/soc/fsl/mpc5200_dma.h b/sound/soc/fsl/mpc5200_dma.h index 8d396bb9d9f..22208b373fb 100644 --- a/sound/soc/fsl/mpc5200_dma.h +++ b/sound/soc/fsl/mpc5200_dma.h @@ -13,26 +13,25 @@ * @psc_dma: pointer back to parent psc_dma data structure * @bcom_task: bestcomm task structure * @irq: irq number for bestcomm task - * @period_start: physical address of start of DMA region * @period_end: physical address of end of DMA region * @period_next_pt: physical address of next DMA buffer to enqueue * @period_bytes: size of DMA period in bytes + * @ac97_slot_bits: Enable bits for turning on the correct AC97 slot */ struct psc_dma_stream { struct snd_pcm_runtime *runtime; - snd_pcm_uframes_t appl_ptr; - int active; struct psc_dma *psc_dma; struct bcom_task *bcom_task; int irq; struct snd_pcm_substream *stream; - dma_addr_t period_start; - dma_addr_t period_end; - dma_addr_t period_next_pt; - dma_addr_t period_current_pt; + int period_next; + int period_current; int period_bytes; - int period_size; + int period_count; + + /* AC97 state */ + u32 ac97_slot_bits; }; /** @@ -73,6 +72,15 @@ struct psc_dma { } stats; }; +/* Utility for retrieving psc_dma_stream structure from a substream */ +inline struct psc_dma_stream * +to_psc_dma_stream(struct snd_pcm_substream *substream, struct psc_dma *psc_dma) +{ + if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) + return &psc_dma->capture; + return &psc_dma->playback; +} + int mpc5200_audio_dma_create(struct of_device *op); int mpc5200_audio_dma_destroy(struct of_device *op); diff --git a/sound/soc/fsl/mpc5200_psc_ac97.c b/sound/soc/fsl/mpc5200_psc_ac97.c index c4ae3e096bb..3dbc7f7cd7b 100644 --- a/sound/soc/fsl/mpc5200_psc_ac97.c +++ b/sound/soc/fsl/mpc5200_psc_ac97.c @@ -130,6 +130,7 @@ static int psc_ac97_hw_analog_params(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai) { struct psc_dma *psc_dma = cpu_dai->private_data; + struct psc_dma_stream *s = to_psc_dma_stream(substream, psc_dma); dev_dbg(psc_dma->dev, "%s(substream=%p) p_size=%i p_bytes=%i" " periods=%i buffer_size=%i buffer_bytes=%i channels=%i" @@ -140,20 +141,10 @@ static int psc_ac97_hw_analog_params(struct snd_pcm_substream *substream, params_channels(params), params_rate(params), params_format(params)); - - if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) { - if (params_channels(params) == 1) - psc_dma->slots |= 0x00000100; - else - psc_dma->slots |= 0x00000300; - } else { - if (params_channels(params) == 1) - psc_dma->slots |= 0x01000000; - else - psc_dma->slots |= 0x03000000; - } - out_be32(&psc_dma->psc_regs->ac97_slots, psc_dma->slots); - + /* Determine the set of enable bits to turn on */ + s->ac97_slot_bits = (params_channels(params) == 1) ? 0x100 : 0x300; + if (substream->pstr->stream != SNDRV_PCM_STREAM_CAPTURE) + s->ac97_slot_bits <<= 16; return 0; } @@ -163,6 +154,8 @@ static int psc_ac97_hw_digital_params(struct snd_pcm_substream *substream, { struct psc_dma *psc_dma = cpu_dai->private_data; + dev_dbg(psc_dma->dev, "%s(substream=%p)\n", __func__, substream); + if (params_channels(params) == 1) out_be32(&psc_dma->psc_regs->ac97_slots, 0x01000000); else @@ -176,14 +169,24 @@ static int psc_ac97_trigger(struct snd_pcm_substream *substream, int cmd, { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data; + struct psc_dma_stream *s = to_psc_dma_stream(substream, psc_dma); switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + dev_dbg(psc_dma->dev, "AC97 START: stream=%i\n", + substream->pstr->stream); + + /* Set the slot enable bits */ + psc_dma->slots |= s->ac97_slot_bits; + out_be32(&psc_dma->psc_regs->ac97_slots, psc_dma->slots); + break; + case SNDRV_PCM_TRIGGER_STOP: - if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) - psc_dma->slots &= 0xFFFF0000; - else - psc_dma->slots &= 0x0000FFFF; + dev_dbg(psc_dma->dev, "AC97 STOP: stream=%i\n", + substream->pstr->stream); + /* Clear the slot enable bits */ + psc_dma->slots &= ~(s->ac97_slot_bits); out_be32(&psc_dma->psc_regs->ac97_slots, psc_dma->slots); break; } diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig index 2dee9839be8..61952aa6cd5 100644 --- a/sound/soc/omap/Kconfig +++ b/sound/soc/omap/Kconfig @@ -21,7 +21,18 @@ config SND_OMAP_SOC_AMS_DELTA select SND_OMAP_SOC_MCBSP select SND_SOC_CX20442 help - Say Y if you want to add support for SoC audio on Amstrad Delta. + Say Y if you want to add support for SoC audio device connected to + a handset and a speakerphone found on Amstrad E3 (Delta) videophone. + + Note that in order to get those devices fully supported, you have to + build the kernel with standard serial port driver included and + configured for at least 4 ports. Then, from userspace, you must load + a line discipline #19 on the modem (ttyS3) serial line. The simplest + way to achieve this is to install util-linux-ng and use the included + ldattach utility. This can be started automatically from udev, + a simple rule like this one should do the trick (it does for me): + ACTION=="add", KERNEL=="controlC0", \ + RUN+="/usr/sbin/ldattach 19 /dev/ttyS3" config SND_OMAP_SOC_OSK5912 tristate "SoC Audio support for omap osk5912" @@ -32,12 +43,13 @@ config SND_OMAP_SOC_OSK5912 Say Y if you want to add support for SoC audio on osk5912. config SND_OMAP_SOC_OVERO - tristate "SoC Audio support for Gumstix Overo" - depends on TWL4030_CORE && SND_OMAP_SOC && MACH_OVERO + tristate "SoC Audio support for Gumstix Overo and CompuLab CM-T35" + depends on TWL4030_CORE && SND_OMAP_SOC && (MACH_OVERO || MACH_CM_T35) select SND_OMAP_SOC_MCBSP select SND_SOC_TWL4030 help - Say Y if you want to add support for SoC audio on the Gumstix Overo. + Say Y if you want to add support for SoC audio on the + Gumstix Overo or CompuLab CM-T35 config SND_OMAP_SOC_OMAP2EVM tristate "SoC Audio support for OMAP2EVM board" @@ -55,6 +67,15 @@ config SND_OMAP_SOC_OMAP3EVM help Say Y if you want to add support for SoC audio on the omap3evm board. +config SND_OMAP_SOC_AM3517EVM + tristate "SoC Audio support for OMAP3517 / AM3517 EVM" + depends on SND_OMAP_SOC && MACH_OMAP3517EVM && I2C + select SND_OMAP_SOC_MCBSP + select SND_SOC_TLV320AIC23 + help + Say Y if you want to add support for SoC audio on the OMAP3517 / AM3517 + EVM. + config SND_OMAP_SOC_SDP3430 tristate "SoC Audio support for Texas Instruments SDP3430" depends on TWL4030_CORE && SND_OMAP_SOC && MACH_OMAP_3430SDP @@ -88,3 +109,10 @@ config SND_OMAP_SOC_ZOOM2 help Say Y if you want to add support for Soc audio on Zoom2 board. +config SND_OMAP_SOC_IGEP0020 + tristate "SoC Audio support for IGEP v2" + depends on TWL4030_CORE && SND_OMAP_SOC && MACH_IGEP0020 + select SND_OMAP_SOC_MCBSP + select SND_SOC_TWL4030 + help + Say Y if you want to add support for Soc audio on IGEP v2 board. diff --git a/sound/soc/omap/Makefile b/sound/soc/omap/Makefile index 02d69471dcb..d49458a29bb 100644 --- a/sound/soc/omap/Makefile +++ b/sound/soc/omap/Makefile @@ -12,10 +12,12 @@ snd-soc-osk5912-objs := osk5912.o snd-soc-overo-objs := overo.o snd-soc-omap2evm-objs := omap2evm.o snd-soc-omap3evm-objs := omap3evm.o +snd-soc-am3517evm-objs := am3517evm.o snd-soc-sdp3430-objs := sdp3430.o snd-soc-omap3pandora-objs := omap3pandora.o snd-soc-omap3beagle-objs := omap3beagle.o snd-soc-zoom2-objs := zoom2.o +snd-soc-igep0020-objs := igep0020.o obj-$(CONFIG_SND_OMAP_SOC_N810) += snd-soc-n810.o obj-$(CONFIG_SND_OMAP_SOC_AMS_DELTA) += snd-soc-ams-delta.o @@ -23,7 +25,9 @@ obj-$(CONFIG_SND_OMAP_SOC_OSK5912) += snd-soc-osk5912.o obj-$(CONFIG_SND_OMAP_SOC_OVERO) += snd-soc-overo.o obj-$(CONFIG_MACH_OMAP2EVM) += snd-soc-omap2evm.o obj-$(CONFIG_MACH_OMAP3EVM) += snd-soc-omap3evm.o +obj-$(CONFIG_MACH_OMAP3517EVM) += snd-soc-am3517evm.o obj-$(CONFIG_SND_OMAP_SOC_SDP3430) += snd-soc-sdp3430.o obj-$(CONFIG_SND_OMAP_SOC_OMAP3_PANDORA) += snd-soc-omap3pandora.o obj-$(CONFIG_SND_OMAP_SOC_OMAP3_BEAGLE) += snd-soc-omap3beagle.o obj-$(CONFIG_SND_OMAP_SOC_ZOOM2) += snd-soc-zoom2.o +obj-$(CONFIG_SND_OMAP_SOC_IGEP0020) += snd-soc-igep0020.o diff --git a/sound/soc/omap/am3517evm.c b/sound/soc/omap/am3517evm.c new file mode 100644 index 00000000000..135901b2ea1 --- /dev/null +++ b/sound/soc/omap/am3517evm.c @@ -0,0 +1,202 @@ +/* + * am3517evm.c -- ALSA SoC support for OMAP3517 / AM3517 EVM + * + * Author: Anuj Aggarwal <anuj.aggarwal@ti.com> + * + * Based on sound/soc/omap/beagle.c by Steve Sakoman + * + * Copyright (C) 2009 Texas Instruments Incorporated + * + * 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 version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind, + * whether express or implied; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include <linux/clk.h> +#include <linux/platform_device.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/hardware.h> +#include <mach/gpio.h> +#include <plat/mcbsp.h> + +#include "omap-mcbsp.h" +#include "omap-pcm.h" + +#include "../codecs/tlv320aic23.h" + +#define CODEC_CLOCK 12000000 + +static int am3517evm_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 *codec_dai = rtd->dai->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; + int ret; + + /* Set codec DAI configuration */ + ret = snd_soc_dai_set_fmt(codec_dai, + SND_SOC_DAIFMT_DSP_B | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM); + if (ret < 0) { + printk(KERN_ERR "can't set codec DAI configuration\n"); + return ret; + } + + /* Set cpu DAI configuration */ + ret = snd_soc_dai_set_fmt(cpu_dai, + SND_SOC_DAIFMT_DSP_B | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM); + if (ret < 0) { + printk(KERN_ERR "can't set cpu DAI configuration\n"); + return ret; + } + + /* Set the codec system clock for DAC and ADC */ + ret = snd_soc_dai_set_sysclk(codec_dai, 0, + CODEC_CLOCK, SND_SOC_CLOCK_IN); + if (ret < 0) { + printk(KERN_ERR "can't set codec system clock\n"); + return ret; + } + + ret = snd_soc_dai_set_sysclk(cpu_dai, OMAP_MCBSP_CLKR_SRC_CLKX, 0, + SND_SOC_CLOCK_IN); + if (ret < 0) { + printk(KERN_ERR "can't set CPU system clock OMAP_MCBSP_CLKR_SRC_CLKX\n"); + return ret; + } + + snd_soc_dai_set_sysclk(cpu_dai, OMAP_MCBSP_FSR_SRC_FSX, 0, + SND_SOC_CLOCK_IN); + if (ret < 0) { + printk(KERN_ERR "can't set CPU system clock OMAP_MCBSP_FSR_SRC_FSX\n"); + return ret; + } + + return 0; +} + +static struct snd_soc_ops am3517evm_ops = { + .hw_params = am3517evm_hw_params, +}; + +/* am3517evm machine dapm widgets */ +static const struct snd_soc_dapm_widget tlv320aic23_dapm_widgets[] = { + SND_SOC_DAPM_HP("Line Out", NULL), + SND_SOC_DAPM_LINE("Line In", NULL), + SND_SOC_DAPM_MIC("Mic In", NULL), +}; + +static const struct snd_soc_dapm_route audio_map[] = { + /* Line Out connected to LLOUT, RLOUT */ + {"Line Out", NULL, "LOUT"}, + {"Line Out", NULL, "ROUT"}, + + {"LLINEIN", NULL, "Line In"}, + {"RLINEIN", NULL, "Line In"}, + + {"MICIN", NULL, "Mic In"}, +}; + +static int am3517evm_aic23_init(struct snd_soc_codec *codec) +{ + /* Add am3517-evm specific widgets */ + snd_soc_dapm_new_controls(codec, tlv320aic23_dapm_widgets, + ARRAY_SIZE(tlv320aic23_dapm_widgets)); + + /* Set up davinci-evm specific audio path audio_map */ + snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + + /* always connected */ + snd_soc_dapm_enable_pin(codec, "Line Out"); + snd_soc_dapm_enable_pin(codec, "Line In"); + snd_soc_dapm_enable_pin(codec, "Mic In"); + + snd_soc_dapm_sync(codec); + + return 0; +} + +/* Digital audio interface glue - connects codec <--> CPU */ +static struct snd_soc_dai_link am3517evm_dai = { + .name = "TLV320AIC23", + .stream_name = "AIC23", + .cpu_dai = &omap_mcbsp_dai[0], + .codec_dai = &tlv320aic23_dai, + .init = am3517evm_aic23_init, + .ops = &am3517evm_ops, +}; + +/* Audio machine driver */ +static struct snd_soc_card snd_soc_am3517evm = { + .name = "am3517evm", + .platform = &omap_soc_platform, + .dai_link = &am3517evm_dai, + .num_links = 1, +}; + +/* Audio subsystem */ +static struct snd_soc_device am3517evm_snd_devdata = { + .card = &snd_soc_am3517evm, + .codec_dev = &soc_codec_dev_tlv320aic23, +}; + +static struct platform_device *am3517evm_snd_device; + +static int __init am3517evm_soc_init(void) +{ + int ret; + + if (!machine_is_omap3517evm()) { + pr_err("Not OMAP3517 / AM3517 EVM!\n"); + return -ENODEV; + } + pr_info("OMAP3517 / AM3517 EVM SoC init\n"); + + am3517evm_snd_device = platform_device_alloc("soc-audio", -1); + if (!am3517evm_snd_device) { + printk(KERN_ERR "Platform device allocation failed\n"); + return -ENOMEM; + } + + platform_set_drvdata(am3517evm_snd_device, &am3517evm_snd_devdata); + am3517evm_snd_devdata.dev = &am3517evm_snd_device->dev; + *(unsigned int *)am3517evm_dai.cpu_dai->private_data = 0; /* McBSP1 */ + + ret = platform_device_add(am3517evm_snd_device); + if (ret) + goto err1; + + return 0; + +err1: + printk(KERN_ERR "Unable to add platform device\n"); + platform_device_put(am3517evm_snd_device); + + return ret; +} + +static void __exit am3517evm_soc_exit(void) +{ + platform_device_unregister(am3517evm_snd_device); +} + +module_init(am3517evm_soc_init); +module_exit(am3517evm_soc_exit); + +MODULE_AUTHOR("Anuj Aggarwal <anuj.aggarwal@ti.com>"); +MODULE_DESCRIPTION("ALSA SoC OMAP3517 / AM3517 EVM"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/omap/igep0020.c b/sound/soc/omap/igep0020.c new file mode 100644 index 00000000000..3583c429f9b --- /dev/null +++ b/sound/soc/omap/igep0020.c @@ -0,0 +1,148 @@ +/* + * igep0020.c -- SoC audio for IGEP v2 + * + * Based on sound/soc/omap/overo.c by 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 + * + */ + +#include <linux/clk.h> +#include <linux/platform_device.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/hardware.h> +#include <mach/gpio.h> +#include <plat/mcbsp.h> + +#include "omap-mcbsp.h" +#include "omap-pcm.h" +#include "../codecs/twl4030.h" + +static int igep2_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 *codec_dai = rtd->dai->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; + int ret; + + /* Set codec DAI configuration */ + ret = snd_soc_dai_set_fmt(codec_dai, + SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM); + if (ret < 0) { + printk(KERN_ERR "can't set codec DAI configuration\n"); + return ret; + } + + /* Set cpu DAI configuration */ + ret = snd_soc_dai_set_fmt(cpu_dai, + SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM); + if (ret < 0) { + printk(KERN_ERR "can't set cpu DAI configuration\n"); + return ret; + } + + /* Set the codec system clock for DAC and ADC */ + ret = snd_soc_dai_set_sysclk(codec_dai, 0, 26000000, + SND_SOC_CLOCK_IN); + if (ret < 0) { + printk(KERN_ERR "can't set codec system clock\n"); + return ret; + } + + return 0; +} + +static struct snd_soc_ops igep2_ops = { + .hw_params = igep2_hw_params, +}; + +/* Digital audio interface glue - connects codec <--> CPU */ +static struct snd_soc_dai_link igep2_dai = { + .name = "TWL4030", + .stream_name = "TWL4030", + .cpu_dai = &omap_mcbsp_dai[0], + .codec_dai = &twl4030_dai[TWL4030_DAI_HIFI], + .ops = &igep2_ops, +}; + +/* Audio machine driver */ +static struct snd_soc_card snd_soc_card_igep2 = { + .name = "igep2", + .platform = &omap_soc_platform, + .dai_link = &igep2_dai, + .num_links = 1, +}; + +/* Audio subsystem */ +static struct snd_soc_device igep2_snd_devdata = { + .card = &snd_soc_card_igep2, + .codec_dev = &soc_codec_dev_twl4030, +}; + +static struct platform_device *igep2_snd_device; + +static int __init igep2_soc_init(void) +{ + int ret; + + if (!machine_is_igep0020()) { + pr_debug("Not IGEP v2!\n"); + return -ENODEV; + } + printk(KERN_INFO "IGEP v2 SoC init\n"); + + igep2_snd_device = platform_device_alloc("soc-audio", -1); + if (!igep2_snd_device) { + printk(KERN_ERR "Platform device allocation failed\n"); + return -ENOMEM; + } + + platform_set_drvdata(igep2_snd_device, &igep2_snd_devdata); + igep2_snd_devdata.dev = &igep2_snd_device->dev; + *(unsigned int *)igep2_dai.cpu_dai->private_data = 1; /* McBSP2 */ + + ret = platform_device_add(igep2_snd_device); + if (ret) + goto err1; + + return 0; + +err1: + printk(KERN_ERR "Unable to add platform device\n"); + platform_device_put(igep2_snd_device); + + return ret; +} +module_init(igep2_soc_init); + +static void __exit igep2_soc_exit(void) +{ + platform_device_unregister(igep2_snd_device); +} +module_exit(igep2_soc_exit); + +MODULE_AUTHOR("Enric Balletbo i Serra <eballetbo@iseebcn.com>"); +MODULE_DESCRIPTION("ALSA SoC IGEP v2"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/omap/omap-mcbsp.c b/sound/soc/omap/omap-mcbsp.c index 3341f49402c..45be94201c8 100644 --- a/sound/soc/omap/omap-mcbsp.c +++ b/sound/soc/omap/omap-mcbsp.c @@ -49,6 +49,8 @@ struct omap_mcbsp_data { */ int active; int configured; + unsigned int in_freq; + int clk_div; }; #define to_mcbsp(priv) container_of((priv), struct omap_mcbsp_data, bus_id) @@ -257,7 +259,7 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream, int dma, bus_id = mcbsp_data->bus_id, id = cpu_dai->id; int wlen, channels, wpf, sync_mode = OMAP_DMA_SYNC_ELEMENT; unsigned long port; - unsigned int format; + unsigned int format, div, framesize, master; if (cpu_class_is_omap1()) { dma = omap1_dma_reqs[bus_id][substream->stream]; @@ -294,28 +296,19 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream, format = mcbsp_data->fmt & SND_SOC_DAIFMT_FORMAT_MASK; wpf = channels = params_channels(params); - switch (channels) { - case 2: - if (format == SND_SOC_DAIFMT_I2S) { - /* Use dual-phase frames */ - regs->rcr2 |= RPHASE; - regs->xcr2 |= XPHASE; - /* Set 1 word per (McBSP) frame for phase1 and phase2 */ - wpf--; - regs->rcr2 |= RFRLEN2(wpf - 1); - regs->xcr2 |= XFRLEN2(wpf - 1); - } - case 1: - case 4: - /* Set word per (McBSP) frame for phase1 */ - regs->rcr1 |= RFRLEN1(wpf - 1); - regs->xcr1 |= XFRLEN1(wpf - 1); - break; - default: - /* Unsupported number of channels */ - return -EINVAL; + if (channels == 2 && format == SND_SOC_DAIFMT_I2S) { + /* Use dual-phase frames */ + regs->rcr2 |= RPHASE; + regs->xcr2 |= XPHASE; + /* Set 1 word per (McBSP) frame for phase1 and phase2 */ + wpf--; + regs->rcr2 |= RFRLEN2(wpf - 1); + regs->xcr2 |= XFRLEN2(wpf - 1); } + regs->rcr1 |= RFRLEN1(wpf - 1); + regs->xcr1 |= XFRLEN1(wpf - 1); + switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: /* Set word lengths */ @@ -330,15 +323,30 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } + /* In McBSP master modes, FRAME (i.e. sample rate) is generated + * by _counting_ BCLKs. Calculate frame size in BCLKs */ + master = mcbsp_data->fmt & SND_SOC_DAIFMT_MASTER_MASK; + if (master == SND_SOC_DAIFMT_CBS_CFS) { + div = mcbsp_data->clk_div ? mcbsp_data->clk_div : 1; + framesize = (mcbsp_data->in_freq / div) / params_rate(params); + + if (framesize < wlen * channels) { + printk(KERN_ERR "%s: not enough bandwidth for desired rate and " + "channels\n", __func__); + return -EINVAL; + } + } else + framesize = wlen * channels; + /* Set FS period and length in terms of bit clock periods */ switch (format) { case SND_SOC_DAIFMT_I2S: - regs->srgr2 |= FPER(wlen * channels - 1); - regs->srgr1 |= FWID(wlen - 1); + regs->srgr2 |= FPER(framesize - 1); + regs->srgr1 |= FWID((framesize >> 1) - 1); break; case SND_SOC_DAIFMT_DSP_A: case SND_SOC_DAIFMT_DSP_B: - regs->srgr2 |= FPER(wlen * channels - 1); + regs->srgr2 |= FPER(framesize - 1); regs->srgr1 |= FWID(0); break; } @@ -454,6 +462,7 @@ static int omap_mcbsp_dai_set_clkdiv(struct snd_soc_dai *cpu_dai, if (div_id != OMAP_MCBSP_CLKGDV) return -ENODEV; + mcbsp_data->clk_div = div; regs->srgr1 |= CLKGDV(div - 1); return 0; @@ -554,6 +563,8 @@ static int omap_mcbsp_dai_set_dai_sysclk(struct snd_soc_dai *cpu_dai, struct omap_mcbsp_reg_cfg *regs = &mcbsp_data->regs; int err = 0; + mcbsp_data->in_freq = freq; + switch (clk_id) { case OMAP_MCBSP_SYSCLK_CLK: regs->srgr2 |= CLKSM; @@ -598,13 +609,13 @@ static struct snd_soc_dai_ops omap_mcbsp_dai_ops = { .id = (link_id), \ .playback = { \ .channels_min = 1, \ - .channels_max = 4, \ + .channels_max = 16, \ .rates = OMAP_MCBSP_RATES, \ .formats = SNDRV_PCM_FMTBIT_S16_LE, \ }, \ .capture = { \ .channels_min = 1, \ - .channels_max = 4, \ + .channels_max = 16, \ .rates = OMAP_MCBSP_RATES, \ .formats = SNDRV_PCM_FMTBIT_S16_LE, \ }, \ diff --git a/sound/soc/omap/omap3evm.c b/sound/soc/omap/omap3evm.c index 9114c263077..f484dcd6340 100644 --- a/sound/soc/omap/omap3evm.c +++ b/sound/soc/omap/omap3evm.c @@ -93,10 +93,17 @@ static struct snd_soc_card snd_soc_omap3evm = { .num_links = 1, }; +/* twl4030 setup */ +static struct twl4030_setup_data twl4030_setup = { + .ramp_delay_value = 4, + .sysclk = 26000, +}; + /* Audio subsystem */ static struct snd_soc_device omap3evm_snd_devdata = { .card = &snd_soc_omap3evm, .codec_dev = &soc_codec_dev_twl4030, + .codec_data = &twl4030_setup, }; static struct platform_device *omap3evm_snd_device; @@ -144,4 +151,4 @@ module_exit(omap3evm_soc_exit); MODULE_AUTHOR("Anuj Aggarwal <anuj.aggarwal@ti.com>"); MODULE_DESCRIPTION("ALSA SoC OMAP3 EVM"); -MODULE_LICENSE("GPLv2"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/omap/omap3pandora.c b/sound/soc/omap/omap3pandora.c index ad219aaf7cb..71b2c161158 100644 --- a/sound/soc/omap/omap3pandora.c +++ b/sound/soc/omap/omap3pandora.c @@ -40,9 +40,12 @@ #define PREFIX "ASoC omap3pandora: " -static int omap3pandora_cmn_hw_params(struct snd_soc_dai *codec_dai, - struct snd_soc_dai *cpu_dai, unsigned int fmt) +static int omap3pandora_cmn_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, unsigned int fmt) { + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; int ret; /* Set codec DAI configuration */ @@ -68,8 +71,9 @@ static int omap3pandora_cmn_hw_params(struct snd_soc_dai *codec_dai, } /* Set McBSP clock to external */ - ret = snd_soc_dai_set_sysclk(cpu_dai, OMAP_MCBSP_SYSCLK_CLKS_EXT, 0, - SND_SOC_CLOCK_IN); + ret = snd_soc_dai_set_sysclk(cpu_dai, OMAP_MCBSP_SYSCLK_CLKS_EXT, + 256 * params_rate(params), + SND_SOC_CLOCK_IN); if (ret < 0) { pr_err(PREFIX "can't set cpu system clock\n"); return ret; @@ -87,11 +91,7 @@ static int omap3pandora_cmn_hw_params(struct snd_soc_dai *codec_dai, static int omap3pandora_out_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 *codec_dai = rtd->dai->codec_dai; - struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; - - return omap3pandora_cmn_hw_params(codec_dai, cpu_dai, + return omap3pandora_cmn_hw_params(substream, params, SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_IB_NF | SND_SOC_DAIFMT_CBS_CFS); @@ -100,11 +100,7 @@ static int omap3pandora_out_hw_params(struct snd_pcm_substream *substream, static int omap3pandora_in_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 *codec_dai = rtd->dai->codec_dai; - struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; - - return omap3pandora_cmn_hw_params(codec_dai, cpu_dai, + return omap3pandora_cmn_hw_params(substream, params, SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); @@ -134,7 +130,7 @@ static int omap3pandora_hp_event(struct snd_soc_dapm_widget *w, * |P| <--- TWL4030 <--------- Line In and MICs */ static const struct snd_soc_dapm_widget omap3pandora_out_dapm_widgets[] = { - SND_SOC_DAPM_DAC("PCM DAC", "Playback", SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("PCM DAC", "HiFi Playback", SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_PGA_E("Headphone Amplifier", SND_SOC_NOPM, 0, 0, NULL, 0, omap3pandora_hp_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), @@ -181,6 +177,7 @@ static int omap3pandora_out_init(struct snd_soc_codec *codec) snd_soc_dapm_nc_pin(codec, "CARKITR"); snd_soc_dapm_nc_pin(codec, "HFL"); snd_soc_dapm_nc_pin(codec, "HFR"); + snd_soc_dapm_nc_pin(codec, "VIBRA"); ret = snd_soc_dapm_new_controls(codec, omap3pandora_out_dapm_widgets, ARRAY_SIZE(omap3pandora_out_dapm_widgets)); diff --git a/sound/soc/omap/overo.c b/sound/soc/omap/overo.c index ec4f8fd8b3a..97a4d6308bd 100644 --- a/sound/soc/omap/overo.c +++ b/sound/soc/omap/overo.c @@ -107,8 +107,8 @@ static int __init overo_soc_init(void) { int ret; - if (!machine_is_overo()) { - pr_debug("Not Overo!\n"); + if (!(machine_is_overo() || machine_is_cm_t35())) { + pr_debug("Incomatible machine!\n"); return -ENODEV; } printk(KERN_INFO "overo SoC init\n"); diff --git a/sound/soc/s3c24xx/Kconfig b/sound/soc/s3c24xx/Kconfig index d7912f1e462..b489f1ae103 100644 --- a/sound/soc/s3c24xx/Kconfig +++ b/sound/soc/s3c24xx/Kconfig @@ -24,6 +24,9 @@ config SND_S3C64XX_SOC_I2S select SND_S3C_I2SV2_SOC select S3C64XX_DMA +config SND_S3C_SOC_PCM + tristate + config SND_S3C2443_SOC_AC97 tristate select S3C2410_DMA diff --git a/sound/soc/s3c24xx/Makefile b/sound/soc/s3c24xx/Makefile index 7790406f90b..b744657733d 100644 --- a/sound/soc/s3c24xx/Makefile +++ b/sound/soc/s3c24xx/Makefile @@ -1,10 +1,11 @@ # S3c24XX Platform Support -snd-soc-s3c24xx-objs := s3c24xx-pcm.o +snd-soc-s3c24xx-objs := s3c-dma.o snd-soc-s3c24xx-i2s-objs := s3c24xx-i2s.o snd-soc-s3c2412-i2s-objs := s3c2412-i2s.o snd-soc-s3c64xx-i2s-objs := s3c64xx-i2s.o snd-soc-s3c2443-ac97-objs := s3c2443-ac97.o snd-soc-s3c-i2s-v2-objs := s3c-i2s-v2.o +snd-soc-s3c-pcm-objs := s3c-pcm.o obj-$(CONFIG_SND_S3C24XX_SOC) += snd-soc-s3c24xx.o obj-$(CONFIG_SND_S3C24XX_SOC_I2S) += snd-soc-s3c24xx-i2s.o @@ -12,6 +13,7 @@ obj-$(CONFIG_SND_S3C2443_SOC_AC97) += snd-soc-s3c2443-ac97.o obj-$(CONFIG_SND_S3C2412_SOC_I2S) += snd-soc-s3c2412-i2s.o obj-$(CONFIG_SND_S3C64XX_SOC_I2S) += snd-soc-s3c64xx-i2s.o obj-$(CONFIG_SND_S3C_I2SV2_SOC) += snd-soc-s3c-i2s-v2.o +obj-$(CONFIG_SND_S3C_SOC_PCM) += snd-soc-s3c-pcm.o # S3C24XX Machine Support snd-soc-jive-wm8750-objs := jive_wm8750.o diff --git a/sound/soc/s3c24xx/jive_wm8750.c b/sound/soc/s3c24xx/jive_wm8750.c index 93e6c87b739..59dc2c6b56d 100644 --- a/sound/soc/s3c24xx/jive_wm8750.c +++ b/sound/soc/s3c24xx/jive_wm8750.c @@ -25,7 +25,7 @@ #include <asm/mach-types.h> -#include "s3c24xx-pcm.h" +#include "s3c-dma.h" #include "s3c2412-i2s.h" #include "../codecs/wm8750.h" diff --git a/sound/soc/s3c24xx/ln2440sbc_alc650.c b/sound/soc/s3c24xx/ln2440sbc_alc650.c index 12c71482d25..d00d359a03e 100644 --- a/sound/soc/s3c24xx/ln2440sbc_alc650.c +++ b/sound/soc/s3c24xx/ln2440sbc_alc650.c @@ -24,7 +24,7 @@ #include <sound/soc-dapm.h> #include "../codecs/ac97.h" -#include "s3c24xx-pcm.h" +#include "s3c-dma.h" #include "s3c24xx-ac97.h" static struct snd_soc_card ln2440sbc; diff --git a/sound/soc/s3c24xx/neo1973_gta02_wm8753.c b/sound/soc/s3c24xx/neo1973_gta02_wm8753.c index 26409a9cef9..dea83d30a5c 100644 --- a/sound/soc/s3c24xx/neo1973_gta02_wm8753.c +++ b/sound/soc/s3c24xx/neo1973_gta02_wm8753.c @@ -32,7 +32,7 @@ #include <asm/io.h> #include <mach/gta02.h> #include "../codecs/wm8753.h" -#include "s3c24xx-pcm.h" +#include "s3c-dma.h" #include "s3c24xx-i2s.h" static struct snd_soc_card neo1973_gta02; diff --git a/sound/soc/s3c24xx/neo1973_wm8753.c b/sound/soc/s3c24xx/neo1973_wm8753.c index 77de6c5127d..0cb4f86f6d1 100644 --- a/sound/soc/s3c24xx/neo1973_wm8753.c +++ b/sound/soc/s3c24xx/neo1973_wm8753.c @@ -36,7 +36,7 @@ #include "../codecs/wm8753.h" #include "lm4857.h" -#include "s3c24xx-pcm.h" +#include "s3c-dma.h" #include "s3c24xx-i2s.h" /* define the scenarios */ diff --git a/sound/soc/s3c24xx/s3c24xx-pcm.c b/sound/soc/s3c24xx/s3c-dma.c index 27cf097c2b1..7725e26d6c9 100644 --- a/sound/soc/s3c24xx/s3c24xx-pcm.c +++ b/sound/soc/s3c24xx/s3c-dma.c @@ -1,5 +1,5 @@ /* - * s3c24xx-pcm.c -- ALSA Soc Audio Layer + * s3c-dma.c -- ALSA Soc Audio Layer * * (c) 2006 Wolfson Microelectronics PLC. * Graeme Gregory graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com @@ -30,9 +30,9 @@ #include <mach/hardware.h> #include <mach/dma.h> -#include "s3c24xx-pcm.h" +#include "s3c-dma.h" -static const struct snd_pcm_hardware s3c24xx_pcm_hardware = { +static const struct snd_pcm_hardware s3c_dma_hardware = { .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP | @@ -62,23 +62,32 @@ struct s3c24xx_runtime_data { dma_addr_t dma_start; dma_addr_t dma_pos; dma_addr_t dma_end; - struct s3c24xx_pcm_dma_params *params; + struct s3c_dma_params *params; }; -/* s3c24xx_pcm_enqueue +/* s3c_dma_enqueue * * place a dma buffer onto the queue for the dma system * to handle. */ -static void s3c24xx_pcm_enqueue(struct snd_pcm_substream *substream) +static void s3c_dma_enqueue(struct snd_pcm_substream *substream) { struct s3c24xx_runtime_data *prtd = substream->runtime->private_data; dma_addr_t pos = prtd->dma_pos; + unsigned int limit; int ret; pr_debug("Entered %s\n", __func__); - while (prtd->dma_loaded < prtd->dma_limit) { + if (s3c_dma_has_circular()) + limit = (prtd->dma_end - prtd->dma_start) / prtd->dma_period; + else + limit = prtd->dma_limit; + + pr_debug("%s: loaded %d, limit %d\n", + __func__, prtd->dma_loaded, limit); + + while (prtd->dma_loaded < limit) { unsigned long len = prtd->dma_period; pr_debug("dma_loaded: %d\n", prtd->dma_loaded); @@ -122,21 +131,21 @@ static void s3c24xx_audio_buffdone(struct s3c2410_dma_chan *channel, snd_pcm_period_elapsed(substream); spin_lock(&prtd->lock); - if (prtd->state & ST_RUNNING) { + if (prtd->state & ST_RUNNING && !s3c_dma_has_circular()) { prtd->dma_loaded--; - s3c24xx_pcm_enqueue(substream); + s3c_dma_enqueue(substream); } spin_unlock(&prtd->lock); } -static int s3c24xx_pcm_hw_params(struct snd_pcm_substream *substream, +static int s3c_dma_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_pcm_runtime *runtime = substream->runtime; struct s3c24xx_runtime_data *prtd = runtime->private_data; struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct s3c24xx_pcm_dma_params *dma = rtd->dai->cpu_dai->dma_data; + struct s3c_dma_params *dma = rtd->dai->cpu_dai->dma_data; unsigned long totbytes = params_buffer_bytes(params); int ret = 0; @@ -163,6 +172,11 @@ static int s3c24xx_pcm_hw_params(struct snd_pcm_substream *substream, printk(KERN_ERR "failed to get dma channel\n"); return ret; } + + /* use the circular buffering if we have it available. */ + if (s3c_dma_has_circular()) + s3c2410_dma_setflags(prtd->params->channel, + S3C2410_DMAF_CIRCULAR); } s3c2410_dma_set_buffdone_fn(prtd->params->channel, @@ -184,7 +198,7 @@ static int s3c24xx_pcm_hw_params(struct snd_pcm_substream *substream, return 0; } -static int s3c24xx_pcm_hw_free(struct snd_pcm_substream *substream) +static int s3c_dma_hw_free(struct snd_pcm_substream *substream) { struct s3c24xx_runtime_data *prtd = substream->runtime->private_data; @@ -201,7 +215,7 @@ static int s3c24xx_pcm_hw_free(struct snd_pcm_substream *substream) return 0; } -static int s3c24xx_pcm_prepare(struct snd_pcm_substream *substream) +static int s3c_dma_prepare(struct snd_pcm_substream *substream) { struct s3c24xx_runtime_data *prtd = substream->runtime->private_data; int ret = 0; @@ -234,12 +248,12 @@ static int s3c24xx_pcm_prepare(struct snd_pcm_substream *substream) prtd->dma_pos = prtd->dma_start; /* enqueue dma buffers */ - s3c24xx_pcm_enqueue(substream); + s3c_dma_enqueue(substream); return ret; } -static int s3c24xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +static int s3c_dma_trigger(struct snd_pcm_substream *substream, int cmd) { struct s3c24xx_runtime_data *prtd = substream->runtime->private_data; int ret = 0; @@ -274,7 +288,7 @@ static int s3c24xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) } static snd_pcm_uframes_t -s3c24xx_pcm_pointer(struct snd_pcm_substream *substream) +s3c_dma_pointer(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct s3c24xx_runtime_data *prtd = runtime->private_data; @@ -309,7 +323,7 @@ s3c24xx_pcm_pointer(struct snd_pcm_substream *substream) return bytes_to_frames(substream->runtime, res); } -static int s3c24xx_pcm_open(struct snd_pcm_substream *substream) +static int s3c_dma_open(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct s3c24xx_runtime_data *prtd; @@ -317,7 +331,7 @@ static int s3c24xx_pcm_open(struct snd_pcm_substream *substream) pr_debug("Entered %s\n", __func__); snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); - snd_soc_set_runtime_hwparams(substream, &s3c24xx_pcm_hardware); + snd_soc_set_runtime_hwparams(substream, &s3c_dma_hardware); prtd = kzalloc(sizeof(struct s3c24xx_runtime_data), GFP_KERNEL); if (prtd == NULL) @@ -329,7 +343,7 @@ static int s3c24xx_pcm_open(struct snd_pcm_substream *substream) return 0; } -static int s3c24xx_pcm_close(struct snd_pcm_substream *substream) +static int s3c_dma_close(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct s3c24xx_runtime_data *prtd = runtime->private_data; @@ -337,14 +351,14 @@ static int s3c24xx_pcm_close(struct snd_pcm_substream *substream) pr_debug("Entered %s\n", __func__); if (!prtd) - pr_debug("s3c24xx_pcm_close called with prtd == NULL\n"); + pr_debug("s3c_dma_close called with prtd == NULL\n"); kfree(prtd); return 0; } -static int s3c24xx_pcm_mmap(struct snd_pcm_substream *substream, +static int s3c_dma_mmap(struct snd_pcm_substream *substream, struct vm_area_struct *vma) { struct snd_pcm_runtime *runtime = substream->runtime; @@ -357,23 +371,23 @@ static int s3c24xx_pcm_mmap(struct snd_pcm_substream *substream, runtime->dma_bytes); } -static struct snd_pcm_ops s3c24xx_pcm_ops = { - .open = s3c24xx_pcm_open, - .close = s3c24xx_pcm_close, +static struct snd_pcm_ops s3c_dma_ops = { + .open = s3c_dma_open, + .close = s3c_dma_close, .ioctl = snd_pcm_lib_ioctl, - .hw_params = s3c24xx_pcm_hw_params, - .hw_free = s3c24xx_pcm_hw_free, - .prepare = s3c24xx_pcm_prepare, - .trigger = s3c24xx_pcm_trigger, - .pointer = s3c24xx_pcm_pointer, - .mmap = s3c24xx_pcm_mmap, + .hw_params = s3c_dma_hw_params, + .hw_free = s3c_dma_hw_free, + .prepare = s3c_dma_prepare, + .trigger = s3c_dma_trigger, + .pointer = s3c_dma_pointer, + .mmap = s3c_dma_mmap, }; -static int s3c24xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) +static int s3c_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) { struct snd_pcm_substream *substream = pcm->streams[stream].substream; struct snd_dma_buffer *buf = &substream->dma_buffer; - size_t size = s3c24xx_pcm_hardware.buffer_bytes_max; + size_t size = s3c_dma_hardware.buffer_bytes_max; pr_debug("Entered %s\n", __func__); @@ -388,7 +402,7 @@ static int s3c24xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) return 0; } -static void s3c24xx_pcm_free_dma_buffers(struct snd_pcm *pcm) +static void s3c_dma_free_dma_buffers(struct snd_pcm *pcm) { struct snd_pcm_substream *substream; struct snd_dma_buffer *buf; @@ -411,9 +425,9 @@ static void s3c24xx_pcm_free_dma_buffers(struct snd_pcm *pcm) } } -static u64 s3c24xx_pcm_dmamask = DMA_BIT_MASK(32); +static u64 s3c_dma_mask = DMA_BIT_MASK(32); -static int s3c24xx_pcm_new(struct snd_card *card, +static int s3c_dma_new(struct snd_card *card, struct snd_soc_dai *dai, struct snd_pcm *pcm) { int ret = 0; @@ -421,19 +435,19 @@ static int s3c24xx_pcm_new(struct snd_card *card, pr_debug("Entered %s\n", __func__); if (!card->dev->dma_mask) - card->dev->dma_mask = &s3c24xx_pcm_dmamask; + card->dev->dma_mask = &s3c_dma_mask; if (!card->dev->coherent_dma_mask) card->dev->coherent_dma_mask = 0xffffffff; if (dai->playback.channels_min) { - ret = s3c24xx_pcm_preallocate_dma_buffer(pcm, + ret = s3c_preallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_PLAYBACK); if (ret) goto out; } if (dai->capture.channels_min) { - ret = s3c24xx_pcm_preallocate_dma_buffer(pcm, + ret = s3c_preallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_CAPTURE); if (ret) goto out; @@ -444,9 +458,9 @@ static int s3c24xx_pcm_new(struct snd_card *card, struct snd_soc_platform s3c24xx_soc_platform = { .name = "s3c24xx-audio", - .pcm_ops = &s3c24xx_pcm_ops, - .pcm_new = s3c24xx_pcm_new, - .pcm_free = s3c24xx_pcm_free_dma_buffers, + .pcm_ops = &s3c_dma_ops, + .pcm_new = s3c_dma_new, + .pcm_free = s3c_dma_free_dma_buffers, }; EXPORT_SYMBOL_GPL(s3c24xx_soc_platform); @@ -463,5 +477,5 @@ static void __exit s3c24xx_soc_platform_exit(void) module_exit(s3c24xx_soc_platform_exit); MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>"); -MODULE_DESCRIPTION("Samsung S3C24XX PCM DMA module"); +MODULE_DESCRIPTION("Samsung S3C Audio DMA module"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/s3c24xx/s3c24xx-pcm.h b/sound/soc/s3c24xx/s3c-dma.h index 0088c79822e..69bb6bf6fc1 100644 --- a/sound/soc/s3c24xx/s3c24xx-pcm.h +++ b/sound/soc/s3c24xx/s3c-dma.h @@ -1,5 +1,5 @@ /* - * s3c24xx-pcm.h -- + * s3c-dma.h -- * * 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 @@ -9,13 +9,13 @@ * ALSA PCM interface for the Samsung S3C24xx CPU */ -#ifndef _S3C24XX_PCM_H -#define _S3C24XX_PCM_H +#ifndef _S3C_AUDIO_H +#define _S3C_AUDIO_H #define ST_RUNNING (1<<0) #define ST_OPENED (1<<1) -struct s3c24xx_pcm_dma_params { +struct s3c_dma_params { struct s3c2410_dma_client *client; /* stream identifier */ int channel; /* Channel ID */ dma_addr_t dma_addr; diff --git a/sound/soc/s3c24xx/s3c-i2s-v2.c b/sound/soc/s3c24xx/s3c-i2s-v2.c index 28b0ab25509..e994d8374fe 100644 --- a/sound/soc/s3c24xx/s3c-i2s-v2.c +++ b/sound/soc/s3c24xx/s3c-i2s-v2.c @@ -35,7 +35,7 @@ #include <mach/dma.h> #include "s3c-i2s-v2.h" -#include "s3c24xx-pcm.h" +#include "s3c-dma.h" #undef S3C_IIS_V2_SUPPORTED @@ -394,7 +394,7 @@ static int s3c2412_i2s_trigger(struct snd_pcm_substream *substream, int cmd, int capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE); unsigned long irqs; int ret = 0; - int channel = ((struct s3c24xx_pcm_dma_params *) + int channel = ((struct s3c_dma_params *) rtd->dai->cpu_dai->dma_data)->channel; pr_debug("Entered %s\n", __func__); diff --git a/sound/soc/s3c24xx/s3c-i2s-v2.h b/sound/soc/s3c24xx/s3c-i2s-v2.h index f66854a77fb..ecf8eaaed1d 100644 --- a/sound/soc/s3c24xx/s3c-i2s-v2.h +++ b/sound/soc/s3c24xx/s3c-i2s-v2.h @@ -49,8 +49,8 @@ struct s3c_i2sv2_info { unsigned char master; - struct s3c24xx_pcm_dma_params *dma_playback; - struct s3c24xx_pcm_dma_params *dma_capture; + struct s3c_dma_params *dma_playback; + struct s3c_dma_params *dma_capture; u32 suspend_iismod; u32 suspend_iiscon; diff --git a/sound/soc/s3c24xx/s3c-pcm.c b/sound/soc/s3c24xx/s3c-pcm.c new file mode 100644 index 00000000000..9e61a7c2d9a --- /dev/null +++ b/sound/soc/s3c24xx/s3c-pcm.c @@ -0,0 +1,552 @@ +/* sound/soc/s3c24xx/s3c-pcm.c + * + * ALSA SoC Audio Layer - S3C PCM-Controller driver + * + * Copyright (c) 2009 Samsung Electronics Co. Ltd + * Author: Jaswinder Singh <jassi.brar@samsung.com> + * based upon I2S drivers by Ben Dooks. + * + * 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/init.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/delay.h> +#include <linux/clk.h> +#include <linux/kernel.h> +#include <linux/gpio.h> +#include <linux/io.h> + +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/initval.h> +#include <sound/soc.h> + +#include <plat/audio.h> +#include <plat/dma.h> + +#include "s3c-dma.h" +#include "s3c-pcm.h" + +static struct s3c2410_dma_client s3c_pcm_dma_client_out = { + .name = "PCM Stereo out" +}; + +static struct s3c2410_dma_client s3c_pcm_dma_client_in = { + .name = "PCM Stereo in" +}; + +static struct s3c_dma_params s3c_pcm_stereo_out[] = { + [0] = { + .client = &s3c_pcm_dma_client_out, + .dma_size = 4, + }, + [1] = { + .client = &s3c_pcm_dma_client_out, + .dma_size = 4, + }, +}; + +static struct s3c_dma_params s3c_pcm_stereo_in[] = { + [0] = { + .client = &s3c_pcm_dma_client_in, + .dma_size = 4, + }, + [1] = { + .client = &s3c_pcm_dma_client_in, + .dma_size = 4, + }, +}; + +static struct s3c_pcm_info s3c_pcm[2]; + +static inline struct s3c_pcm_info *to_info(struct snd_soc_dai *cpu_dai) +{ + return cpu_dai->private_data; +} + +static void s3c_pcm_snd_txctrl(struct s3c_pcm_info *pcm, int on) +{ + void __iomem *regs = pcm->regs; + u32 ctl, clkctl; + + clkctl = readl(regs + S3C_PCM_CLKCTL); + ctl = readl(regs + S3C_PCM_CTL); + ctl &= ~(S3C_PCM_CTL_TXDIPSTICK_MASK + << S3C_PCM_CTL_TXDIPSTICK_SHIFT); + + if (on) { + ctl |= S3C_PCM_CTL_TXDMA_EN; + ctl |= S3C_PCM_CTL_TXFIFO_EN; + ctl |= S3C_PCM_CTL_ENABLE; + ctl |= (0x20<<S3C_PCM_CTL_TXDIPSTICK_SHIFT); + clkctl |= S3C_PCM_CLKCTL_SERCLK_EN; + } else { + ctl &= ~S3C_PCM_CTL_TXDMA_EN; + ctl &= ~S3C_PCM_CTL_TXFIFO_EN; + + if (!(ctl & S3C_PCM_CTL_RXFIFO_EN)) { + ctl &= ~S3C_PCM_CTL_ENABLE; + if (!pcm->idleclk) + clkctl |= S3C_PCM_CLKCTL_SERCLK_EN; + } + } + + writel(clkctl, regs + S3C_PCM_CLKCTL); + writel(ctl, regs + S3C_PCM_CTL); +} + +static void s3c_pcm_snd_rxctrl(struct s3c_pcm_info *pcm, int on) +{ + void __iomem *regs = pcm->regs; + u32 ctl, clkctl; + + ctl = readl(regs + S3C_PCM_CTL); + clkctl = readl(regs + S3C_PCM_CLKCTL); + + if (on) { + ctl |= S3C_PCM_CTL_RXDMA_EN; + ctl |= S3C_PCM_CTL_RXFIFO_EN; + ctl |= S3C_PCM_CTL_ENABLE; + clkctl |= S3C_PCM_CLKCTL_SERCLK_EN; + } else { + ctl &= ~S3C_PCM_CTL_RXDMA_EN; + ctl &= ~S3C_PCM_CTL_RXFIFO_EN; + + if (!(ctl & S3C_PCM_CTL_TXFIFO_EN)) { + ctl &= ~S3C_PCM_CTL_ENABLE; + if (!pcm->idleclk) + clkctl |= S3C_PCM_CLKCTL_SERCLK_EN; + } + } + + writel(clkctl, regs + S3C_PCM_CLKCTL); + writel(ctl, regs + S3C_PCM_CTL); +} + +static int s3c_pcm_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct s3c_pcm_info *pcm = to_info(rtd->dai->cpu_dai); + unsigned long flags; + + dev_dbg(pcm->dev, "Entered %s\n", __func__); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + spin_lock_irqsave(&pcm->lock, flags); + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + s3c_pcm_snd_rxctrl(pcm, 1); + else + s3c_pcm_snd_txctrl(pcm, 1); + + spin_unlock_irqrestore(&pcm->lock, flags); + break; + + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + spin_lock_irqsave(&pcm->lock, flags); + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + s3c_pcm_snd_rxctrl(pcm, 0); + else + s3c_pcm_snd_txctrl(pcm, 0); + + spin_unlock_irqrestore(&pcm->lock, flags); + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int s3c_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *socdai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai_link *dai = rtd->dai; + struct s3c_pcm_info *pcm = to_info(dai->cpu_dai); + void __iomem *regs = pcm->regs; + struct clk *clk; + int sclk_div, sync_div; + unsigned long flags; + u32 clkctl; + + dev_dbg(pcm->dev, "Entered %s\n", __func__); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + dai->cpu_dai->dma_data = pcm->dma_playback; + else + dai->cpu_dai->dma_data = pcm->dma_capture; + + /* Strictly check for sample size */ + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + break; + default: + return -EINVAL; + } + + spin_lock_irqsave(&pcm->lock, flags); + + /* Get hold of the PCMSOURCE_CLK */ + clkctl = readl(regs + S3C_PCM_CLKCTL); + if (clkctl & S3C_PCM_CLKCTL_SERCLKSEL_PCLK) + clk = pcm->pclk; + else + clk = pcm->cclk; + + /* Set the SCLK divider */ + sclk_div = clk_get_rate(clk) / pcm->sclk_per_fs / + params_rate(params) / 2 - 1; + + clkctl &= ~(S3C_PCM_CLKCTL_SCLKDIV_MASK + << S3C_PCM_CLKCTL_SCLKDIV_SHIFT); + clkctl |= ((sclk_div & S3C_PCM_CLKCTL_SCLKDIV_MASK) + << S3C_PCM_CLKCTL_SCLKDIV_SHIFT); + + /* Set the SYNC divider */ + sync_div = pcm->sclk_per_fs - 1; + + clkctl &= ~(S3C_PCM_CLKCTL_SYNCDIV_MASK + << S3C_PCM_CLKCTL_SYNCDIV_SHIFT); + clkctl |= ((sync_div & S3C_PCM_CLKCTL_SYNCDIV_MASK) + << S3C_PCM_CLKCTL_SYNCDIV_SHIFT); + + writel(clkctl, regs + S3C_PCM_CLKCTL); + + spin_unlock_irqrestore(&pcm->lock, flags); + + dev_dbg(pcm->dev, "PCMSOURCE_CLK-%lu SCLK=%ufs \ + SCLK_DIV=%d SYNC_DIV=%d\n", + clk_get_rate(clk), pcm->sclk_per_fs, + sclk_div, sync_div); + + return 0; +} + +static int s3c_pcm_set_fmt(struct snd_soc_dai *cpu_dai, + unsigned int fmt) +{ + struct s3c_pcm_info *pcm = to_info(cpu_dai); + void __iomem *regs = pcm->regs; + unsigned long flags; + int ret = 0; + u32 ctl; + + dev_dbg(pcm->dev, "Entered %s\n", __func__); + + spin_lock_irqsave(&pcm->lock, flags); + + ctl = readl(regs + S3C_PCM_CTL); + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + /* Nothing to do, NB_NF by default */ + break; + default: + dev_err(pcm->dev, "Unsupported clock inversion!\n"); + ret = -EINVAL; + goto exit; + } + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + /* Nothing to do, Master by default */ + break; + default: + dev_err(pcm->dev, "Unsupported master/slave format!\n"); + ret = -EINVAL; + goto exit; + } + + switch (fmt & SND_SOC_DAIFMT_CLOCK_MASK) { + case SND_SOC_DAIFMT_CONT: + pcm->idleclk = 1; + break; + case SND_SOC_DAIFMT_GATED: + pcm->idleclk = 0; + break; + default: + dev_err(pcm->dev, "Invalid Clock gating request!\n"); + ret = -EINVAL; + goto exit; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + ctl |= S3C_PCM_CTL_TXMSB_AFTER_FSYNC; + ctl |= S3C_PCM_CTL_RXMSB_AFTER_FSYNC; + break; + case SND_SOC_DAIFMT_DSP_B: + ctl &= ~S3C_PCM_CTL_TXMSB_AFTER_FSYNC; + ctl &= ~S3C_PCM_CTL_RXMSB_AFTER_FSYNC; + break; + default: + dev_err(pcm->dev, "Unsupported data format!\n"); + ret = -EINVAL; + goto exit; + } + + writel(ctl, regs + S3C_PCM_CTL); + +exit: + spin_unlock_irqrestore(&pcm->lock, flags); + + return ret; +} + +static int s3c_pcm_set_clkdiv(struct snd_soc_dai *cpu_dai, + int div_id, int div) +{ + struct s3c_pcm_info *pcm = to_info(cpu_dai); + + switch (div_id) { + case S3C_PCM_SCLK_PER_FS: + pcm->sclk_per_fs = div; + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int s3c_pcm_set_sysclk(struct snd_soc_dai *cpu_dai, + int clk_id, unsigned int freq, int dir) +{ + struct s3c_pcm_info *pcm = to_info(cpu_dai); + void __iomem *regs = pcm->regs; + u32 clkctl = readl(regs + S3C_PCM_CLKCTL); + + switch (clk_id) { + case S3C_PCM_CLKSRC_PCLK: + clkctl |= S3C_PCM_CLKCTL_SERCLKSEL_PCLK; + break; + + case S3C_PCM_CLKSRC_MUX: + clkctl &= ~S3C_PCM_CLKCTL_SERCLKSEL_PCLK; + + if (clk_get_rate(pcm->cclk) != freq) + clk_set_rate(pcm->cclk, freq); + + break; + + default: + return -EINVAL; + } + + writel(clkctl, regs + S3C_PCM_CLKCTL); + + return 0; +} + +static struct snd_soc_dai_ops s3c_pcm_dai_ops = { + .set_sysclk = s3c_pcm_set_sysclk, + .set_clkdiv = s3c_pcm_set_clkdiv, + .trigger = s3c_pcm_trigger, + .hw_params = s3c_pcm_hw_params, + .set_fmt = s3c_pcm_set_fmt, +}; + +#define S3C_PCM_RATES SNDRV_PCM_RATE_8000_96000 + +#define S3C_PCM_DECLARE(n) \ +{ \ + .name = "samsung-pcm", \ + .id = (n), \ + .symmetric_rates = 1, \ + .ops = &s3c_pcm_dai_ops, \ + .playback = { \ + .channels_min = 2, \ + .channels_max = 2, \ + .rates = S3C_PCM_RATES, \ + .formats = SNDRV_PCM_FMTBIT_S16_LE, \ + }, \ + .capture = { \ + .channels_min = 2, \ + .channels_max = 2, \ + .rates = S3C_PCM_RATES, \ + .formats = SNDRV_PCM_FMTBIT_S16_LE, \ + }, \ +} + +struct snd_soc_dai s3c_pcm_dai[] = { + S3C_PCM_DECLARE(0), + S3C_PCM_DECLARE(1), +}; +EXPORT_SYMBOL_GPL(s3c_pcm_dai); + +static __devinit int s3c_pcm_dev_probe(struct platform_device *pdev) +{ + struct s3c_pcm_info *pcm; + struct snd_soc_dai *dai; + struct resource *mem_res, *dmatx_res, *dmarx_res; + struct s3c_audio_pdata *pcm_pdata; + int ret; + + /* Check for valid device index */ + if ((pdev->id < 0) || pdev->id >= ARRAY_SIZE(s3c_pcm)) { + dev_err(&pdev->dev, "id %d out of range\n", pdev->id); + return -EINVAL; + } + + pcm_pdata = pdev->dev.platform_data; + + /* Check for availability of necessary resource */ + dmatx_res = platform_get_resource(pdev, IORESOURCE_DMA, 0); + if (!dmatx_res) { + dev_err(&pdev->dev, "Unable to get PCM-TX dma resource\n"); + return -ENXIO; + } + + dmarx_res = platform_get_resource(pdev, IORESOURCE_DMA, 1); + if (!dmarx_res) { + dev_err(&pdev->dev, "Unable to get PCM-RX dma resource\n"); + return -ENXIO; + } + + mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem_res) { + dev_err(&pdev->dev, "Unable to get register resource\n"); + return -ENXIO; + } + + if (pcm_pdata && pcm_pdata->cfg_gpio && pcm_pdata->cfg_gpio(pdev)) { + dev_err(&pdev->dev, "Unable to configure gpio\n"); + return -EINVAL; + } + + pcm = &s3c_pcm[pdev->id]; + pcm->dev = &pdev->dev; + + spin_lock_init(&pcm->lock); + + dai = &s3c_pcm_dai[pdev->id]; + dai->dev = &pdev->dev; + + /* Default is 128fs */ + pcm->sclk_per_fs = 128; + + pcm->cclk = clk_get(&pdev->dev, "audio-bus"); + if (IS_ERR(pcm->cclk)) { + dev_err(&pdev->dev, "failed to get audio-bus\n"); + ret = PTR_ERR(pcm->cclk); + goto err1; + } + clk_enable(pcm->cclk); + + /* record our pcm structure for later use in the callbacks */ + dai->private_data = pcm; + + if (!request_mem_region(mem_res->start, + resource_size(mem_res), "samsung-pcm")) { + dev_err(&pdev->dev, "Unable to request register region\n"); + ret = -EBUSY; + goto err2; + } + + pcm->regs = ioremap(mem_res->start, 0x100); + if (pcm->regs == NULL) { + dev_err(&pdev->dev, "cannot ioremap registers\n"); + ret = -ENXIO; + goto err3; + } + + pcm->pclk = clk_get(&pdev->dev, "pcm"); + if (IS_ERR(pcm->pclk)) { + dev_err(&pdev->dev, "failed to get pcm_clock\n"); + ret = -ENOENT; + goto err4; + } + clk_enable(pcm->pclk); + + ret = snd_soc_register_dai(dai); + if (ret != 0) { + dev_err(&pdev->dev, "failed to get pcm_clock\n"); + goto err5; + } + + s3c_pcm_stereo_in[pdev->id].dma_addr = mem_res->start + + S3C_PCM_RXFIFO; + s3c_pcm_stereo_out[pdev->id].dma_addr = mem_res->start + + S3C_PCM_TXFIFO; + + s3c_pcm_stereo_in[pdev->id].channel = dmarx_res->start; + s3c_pcm_stereo_out[pdev->id].channel = dmatx_res->start; + + pcm->dma_capture = &s3c_pcm_stereo_in[pdev->id]; + pcm->dma_playback = &s3c_pcm_stereo_out[pdev->id]; + + return 0; + +err5: + clk_disable(pcm->pclk); + clk_put(pcm->pclk); +err4: + iounmap(pcm->regs); +err3: + release_mem_region(mem_res->start, resource_size(mem_res)); +err2: + clk_disable(pcm->cclk); + clk_put(pcm->cclk); +err1: + return ret; +} + +static __devexit int s3c_pcm_dev_remove(struct platform_device *pdev) +{ + struct s3c_pcm_info *pcm = &s3c_pcm[pdev->id]; + struct resource *mem_res; + + iounmap(pcm->regs); + + mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(mem_res->start, resource_size(mem_res)); + + clk_disable(pcm->cclk); + clk_disable(pcm->pclk); + clk_put(pcm->pclk); + clk_put(pcm->cclk); + + return 0; +} + +static struct platform_driver s3c_pcm_driver = { + .probe = s3c_pcm_dev_probe, + .remove = s3c_pcm_dev_remove, + .driver = { + .name = "samsung-pcm", + .owner = THIS_MODULE, + }, +}; + +static int __init s3c_pcm_init(void) +{ + return platform_driver_register(&s3c_pcm_driver); +} +module_init(s3c_pcm_init); + +static void __exit s3c_pcm_exit(void) +{ + platform_driver_unregister(&s3c_pcm_driver); +} +module_exit(s3c_pcm_exit); + +/* Module information */ +MODULE_AUTHOR("Jaswinder Singh, <jassi.brar@samsung.com>"); +MODULE_DESCRIPTION("S3C PCM Controller Driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/s3c24xx/s3c-pcm.h b/sound/soc/s3c24xx/s3c-pcm.h new file mode 100644 index 00000000000..69ff9971692 --- /dev/null +++ b/sound/soc/s3c24xx/s3c-pcm.h @@ -0,0 +1,123 @@ +/* sound/soc/s3c24xx/s3c-pcm.h + * + * 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 __S3C_PCM_H +#define __S3C_PCM_H __FILE__ + +/*Register Offsets */ +#define S3C_PCM_CTL (0x00) +#define S3C_PCM_CLKCTL (0x04) +#define S3C_PCM_TXFIFO (0x08) +#define S3C_PCM_RXFIFO (0x0C) +#define S3C_PCM_IRQCTL (0x10) +#define S3C_PCM_IRQSTAT (0x14) +#define S3C_PCM_FIFOSTAT (0x18) +#define S3C_PCM_CLRINT (0x20) + +/* PCM_CTL Bit-Fields */ +#define S3C_PCM_CTL_TXDIPSTICK_MASK (0x3f) +#define S3C_PCM_CTL_TXDIPSTICK_SHIFT (13) +#define S3C_PCM_CTL_RXDIPSTICK_MSK (0x3f<<7) +#define S3C_PCM_CTL_TXDMA_EN (0x1<<6) +#define S3C_PCM_CTL_RXDMA_EN (0x1<<5) +#define S3C_PCM_CTL_TXMSB_AFTER_FSYNC (0x1<<4) +#define S3C_PCM_CTL_RXMSB_AFTER_FSYNC (0x1<<3) +#define S3C_PCM_CTL_TXFIFO_EN (0x1<<2) +#define S3C_PCM_CTL_RXFIFO_EN (0x1<<1) +#define S3C_PCM_CTL_ENABLE (0x1<<0) + +/* PCM_CLKCTL Bit-Fields */ +#define S3C_PCM_CLKCTL_SERCLK_EN (0x1<<19) +#define S3C_PCM_CLKCTL_SERCLKSEL_PCLK (0x1<<18) +#define S3C_PCM_CLKCTL_SCLKDIV_MASK (0x1ff) +#define S3C_PCM_CLKCTL_SYNCDIV_MASK (0x1ff) +#define S3C_PCM_CLKCTL_SCLKDIV_SHIFT (9) +#define S3C_PCM_CLKCTL_SYNCDIV_SHIFT (0) + +/* PCM_TXFIFO Bit-Fields */ +#define S3C_PCM_TXFIFO_DVALID (0x1<<16) +#define S3C_PCM_TXFIFO_DATA_MSK (0xffff<<0) + +/* PCM_RXFIFO Bit-Fields */ +#define S3C_PCM_RXFIFO_DVALID (0x1<<16) +#define S3C_PCM_RXFIFO_DATA_MSK (0xffff<<0) + +/* PCM_IRQCTL Bit-Fields */ +#define S3C_PCM_IRQCTL_IRQEN (0x1<<14) +#define S3C_PCM_IRQCTL_WRDEN (0x1<<12) +#define S3C_PCM_IRQCTL_TXEMPTYEN (0x1<<11) +#define S3C_PCM_IRQCTL_TXALMSTEMPTYEN (0x1<<10) +#define S3C_PCM_IRQCTL_TXFULLEN (0x1<<9) +#define S3C_PCM_IRQCTL_TXALMSTFULLEN (0x1<<8) +#define S3C_PCM_IRQCTL_TXSTARVEN (0x1<<7) +#define S3C_PCM_IRQCTL_TXERROVRFLEN (0x1<<6) +#define S3C_PCM_IRQCTL_RXEMPTEN (0x1<<5) +#define S3C_PCM_IRQCTL_RXALMSTEMPTEN (0x1<<4) +#define S3C_PCM_IRQCTL_RXFULLEN (0x1<<3) +#define S3C_PCM_IRQCTL_RXALMSTFULLEN (0x1<<2) +#define S3C_PCM_IRQCTL_RXSTARVEN (0x1<<1) +#define S3C_PCM_IRQCTL_RXERROVRFLEN (0x1<<0) + +/* PCM_IRQSTAT Bit-Fields */ +#define S3C_PCM_IRQSTAT_IRQPND (0x1<<13) +#define S3C_PCM_IRQSTAT_WRD_XFER (0x1<<12) +#define S3C_PCM_IRQSTAT_TXEMPTY (0x1<<11) +#define S3C_PCM_IRQSTAT_TXALMSTEMPTY (0x1<<10) +#define S3C_PCM_IRQSTAT_TXFULL (0x1<<9) +#define S3C_PCM_IRQSTAT_TXALMSTFULL (0x1<<8) +#define S3C_PCM_IRQSTAT_TXSTARV (0x1<<7) +#define S3C_PCM_IRQSTAT_TXERROVRFL (0x1<<6) +#define S3C_PCM_IRQSTAT_RXEMPT (0x1<<5) +#define S3C_PCM_IRQSTAT_RXALMSTEMPT (0x1<<4) +#define S3C_PCM_IRQSTAT_RXFULL (0x1<<3) +#define S3C_PCM_IRQSTAT_RXALMSTFULL (0x1<<2) +#define S3C_PCM_IRQSTAT_RXSTARV (0x1<<1) +#define S3C_PCM_IRQSTAT_RXERROVRFL (0x1<<0) + +/* PCM_FIFOSTAT Bit-Fields */ +#define S3C_PCM_FIFOSTAT_TXCNT_MSK (0x3f<<14) +#define S3C_PCM_FIFOSTAT_TXFIFOEMPTY (0x1<<13) +#define S3C_PCM_FIFOSTAT_TXFIFOALMSTEMPTY (0x1<<12) +#define S3C_PCM_FIFOSTAT_TXFIFOFULL (0x1<<11) +#define S3C_PCM_FIFOSTAT_TXFIFOALMSTFULL (0x1<<10) +#define S3C_PCM_FIFOSTAT_RXCNT_MSK (0x3f<<4) +#define S3C_PCM_FIFOSTAT_RXFIFOEMPTY (0x1<<3) +#define S3C_PCM_FIFOSTAT_RXFIFOALMSTEMPTY (0x1<<2) +#define S3C_PCM_FIFOSTAT_RXFIFOFULL (0x1<<1) +#define S3C_PCM_FIFOSTAT_RXFIFOALMSTFULL (0x1<<0) + +#define S3C_PCM_CLKSRC_PCLK 0 +#define S3C_PCM_CLKSRC_MUX 1 + +#define S3C_PCM_SCLK_PER_FS 0 + +/** + * struct s3c_pcm_info - S3C PCM Controller information + * @dev: The parent device passed to use from the probe. + * @regs: The pointer to the device register block. + * @dma_playback: DMA information for playback channel. + * @dma_capture: DMA information for capture channel. + */ +struct s3c_pcm_info { + spinlock_t lock; + struct device *dev; + void __iomem *regs; + + unsigned int sclk_per_fs; + + /* Whether to keep PCMSCLK enabled even when idle(no active xfer) */ + unsigned int idleclk; + + struct clk *pclk; + struct clk *cclk; + + struct s3c_dma_params *dma_playback; + struct s3c_dma_params *dma_capture; +}; + +#endif /* __S3C_PCM_H */ diff --git a/sound/soc/s3c24xx/s3c2412-i2s.c b/sound/soc/s3c24xx/s3c2412-i2s.c index ac5e47b082f..359e59346ba 100644 --- a/sound/soc/s3c24xx/s3c2412-i2s.c +++ b/sound/soc/s3c24xx/s3c2412-i2s.c @@ -37,7 +37,7 @@ #include <mach/regs-gpio.h> #include <mach/dma.h> -#include "s3c24xx-pcm.h" +#include "s3c-dma.h" #include "s3c2412-i2s.h" #define S3C2412_I2S_DEBUG 0 @@ -50,14 +50,14 @@ static struct s3c2410_dma_client s3c2412_dma_client_in = { .name = "I2S PCM Stereo in" }; -static struct s3c24xx_pcm_dma_params s3c2412_i2s_pcm_stereo_out = { +static struct s3c_dma_params s3c2412_i2s_pcm_stereo_out = { .client = &s3c2412_dma_client_out, .channel = DMACH_I2S_OUT, .dma_addr = S3C2410_PA_IIS + S3C2412_IISTXD, .dma_size = 4, }; -static struct s3c24xx_pcm_dma_params s3c2412_i2s_pcm_stereo_in = { +static struct s3c_dma_params s3c2412_i2s_pcm_stereo_in = { .client = &s3c2412_dma_client_in, .channel = DMACH_I2S_IN, .dma_addr = S3C2410_PA_IIS + S3C2412_IISRXD, diff --git a/sound/soc/s3c24xx/s3c2443-ac97.c b/sound/soc/s3c24xx/s3c2443-ac97.c index b25e9f968df..0191e3acb0b 100644 --- a/sound/soc/s3c24xx/s3c2443-ac97.c +++ b/sound/soc/s3c24xx/s3c2443-ac97.c @@ -35,7 +35,7 @@ #include <asm/dma.h> #include <mach/dma.h> -#include "s3c24xx-pcm.h" +#include "s3c-dma.h" #include "s3c24xx-ac97.h" struct s3c24xx_ac97_info { @@ -188,21 +188,21 @@ static struct s3c2410_dma_client s3c2443_dma_client_micin = { .name = "AC97 Mic Mono in" }; -static struct s3c24xx_pcm_dma_params s3c2443_ac97_pcm_stereo_out = { +static struct s3c_dma_params s3c2443_ac97_pcm_stereo_out = { .client = &s3c2443_dma_client_out, .channel = DMACH_PCM_OUT, .dma_addr = S3C2440_PA_AC97 + S3C_AC97_PCM_DATA, .dma_size = 4, }; -static struct s3c24xx_pcm_dma_params s3c2443_ac97_pcm_stereo_in = { +static struct s3c_dma_params s3c2443_ac97_pcm_stereo_in = { .client = &s3c2443_dma_client_in, .channel = DMACH_PCM_IN, .dma_addr = S3C2440_PA_AC97 + S3C_AC97_PCM_DATA, .dma_size = 4, }; -static struct s3c24xx_pcm_dma_params s3c2443_ac97_mic_mono_in = { +static struct s3c_dma_params s3c2443_ac97_mic_mono_in = { .client = &s3c2443_dma_client_micin, .channel = DMACH_MIC_IN, .dma_addr = S3C2440_PA_AC97 + S3C_AC97_MIC_DATA, @@ -290,7 +290,7 @@ static int s3c2443_ac97_trigger(struct snd_pcm_substream *substream, int cmd, { u32 ac_glbctrl; struct snd_soc_pcm_runtime *rtd = substream->private_data; - int channel = ((struct s3c24xx_pcm_dma_params *) + int channel = ((struct s3c_dma_params *) rtd->dai->cpu_dai->dma_data)->channel; ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL); @@ -339,7 +339,7 @@ static int s3c2443_ac97_mic_trigger(struct snd_pcm_substream *substream, { u32 ac_glbctrl; struct snd_soc_pcm_runtime *rtd = substream->private_data; - int channel = ((struct s3c24xx_pcm_dma_params *) + int channel = ((struct s3c_dma_params *) rtd->dai->cpu_dai->dma_data)->channel; ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL); diff --git a/sound/soc/s3c24xx/s3c24xx-i2s.c b/sound/soc/s3c24xx/s3c24xx-i2s.c index c76b8bb214b..0bc5950b9f0 100644 --- a/sound/soc/s3c24xx/s3c24xx-i2s.c +++ b/sound/soc/s3c24xx/s3c24xx-i2s.c @@ -38,7 +38,7 @@ #include <plat/regs-iis.h> -#include "s3c24xx-pcm.h" +#include "s3c-dma.h" #include "s3c24xx-i2s.h" static struct s3c2410_dma_client s3c24xx_dma_client_out = { @@ -49,14 +49,14 @@ static struct s3c2410_dma_client s3c24xx_dma_client_in = { .name = "I2S PCM Stereo in" }; -static struct s3c24xx_pcm_dma_params s3c24xx_i2s_pcm_stereo_out = { +static struct s3c_dma_params s3c24xx_i2s_pcm_stereo_out = { .client = &s3c24xx_dma_client_out, .channel = DMACH_I2S_OUT, .dma_addr = S3C2410_PA_IIS + S3C2410_IISFIFO, .dma_size = 2, }; -static struct s3c24xx_pcm_dma_params s3c24xx_i2s_pcm_stereo_in = { +static struct s3c_dma_params s3c24xx_i2s_pcm_stereo_in = { .client = &s3c24xx_dma_client_in, .channel = DMACH_I2S_IN, .dma_addr = S3C2410_PA_IIS + S3C2410_IISFIFO, @@ -258,12 +258,12 @@ static int s3c24xx_i2s_hw_params(struct snd_pcm_substream *substream, switch (params_format(params)) { case SNDRV_PCM_FORMAT_S8: iismod &= ~S3C2410_IISMOD_16BIT; - ((struct s3c24xx_pcm_dma_params *) + ((struct s3c_dma_params *) rtd->dai->cpu_dai->dma_data)->dma_size = 1; break; case SNDRV_PCM_FORMAT_S16_LE: iismod |= S3C2410_IISMOD_16BIT; - ((struct s3c24xx_pcm_dma_params *) + ((struct s3c_dma_params *) rtd->dai->cpu_dai->dma_data)->dma_size = 2; break; default: @@ -280,7 +280,7 @@ static int s3c24xx_i2s_trigger(struct snd_pcm_substream *substream, int cmd, { int ret = 0; struct snd_soc_pcm_runtime *rtd = substream->private_data; - int channel = ((struct s3c24xx_pcm_dma_params *) + int channel = ((struct s3c_dma_params *) rtd->dai->cpu_dai->dma_data)->channel; pr_debug("Entered %s\n", __func__); diff --git a/sound/soc/s3c24xx/s3c24xx_simtec.c b/sound/soc/s3c24xx/s3c24xx_simtec.c index 1966e0d5652..507b2ed5d58 100644 --- a/sound/soc/s3c24xx/s3c24xx_simtec.c +++ b/sound/soc/s3c24xx/s3c24xx_simtec.c @@ -21,7 +21,7 @@ #include <plat/audio-simtec.h> -#include "s3c24xx-pcm.h" +#include "s3c-dma.h" #include "s3c24xx-i2s.h" #include "s3c24xx_simtec.h" diff --git a/sound/soc/s3c24xx/s3c24xx_simtec_hermes.c b/sound/soc/s3c24xx/s3c24xx_simtec_hermes.c index 8346bd96eaf..bdf8951af8e 100644 --- a/sound/soc/s3c24xx/s3c24xx_simtec_hermes.c +++ b/sound/soc/s3c24xx/s3c24xx_simtec_hermes.c @@ -18,7 +18,7 @@ #include <plat/audio-simtec.h> -#include "s3c24xx-pcm.h" +#include "s3c-dma.h" #include "s3c24xx-i2s.h" #include "s3c24xx_simtec.h" diff --git a/sound/soc/s3c24xx/s3c24xx_simtec_tlv320aic23.c b/sound/soc/s3c24xx/s3c24xx_simtec_tlv320aic23.c index 25797e09617..185c0acb5ce 100644 --- a/sound/soc/s3c24xx/s3c24xx_simtec_tlv320aic23.c +++ b/sound/soc/s3c24xx/s3c24xx_simtec_tlv320aic23.c @@ -18,7 +18,7 @@ #include <plat/audio-simtec.h> -#include "s3c24xx-pcm.h" +#include "s3c-dma.h" #include "s3c24xx-i2s.h" #include "s3c24xx_simtec.h" diff --git a/sound/soc/s3c24xx/s3c24xx_uda134x.c b/sound/soc/s3c24xx/s3c24xx_uda134x.c index c215d32d632..052d59659c2 100644 --- a/sound/soc/s3c24xx/s3c24xx_uda134x.c +++ b/sound/soc/s3c24xx/s3c24xx_uda134x.c @@ -24,7 +24,7 @@ #include <plat/regs-iis.h> -#include "s3c24xx-pcm.h" +#include "s3c-dma.h" #include "s3c24xx-i2s.h" #include "../codecs/uda134x.h" diff --git a/sound/soc/s3c24xx/s3c64xx-i2s.c b/sound/soc/s3c24xx/s3c64xx-i2s.c index b67eed59666..cc7edb5f792 100644 --- a/sound/soc/s3c24xx/s3c64xx-i2s.c +++ b/sound/soc/s3c24xx/s3c64xx-i2s.c @@ -35,7 +35,7 @@ #include <mach/map.h> #include <mach/dma.h> -#include "s3c24xx-pcm.h" +#include "s3c-dma.h" #include "s3c64xx-i2s.h" static struct s3c2410_dma_client s3c64xx_dma_client_out = { @@ -46,7 +46,7 @@ static struct s3c2410_dma_client s3c64xx_dma_client_in = { .name = "I2S PCM Stereo in" }; -static struct s3c24xx_pcm_dma_params s3c64xx_i2s_pcm_stereo_out[2] = { +static struct s3c_dma_params s3c64xx_i2s_pcm_stereo_out[2] = { [0] = { .channel = DMACH_I2S0_OUT, .client = &s3c64xx_dma_client_out, @@ -61,7 +61,7 @@ static struct s3c24xx_pcm_dma_params s3c64xx_i2s_pcm_stereo_out[2] = { }, }; -static struct s3c24xx_pcm_dma_params s3c64xx_i2s_pcm_stereo_in[2] = { +static struct s3c_dma_params s3c64xx_i2s_pcm_stereo_in[2] = { [0] = { .channel = DMACH_I2S0_IN, .client = &s3c64xx_dma_client_in, @@ -236,6 +236,8 @@ static __devinit int s3c64xx_iis_dev_probe(struct platform_device *pdev) goto err; } + clk_enable(i2s->iis_cclk); + ret = s3c_i2sv2_probe(pdev, dai, i2s, 0); if (ret) goto err_clk; diff --git a/sound/soc/s3c24xx/smdk2443_wm9710.c b/sound/soc/s3c24xx/smdk2443_wm9710.c index a2a4f5323c1..12b783b12fc 100644 --- a/sound/soc/s3c24xx/smdk2443_wm9710.c +++ b/sound/soc/s3c24xx/smdk2443_wm9710.c @@ -20,7 +20,7 @@ #include <sound/soc-dapm.h> #include "../codecs/ac97.h" -#include "s3c24xx-pcm.h" +#include "s3c-dma.h" #include "s3c24xx-ac97.h" static struct snd_soc_card smdk2443; diff --git a/sound/soc/s3c24xx/smdk64xx_wm8580.c b/sound/soc/s3c24xx/smdk64xx_wm8580.c index 482aaf10eff..efe4901213a 100644 --- a/sound/soc/s3c24xx/smdk64xx_wm8580.c +++ b/sound/soc/s3c24xx/smdk64xx_wm8580.c @@ -19,7 +19,7 @@ #include <sound/soc-dapm.h> #include "../codecs/wm8580.h" -#include "s3c24xx-pcm.h" +#include "s3c-dma.h" #include "s3c64xx-i2s.h" #define S3C64XX_I2S_V4 2 @@ -103,7 +103,7 @@ static int smdk64xx_hw_params(struct snd_pcm_substream *substream, if (ret < 0) return ret; - /* Set WM8580 to drive MCLK from it's PLLA */ + /* Set WM8580 to drive MCLK from its PLLA */ ret = snd_soc_dai_set_clkdiv(codec_dai, WM8580_MCLK, WM8580_CLKSRC_PLLA); if (ret < 0) @@ -115,8 +115,7 @@ static int smdk64xx_hw_params(struct snd_pcm_substream *substream, if (ret < 0) return ret; - /* Assuming the CODEC driver evaluates it's rfs too from this call */ - ret = snd_soc_dai_set_pll(codec_dai, 0, WM8580_PLLA, + ret = snd_soc_dai_set_pll(codec_dai, WM8580_PLLA, 0, SMDK64XX_WM8580_FREQ, pll_out); if (ret < 0) return ret; @@ -186,9 +185,10 @@ static int smdk64xx_wm8580_init_paiftx(struct snd_soc_codec *codec) /* Set up PAIFTX audio path */ snd_soc_dapm_add_routes(codec, audio_map_tx, ARRAY_SIZE(audio_map_tx)); - /* All enabled by default */ - snd_soc_dapm_enable_pin(codec, "MicIn"); - snd_soc_dapm_enable_pin(codec, "LineIn"); + /* Enabling the microphone requires the fitting of a 0R + * resistor to connect the line from the microphone jack. + */ + snd_soc_dapm_disable_pin(codec, "MicIn"); /* signal a DAPM event */ snd_soc_dapm_sync(codec); @@ -205,11 +205,6 @@ static int smdk64xx_wm8580_init_paifrx(struct snd_soc_codec *codec) /* Set up PAIFRX audio path */ snd_soc_dapm_add_routes(codec, audio_map_rx, ARRAY_SIZE(audio_map_rx)); - /* All enabled by default */ - snd_soc_dapm_enable_pin(codec, "Front-L/R"); - snd_soc_dapm_enable_pin(codec, "Center/Sub"); - snd_soc_dapm_enable_pin(codec, "Rear-L/R"); - /* signal a DAPM event */ snd_soc_dapm_sync(codec); diff --git a/sound/soc/s6000/s6000-pcm.c b/sound/soc/s6000/s6000-pcm.c index 83b8028e209..0eb1722f658 100644 --- a/sound/soc/s6000/s6000-pcm.c +++ b/sound/soc/s6000/s6000-pcm.c @@ -423,7 +423,7 @@ static void s6000_pcm_free(struct snd_pcm *pcm) snd_pcm_lib_preallocate_free_for_all(pcm); } -static u64 s6000_pcm_dmamask = DMA_32BIT_MASK; +static u64 s6000_pcm_dmamask = DMA_BIT_MASK(32); static int s6000_pcm_new(struct snd_card *card, struct snd_soc_dai *dai, struct snd_pcm *pcm) @@ -435,7 +435,7 @@ static int s6000_pcm_new(struct snd_card *card, if (!card->dev->dma_mask) card->dev->dma_mask = &s6000_pcm_dmamask; if (!card->dev->coherent_dma_mask) - card->dev->coherent_dma_mask = DMA_32BIT_MASK; + card->dev->coherent_dma_mask = DMA_BIT_MASK(32); if (params->dma_in) { s6dmac_disable_chan(DMA_MASK_DMAC(params->dma_in), diff --git a/sound/soc/sh/Kconfig b/sound/soc/sh/Kconfig index 9154b4363db..9e697658655 100644 --- a/sound/soc/sh/Kconfig +++ b/sound/soc/sh/Kconfig @@ -23,7 +23,6 @@ config SND_SOC_SH4_SSI config SND_SOC_SH4_FSI tristate "SH4 FSI support" depends on CPU_SUBTYPE_SH7724 - select SH_DMA help This option enables FSI sound support diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index 44123248b63..e1a3d1a2b4c 100644 --- a/sound/soc/sh/fsi.c +++ b/sound/soc/sh/fsi.c @@ -26,8 +26,6 @@ #include <sound/pcm_params.h> #include <sound/sh_fsi.h> #include <asm/atomic.h> -#include <asm/dma.h> -#include <asm/dma-sh.h> #define DO_FMT 0x0000 #define DOFF_CTL 0x0004 @@ -97,7 +95,6 @@ struct fsi_priv { int fifo_max; int chan; - int dma_chan; int byte_offset; int period_len; @@ -308,62 +305,6 @@ static int fsi_get_fifo_residue(struct fsi_priv *fsi, int is_play) return residue; } -static int fsi_get_residue(struct fsi_priv *fsi, int is_play) -{ - int residue; - int width; - struct snd_pcm_runtime *runtime; - - runtime = fsi->substream->runtime; - - /* get 1 channel data width */ - width = frames_to_bytes(runtime, 1) / fsi->chan; - - if (2 == width) - residue = fsi_get_fifo_residue(fsi, is_play); - else - residue = get_dma_residue(fsi->dma_chan); - - return residue; -} - -/************************************************************************ - - - basic dma function - - -************************************************************************/ -#define PORTA_DMA 0 -#define PORTB_DMA 1 - -static int fsi_get_dma_chan(void) -{ - if (0 != request_dma(PORTA_DMA, "fsia")) - return -EIO; - - if (0 != request_dma(PORTB_DMA, "fsib")) { - free_dma(PORTA_DMA); - return -EIO; - } - - master->fsia.dma_chan = PORTA_DMA; - master->fsib.dma_chan = PORTB_DMA; - - return 0; -} - -static void fsi_free_dma_chan(void) -{ - dma_wait_for_completion(PORTA_DMA); - dma_wait_for_completion(PORTB_DMA); - free_dma(PORTA_DMA); - free_dma(PORTB_DMA); - - master->fsia.dma_chan = -1; - master->fsib.dma_chan = -1; -} - /************************************************************************ @@ -435,44 +376,6 @@ static void fsi_soft_all_reset(void) mdelay(10); } -static void fsi_16data_push(struct fsi_priv *fsi, - struct snd_pcm_runtime *runtime, - int send) -{ - u16 *dma_start; - u32 snd; - int i; - - /* get dma start position for FSI */ - dma_start = (u16 *)runtime->dma_area; - dma_start += fsi->byte_offset / 2; - - /* - * soft dma - * FSI can not use DMA when 16bpp - */ - for (i = 0; i < send; i++) { - snd = (u32)dma_start[i]; - fsi_reg_write(fsi, DODT, snd << 8); - } -} - -static void fsi_32data_push(struct fsi_priv *fsi, - struct snd_pcm_runtime *runtime, - int send) -{ - u32 *dma_start; - - /* get dma start position for FSI */ - dma_start = (u32 *)runtime->dma_area; - dma_start += fsi->byte_offset / 4; - - dma_wait_for_completion(fsi->dma_chan); - dma_configure_channel(fsi->dma_chan, (SM_INC|0x400|TS_32|TM_BUR)); - dma_write(fsi->dma_chan, (u32)dma_start, - (u32)(fsi->base + DODT), send * 4); -} - /* playback interrupt */ static int fsi_data_push(struct fsi_priv *fsi) { @@ -481,6 +384,8 @@ static int fsi_data_push(struct fsi_priv *fsi) int send; int fifo_free; int width; + u8 *start; + int i; if (!fsi || !fsi->substream || @@ -515,12 +420,22 @@ static int fsi_data_push(struct fsi_priv *fsi) if (fifo_free < send) send = fifo_free; - if (2 == width) - fsi_16data_push(fsi, runtime, send); - else if (4 == width) - fsi_32data_push(fsi, runtime, send); - else + start = runtime->dma_area; + start += fsi->byte_offset; + + switch (width) { + case 2: + for (i = 0; i < send; i++) + fsi_reg_write(fsi, DODT, + ((u32)*((u16 *)start + i) << 8)); + break; + case 4: + for (i = 0; i < send; i++) + fsi_reg_write(fsi, DODT, *((u32 *)start + i)); + break; + default: return -EINVAL; + } fsi->byte_offset += send * width; @@ -532,6 +447,75 @@ static int fsi_data_push(struct fsi_priv *fsi) return 0; } +static int fsi_data_pop(struct fsi_priv *fsi) +{ + struct snd_pcm_runtime *runtime; + struct snd_pcm_substream *substream = NULL; + int free; + int fifo_fill; + int width; + u8 *start; + int i; + + if (!fsi || + !fsi->substream || + !fsi->substream->runtime) + return -EINVAL; + + runtime = fsi->substream->runtime; + + /* FSI FIFO has limit. + * So, this driver can not send periods data at a time + */ + if (fsi->byte_offset >= + fsi->period_len * (fsi->periods + 1)) { + + substream = fsi->substream; + fsi->periods = (fsi->periods + 1) % runtime->periods; + + if (0 == fsi->periods) + fsi->byte_offset = 0; + } + + /* get 1 channel data width */ + width = frames_to_bytes(runtime, 1) / fsi->chan; + + /* get free space for alsa */ + free = (fsi->buffer_len - fsi->byte_offset) / width; + + /* get recv size */ + fifo_fill = fsi_get_fifo_residue(fsi, 0); + + if (free < fifo_fill) + fifo_fill = free; + + start = runtime->dma_area; + start += fsi->byte_offset; + + switch (width) { + case 2: + for (i = 0; i < fifo_fill; i++) + *((u16 *)start + i) = + (u16)(fsi_reg_read(fsi, DIDT) >> 8); + break; + case 4: + for (i = 0; i < fifo_fill; i++) + *((u32 *)start + i) = fsi_reg_read(fsi, DIDT); + break; + default: + return -EINVAL; + } + + fsi->byte_offset += fifo_fill * width; + + fsi_irq_enable(fsi, 0); + + if (substream) + snd_pcm_period_elapsed(substream); + + return 0; +} + static irqreturn_t fsi_interrupt(int irq, void *data) { u32 status = fsi_master_read(SOFT_RST) & ~0x00000010; @@ -545,6 +529,10 @@ static irqreturn_t fsi_interrupt(int irq, void *data) fsi_data_push(&master->fsia); if (int_st & INT_B_OUT) fsi_data_push(&master->fsib); + if (int_st & INT_A_IN) + fsi_data_pop(&master->fsia); + if (int_st & INT_B_IN) + fsi_data_pop(&master->fsib); fsi_master_write(INT_ST, 0x0000000); @@ -664,8 +652,6 @@ static int fsi_dai_startup(struct snd_pcm_substream *substream, } fsi_reg_write(fsi, reg, data); - dev_dbg(dai->dev, "use %s format (%d channel) use %d DMAC\n", - msg, fsi->chan, fsi->dma_chan); /* * clear clk reset if master mode @@ -699,16 +685,12 @@ static int fsi_dai_trigger(struct snd_pcm_substream *substream, int cmd, int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; int ret = 0; - /* capture not supported */ - if (!is_play) - return -ENODEV; - switch (cmd) { case SNDRV_PCM_TRIGGER_START: fsi_stream_push(fsi, substream, frames_to_bytes(runtime, runtime->buffer_size), frames_to_bytes(runtime, runtime->period_size)); - ret = fsi_data_push(fsi); + ret = is_play ? fsi_data_push(fsi) : fsi_data_pop(fsi); break; case SNDRV_PCM_TRIGGER_STOP: fsi_irq_disable(fsi, is_play); @@ -780,10 +762,9 @@ static snd_pcm_uframes_t fsi_pointer(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct fsi_priv *fsi = fsi_get(substream); - int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; long location; - location = (fsi->byte_offset - 1) - fsi_get_residue(fsi, is_play); + location = (fsi->byte_offset - 1); if (location < 0) location = 0; @@ -845,7 +826,12 @@ struct snd_soc_dai fsi_soc_dai[] = { .channels_min = 1, .channels_max = 8, }, - /* capture not supported */ + .capture = { + .rates = FSI_RATES, + .formats = FSI_FMTS, + .channels_min = 1, + .channels_max = 8, + }, .ops = &fsi_dai_ops, }, { @@ -857,7 +843,12 @@ struct snd_soc_dai fsi_soc_dai[] = { .channels_min = 1, .channels_max = 8, }, - /* capture not supported */ + .capture = { + .rates = FSI_RATES, + .formats = FSI_FMTS, + .channels_min = 1, + .channels_max = 8, + }, .ops = &fsi_dai_ops, }, }; @@ -912,22 +903,13 @@ static int fsi_probe(struct platform_device *pdev) master->fsia.base = master->base; master->fsib.base = master->base + 0x40; - master->fsia.dma_chan = -1; - master->fsib.dma_chan = -1; - - ret = fsi_get_dma_chan(); - if (ret < 0) { - dev_err(&pdev->dev, "cannot get dma api\n"); - goto exit_iounmap; - } - /* FSI is based on SPU mstp */ snprintf(clk_name, sizeof(clk_name), "spu%d", pdev->id); master->clk = clk_get(NULL, clk_name); if (IS_ERR(master->clk)) { dev_err(&pdev->dev, "cannot get %s mstp\n", clk_name); ret = -EIO; - goto exit_free_dma; + goto exit_iounmap; } fsi_soc_dai[0].dev = &pdev->dev; @@ -938,7 +920,7 @@ static int fsi_probe(struct platform_device *pdev) ret = request_irq(irq, &fsi_interrupt, IRQF_DISABLED, "fsi", master); if (ret) { dev_err(&pdev->dev, "irq request err\n"); - goto exit_free_dma; + goto exit_iounmap; } ret = snd_soc_register_platform(&fsi_soc_platform); @@ -951,8 +933,6 @@ static int fsi_probe(struct platform_device *pdev) exit_free_irq: free_irq(irq, master); -exit_free_dma: - fsi_free_dma_chan(); exit_iounmap: iounmap(master->base); exit_kfree: @@ -969,8 +949,6 @@ static int fsi_remove(struct platform_device *pdev) clk_put(master->clk); - fsi_free_dma_chan(); - free_irq(master->irq, master); iounmap(master->base); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 2d190df9fcc..ef8f28284cb 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -37,7 +37,6 @@ #include <sound/initval.h> static DEFINE_MUTEX(pcm_mutex); -static DEFINE_MUTEX(io_mutex); static DECLARE_WAIT_QUEUE_HEAD(soc_pm_waitq); #ifdef CONFIG_DEBUG_FS @@ -81,6 +80,173 @@ static int run_delayed_work(struct delayed_work *dwork) return ret; } +/* codec register dump */ +static ssize_t soc_codec_reg_show(struct snd_soc_codec *codec, char *buf) +{ + int i, step = 1, count = 0; + + if (!codec->reg_cache_size) + return 0; + + if (codec->reg_cache_step) + step = codec->reg_cache_step; + + count += sprintf(buf, "%s registers\n", codec->name); + for (i = 0; i < codec->reg_cache_size; i += step) { + if (codec->readable_register && !codec->readable_register(i)) + continue; + + count += sprintf(buf + count, "%2x: ", i); + if (count >= PAGE_SIZE - 1) + break; + + if (codec->display_register) + count += codec->display_register(codec, buf + count, + PAGE_SIZE - count, i); + else + count += snprintf(buf + count, PAGE_SIZE - count, + "%4x", codec->read(codec, i)); + + if (count >= PAGE_SIZE - 1) + break; + + count += snprintf(buf + count, PAGE_SIZE - count, "\n"); + if (count >= PAGE_SIZE - 1) + break; + } + + /* Truncate count; min() would cause a warning */ + if (count >= PAGE_SIZE) + count = PAGE_SIZE - 1; + + return count; +} +static ssize_t codec_reg_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct snd_soc_device *devdata = dev_get_drvdata(dev); + return soc_codec_reg_show(devdata->card->codec, buf); +} + +static DEVICE_ATTR(codec_reg, 0444, codec_reg_show, NULL); + +#ifdef CONFIG_DEBUG_FS +static int codec_reg_open_file(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t codec_reg_read_file(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + ssize_t ret; + struct snd_soc_codec *codec = file->private_data; + char *buf = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; + ret = soc_codec_reg_show(codec, buf); + if (ret >= 0) + ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret); + kfree(buf); + return ret; +} + +static ssize_t codec_reg_write_file(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos) +{ + char buf[32]; + int buf_size; + char *start = buf; + unsigned long reg, value; + int step = 1; + struct snd_soc_codec *codec = file->private_data; + + buf_size = min(count, (sizeof(buf)-1)); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + buf[buf_size] = 0; + + if (codec->reg_cache_step) + step = codec->reg_cache_step; + + while (*start == ' ') + start++; + reg = simple_strtoul(start, &start, 16); + if ((reg >= codec->reg_cache_size) || (reg % step)) + return -EINVAL; + while (*start == ' ') + start++; + if (strict_strtoul(start, 16, &value)) + return -EINVAL; + codec->write(codec, reg, value); + return buf_size; +} + +static const struct file_operations codec_reg_fops = { + .open = codec_reg_open_file, + .read = codec_reg_read_file, + .write = codec_reg_write_file, +}; + +static void soc_init_codec_debugfs(struct snd_soc_codec *codec) +{ + char codec_root[128]; + + if (codec->dev) + snprintf(codec_root, sizeof(codec_root), + "%s.%s", codec->name, dev_name(codec->dev)); + else + snprintf(codec_root, sizeof(codec_root), + "%s", codec->name); + + codec->debugfs_codec_root = debugfs_create_dir(codec_root, + debugfs_root); + if (!codec->debugfs_codec_root) { + printk(KERN_WARNING + "ASoC: Failed to create codec debugfs directory\n"); + return; + } + + codec->debugfs_reg = debugfs_create_file("codec_reg", 0644, + codec->debugfs_codec_root, + codec, &codec_reg_fops); + if (!codec->debugfs_reg) + printk(KERN_WARNING + "ASoC: Failed to create codec register debugfs file\n"); + + codec->debugfs_pop_time = debugfs_create_u32("dapm_pop_time", 0744, + codec->debugfs_codec_root, + &codec->pop_time); + if (!codec->debugfs_pop_time) + printk(KERN_WARNING + "Failed to create pop time debugfs file\n"); + + codec->debugfs_dapm = debugfs_create_dir("dapm", + codec->debugfs_codec_root); + if (!codec->debugfs_dapm) + printk(KERN_WARNING + "Failed to create DAPM debugfs directory\n"); + + snd_soc_dapm_debugfs_init(codec); +} + +static void soc_cleanup_codec_debugfs(struct snd_soc_codec *codec) +{ + debugfs_remove_recursive(codec->debugfs_codec_root); +} + +#else + +static inline void soc_init_codec_debugfs(struct snd_soc_codec *codec) +{ +} + +static inline void soc_cleanup_codec_debugfs(struct snd_soc_codec *codec) +{ +} +#endif + #ifdef CONFIG_SND_SOC_AC97_BUS /* unregister ac97 codec */ static int soc_ac97_dev_unregister(struct snd_soc_codec *codec) @@ -804,6 +970,7 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card) struct platform_device, dev); struct snd_soc_codec_device *codec_dev = card->socdev->codec_dev; + struct snd_soc_codec *codec; struct snd_soc_platform *platform; struct snd_soc_dai *dai; int i, found, ret, ac97; @@ -892,6 +1059,7 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card) if (ret < 0) goto cpu_dai_err; } + codec = card->codec; if (platform->probe) { ret = platform->probe(pdev); @@ -906,10 +1074,69 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card) INIT_WORK(&card->deferred_resume_work, soc_resume_deferred); #endif + for (i = 0; i < card->num_links; i++) { + if (card->dai_link[i].init) { + ret = card->dai_link[i].init(codec); + if (ret < 0) { + printk(KERN_ERR "asoc: failed to init %s\n", + card->dai_link[i].stream_name); + continue; + } + } + if (card->dai_link[i].codec_dai->ac97_control) + ac97 = 1; + } + + snprintf(codec->card->shortname, sizeof(codec->card->shortname), + "%s", card->name); + snprintf(codec->card->longname, sizeof(codec->card->longname), + "%s (%s)", card->name, codec->name); + + /* Make sure all DAPM widgets are instantiated */ + snd_soc_dapm_new_widgets(codec); + + ret = snd_card_register(codec->card); + if (ret < 0) { + printk(KERN_ERR "asoc: failed to register soundcard for %s\n", + codec->name); + goto card_err; + } + + mutex_lock(&codec->mutex); +#ifdef CONFIG_SND_SOC_AC97_BUS + /* Only instantiate AC97 if not already done by the adaptor + * for the generic AC97 subsystem. + */ + if (ac97 && strcmp(codec->name, "AC97") != 0) { + ret = soc_ac97_dev_register(codec); + if (ret < 0) { + printk(KERN_ERR "asoc: AC97 device register failed\n"); + snd_card_free(codec->card); + mutex_unlock(&codec->mutex); + goto card_err; + } + } +#endif + + ret = snd_soc_dapm_sys_add(card->socdev->dev); + if (ret < 0) + printk(KERN_WARNING "asoc: failed to add dapm sysfs entries\n"); + + ret = device_create_file(card->socdev->dev, &dev_attr_codec_reg); + if (ret < 0) + printk(KERN_WARNING "asoc: failed to add codec sysfs files\n"); + + soc_init_codec_debugfs(codec); + mutex_unlock(&codec->mutex); + card->instantiated = 1; return; +card_err: + if (platform->remove) + platform->remove(pdev); + platform_err: if (codec_dev->remove) codec_dev->remove(pdev); @@ -1112,173 +1339,6 @@ int snd_soc_codec_volatile_register(struct snd_soc_codec *codec, int reg) } EXPORT_SYMBOL_GPL(snd_soc_codec_volatile_register); -/* codec register dump */ -static ssize_t soc_codec_reg_show(struct snd_soc_codec *codec, char *buf) -{ - int i, step = 1, count = 0; - - if (!codec->reg_cache_size) - return 0; - - if (codec->reg_cache_step) - step = codec->reg_cache_step; - - count += sprintf(buf, "%s registers\n", codec->name); - for (i = 0; i < codec->reg_cache_size; i += step) { - if (codec->readable_register && !codec->readable_register(i)) - continue; - - count += sprintf(buf + count, "%2x: ", i); - if (count >= PAGE_SIZE - 1) - break; - - if (codec->display_register) - count += codec->display_register(codec, buf + count, - PAGE_SIZE - count, i); - else - count += snprintf(buf + count, PAGE_SIZE - count, - "%4x", codec->read(codec, i)); - - if (count >= PAGE_SIZE - 1) - break; - - count += snprintf(buf + count, PAGE_SIZE - count, "\n"); - if (count >= PAGE_SIZE - 1) - break; - } - - /* Truncate count; min() would cause a warning */ - if (count >= PAGE_SIZE) - count = PAGE_SIZE - 1; - - return count; -} -static ssize_t codec_reg_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct snd_soc_device *devdata = dev_get_drvdata(dev); - return soc_codec_reg_show(devdata->card->codec, buf); -} - -static DEVICE_ATTR(codec_reg, 0444, codec_reg_show, NULL); - -#ifdef CONFIG_DEBUG_FS -static int codec_reg_open_file(struct inode *inode, struct file *file) -{ - file->private_data = inode->i_private; - return 0; -} - -static ssize_t codec_reg_read_file(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - ssize_t ret; - struct snd_soc_codec *codec = file->private_data; - char *buf = kmalloc(PAGE_SIZE, GFP_KERNEL); - if (!buf) - return -ENOMEM; - ret = soc_codec_reg_show(codec, buf); - if (ret >= 0) - ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret); - kfree(buf); - return ret; -} - -static ssize_t codec_reg_write_file(struct file *file, - const char __user *user_buf, size_t count, loff_t *ppos) -{ - char buf[32]; - int buf_size; - char *start = buf; - unsigned long reg, value; - int step = 1; - struct snd_soc_codec *codec = file->private_data; - - buf_size = min(count, (sizeof(buf)-1)); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - buf[buf_size] = 0; - - if (codec->reg_cache_step) - step = codec->reg_cache_step; - - while (*start == ' ') - start++; - reg = simple_strtoul(start, &start, 16); - if ((reg >= codec->reg_cache_size) || (reg % step)) - return -EINVAL; - while (*start == ' ') - start++; - if (strict_strtoul(start, 16, &value)) - return -EINVAL; - codec->write(codec, reg, value); - return buf_size; -} - -static const struct file_operations codec_reg_fops = { - .open = codec_reg_open_file, - .read = codec_reg_read_file, - .write = codec_reg_write_file, -}; - -static void soc_init_codec_debugfs(struct snd_soc_codec *codec) -{ - char codec_root[128]; - - if (codec->dev) - snprintf(codec_root, sizeof(codec_root), - "%s.%s", codec->name, dev_name(codec->dev)); - else - snprintf(codec_root, sizeof(codec_root), - "%s", codec->name); - - codec->debugfs_codec_root = debugfs_create_dir(codec_root, - debugfs_root); - if (!codec->debugfs_codec_root) { - printk(KERN_WARNING - "ASoC: Failed to create codec debugfs directory\n"); - return; - } - - codec->debugfs_reg = debugfs_create_file("codec_reg", 0644, - codec->debugfs_codec_root, - codec, &codec_reg_fops); - if (!codec->debugfs_reg) - printk(KERN_WARNING - "ASoC: Failed to create codec register debugfs file\n"); - - codec->debugfs_pop_time = debugfs_create_u32("dapm_pop_time", 0744, - codec->debugfs_codec_root, - &codec->pop_time); - if (!codec->debugfs_pop_time) - printk(KERN_WARNING - "Failed to create pop time debugfs file\n"); - - codec->debugfs_dapm = debugfs_create_dir("dapm", - codec->debugfs_codec_root); - if (!codec->debugfs_dapm) - printk(KERN_WARNING - "Failed to create DAPM debugfs directory\n"); - - snd_soc_dapm_debugfs_init(codec); -} - -static void soc_cleanup_codec_debugfs(struct snd_soc_codec *codec) -{ - debugfs_remove_recursive(codec->debugfs_codec_root); -} - -#else - -static inline void soc_init_codec_debugfs(struct snd_soc_codec *codec) -{ -} - -static inline void soc_cleanup_codec_debugfs(struct snd_soc_codec *codec) -{ -} -#endif - /** * snd_soc_new_ac97_codec - initailise AC97 device * @codec: audio codec @@ -1346,19 +1406,41 @@ int snd_soc_update_bits(struct snd_soc_codec *codec, unsigned short reg, int change; unsigned int old, new; - mutex_lock(&io_mutex); old = snd_soc_read(codec, reg); new = (old & ~mask) | value; change = old != new; if (change) snd_soc_write(codec, reg, new); - mutex_unlock(&io_mutex); return change; } EXPORT_SYMBOL_GPL(snd_soc_update_bits); /** + * snd_soc_update_bits_locked - update codec register bits + * @codec: audio codec + * @reg: codec register + * @mask: register mask + * @value: new value + * + * Writes new register value, and takes the codec mutex. + * + * Returns 1 for change else 0. + */ +static int snd_soc_update_bits_locked(struct snd_soc_codec *codec, + unsigned short reg, unsigned int mask, + unsigned int value) +{ + int change; + + mutex_lock(&codec->mutex); + change = snd_soc_update_bits(codec, reg, mask, value); + mutex_unlock(&codec->mutex); + + return change; +} + +/** * snd_soc_test_bits - test register for change * @codec: audio codec * @reg: codec register @@ -1376,11 +1458,9 @@ int snd_soc_test_bits(struct snd_soc_codec *codec, unsigned short reg, int change; unsigned int old, new; - mutex_lock(&io_mutex); old = snd_soc_read(codec, reg); new = (old & ~mask) | value; change = old != new; - mutex_unlock(&io_mutex); return change; } @@ -1427,89 +1507,16 @@ int snd_soc_new_pcms(struct snd_soc_device *socdev, int idx, const char *xid) mutex_unlock(&codec->mutex); return ret; } - } - - mutex_unlock(&codec->mutex); - return ret; -} -EXPORT_SYMBOL_GPL(snd_soc_new_pcms); - -/** - * snd_soc_init_card - register sound card - * @socdev: the SoC audio device - * - * Register a SoC sound card. Also registers an AC97 device if the - * codec is AC97 for ad hoc devices. - * - * Returns 0 for success, else error. - */ -int snd_soc_init_card(struct snd_soc_device *socdev) -{ - struct snd_soc_card *card = socdev->card; - struct snd_soc_codec *codec = card->codec; - int ret = 0, i, ac97 = 0, err = 0; - - for (i = 0; i < card->num_links; i++) { - if (card->dai_link[i].init) { - err = card->dai_link[i].init(codec); - if (err < 0) { - printk(KERN_ERR "asoc: failed to init %s\n", - card->dai_link[i].stream_name); - continue; - } - } if (card->dai_link[i].codec_dai->ac97_control) { - ac97 = 1; snd_ac97_dev_add_pdata(codec->ac97, card->dai_link[i].cpu_dai->ac97_pdata); } } - snprintf(codec->card->shortname, sizeof(codec->card->shortname), - "%s", card->name); - snprintf(codec->card->longname, sizeof(codec->card->longname), - "%s (%s)", card->name, codec->name); - - /* Make sure all DAPM widgets are instantiated */ - snd_soc_dapm_new_widgets(codec); - - ret = snd_card_register(codec->card); - if (ret < 0) { - printk(KERN_ERR "asoc: failed to register soundcard for %s\n", - codec->name); - goto out; - } - - mutex_lock(&codec->mutex); -#ifdef CONFIG_SND_SOC_AC97_BUS - /* Only instantiate AC97 if not already done by the adaptor - * for the generic AC97 subsystem. - */ - if (ac97 && strcmp(codec->name, "AC97") != 0) { - ret = soc_ac97_dev_register(codec); - if (ret < 0) { - printk(KERN_ERR "asoc: AC97 device register failed\n"); - snd_card_free(codec->card); - mutex_unlock(&codec->mutex); - goto out; - } - } -#endif - - err = snd_soc_dapm_sys_add(socdev->dev); - if (err < 0) - printk(KERN_WARNING "asoc: failed to add dapm sysfs entries\n"); - err = device_create_file(socdev->dev, &dev_attr_codec_reg); - if (err < 0) - printk(KERN_WARNING "asoc: failed to add codec sysfs files\n"); - - soc_init_codec_debugfs(codec); mutex_unlock(&codec->mutex); - -out: return ret; } -EXPORT_SYMBOL_GPL(snd_soc_init_card); +EXPORT_SYMBOL_GPL(snd_soc_new_pcms); /** * snd_soc_free_pcms - free sound card and pcms @@ -1711,7 +1718,7 @@ int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol, mask |= (bitmask - 1) << e->shift_r; } - return snd_soc_update_bits(codec, e->reg, mask, val); + return snd_soc_update_bits_locked(codec, e->reg, mask, val); } EXPORT_SYMBOL_GPL(snd_soc_put_enum_double); @@ -1785,7 +1792,7 @@ int snd_soc_put_value_enum_double(struct snd_kcontrol *kcontrol, mask |= e->mask << e->shift_r; } - return snd_soc_update_bits(codec, e->reg, mask, val); + return snd_soc_update_bits_locked(codec, e->reg, mask, val); } EXPORT_SYMBOL_GPL(snd_soc_put_value_enum_double); @@ -1946,7 +1953,7 @@ int snd_soc_put_volsw(struct snd_kcontrol *kcontrol, val_mask |= mask << rshift; val |= val2 << rshift; } - return snd_soc_update_bits(codec, reg, val_mask, val); + return snd_soc_update_bits_locked(codec, reg, val_mask, val); } EXPORT_SYMBOL_GPL(snd_soc_put_volsw); @@ -2052,11 +2059,11 @@ int snd_soc_put_volsw_2r(struct snd_kcontrol *kcontrol, val = val << shift; val2 = val2 << shift; - err = snd_soc_update_bits(codec, reg, val_mask, val); + err = snd_soc_update_bits_locked(codec, reg, val_mask, val); if (err < 0) return err; - err = snd_soc_update_bits(codec, reg2, val_mask, val2); + err = snd_soc_update_bits_locked(codec, reg2, val_mask, val2); return err; } EXPORT_SYMBOL_GPL(snd_soc_put_volsw_2r); @@ -2135,7 +2142,7 @@ int snd_soc_put_volsw_s8(struct snd_kcontrol *kcontrol, val = (ucontrol->value.integer.value[0]+min) & 0xff; val |= ((ucontrol->value.integer.value[1]+min) & 0xff) << 8; - return snd_soc_update_bits(codec, reg, 0xffff, val); + return snd_soc_update_bits_locked(codec, reg, 0xffff, val); } EXPORT_SYMBOL_GPL(snd_soc_put_volsw_s8); diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index eaadb4b742f..0d294ef7259 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -977,9 +977,19 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event) if (!w->power_check) continue; - power = w->power_check(w); - if (power) - sys_power = 1; + /* If we're suspending then pull down all the + * power. */ + switch (event) { + case SND_SOC_DAPM_STREAM_SUSPEND: + power = 0; + break; + + default: + power = w->power_check(w); + if (power) + sys_power = 1; + break; + } if (w->power == power) continue; @@ -1003,8 +1013,12 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event) case SND_SOC_DAPM_STREAM_RESUME: sys_power = 1; break; + case SND_SOC_DAPM_STREAM_SUSPEND: + sys_power = 0; + break; case SND_SOC_DAPM_STREAM_NOP: sys_power = codec->bias_level != SND_SOC_BIAS_STANDBY; + break; default: break; } diff --git a/sound/soc/soc-jack.c b/sound/soc/soc-jack.c index 12124149601..3c07a94c2e3 100644 --- a/sound/soc/soc-jack.c +++ b/sound/soc/soc-jack.c @@ -163,6 +163,9 @@ static void snd_soc_jack_gpio_detect(struct snd_soc_jack_gpio *gpio) else report = 0; + if (gpio->jack_status_check) + report = gpio->jack_status_check(); + snd_soc_jack_report(jack, report, gpio->report); } diff --git a/sound/soc/soc-utils.c b/sound/soc/soc-utils.c new file mode 100644 index 00000000000..b16aaaeb0aa --- /dev/null +++ b/sound/soc/soc-utils.c @@ -0,0 +1,68 @@ +/* + * soc-util.c -- ALSA SoC Audio Layer utility functions + * + * Copyright 2009 Wolfson Microelectronics PLC. + * + * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> + * Liam Girdwood <lrg@slimlogic.co.uk> + * + * + * 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 <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> + +int snd_soc_calc_frame_size(int sample_size, int channels, int tdm_slots) +{ + return sample_size * channels * tdm_slots; +} +EXPORT_SYMBOL_GPL(snd_soc_calc_frame_size); + +int snd_soc_params_to_frame_size(struct snd_pcm_hw_params *params) +{ + int sample_size; + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + case SNDRV_PCM_FORMAT_S16_BE: + sample_size = 16; + break; + case SNDRV_PCM_FORMAT_S20_3LE: + case SNDRV_PCM_FORMAT_S20_3BE: + sample_size = 20; + break; + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S24_BE: + sample_size = 24; + break; + case SNDRV_PCM_FORMAT_S32_LE: + case SNDRV_PCM_FORMAT_S32_BE: + sample_size = 32; + break; + default: + return -ENOTSUPP; + } + + return snd_soc_calc_frame_size(sample_size, params_channels(params), + 1); +} +EXPORT_SYMBOL_GPL(snd_soc_params_to_frame_size); + +int snd_soc_params_to_bclk(struct snd_pcm_hw_params *params) +{ + int ret; + + ret = snd_soc_params_to_frame_size(params); + + if (ret > 0) + return ret * params_rate(params); + else + return ret; +} +EXPORT_SYMBOL_GPL(snd_soc_params_to_bclk); diff --git a/sound/usb/caiaq/audio.c b/sound/usb/caiaq/audio.c index 121af0644fd..86b2c3b92df 100644 --- a/sound/usb/caiaq/audio.c +++ b/sound/usb/caiaq/audio.c @@ -62,10 +62,14 @@ static void activate_substream(struct snd_usb_caiaqdev *dev, struct snd_pcm_substream *sub) { + spin_lock(&dev->spinlock); + if (sub->stream == SNDRV_PCM_STREAM_PLAYBACK) dev->sub_playback[sub->number] = sub; else dev->sub_capture[sub->number] = sub; + + spin_unlock(&dev->spinlock); } static void @@ -269,16 +273,22 @@ snd_usb_caiaq_pcm_pointer(struct snd_pcm_substream *sub) { int index = sub->number; struct snd_usb_caiaqdev *dev = snd_pcm_substream_chip(sub); + snd_pcm_uframes_t ptr; + + spin_lock(&dev->spinlock); if (dev->input_panic || dev->output_panic) - return SNDRV_PCM_POS_XRUN; + ptr = SNDRV_PCM_POS_XRUN; if (sub->stream == SNDRV_PCM_STREAM_PLAYBACK) - return bytes_to_frames(sub->runtime, + ptr = bytes_to_frames(sub->runtime, dev->audio_out_buf_pos[index]); else - return bytes_to_frames(sub->runtime, + ptr = bytes_to_frames(sub->runtime, dev->audio_in_buf_pos[index]); + + spin_unlock(&dev->spinlock); + return ptr; } /* operators for both playback and capture */ diff --git a/sound/usb/caiaq/device.c b/sound/usb/caiaq/device.c index 83e6c1312d4..a3f02dd9744 100644 --- a/sound/usb/caiaq/device.c +++ b/sound/usb/caiaq/device.c @@ -35,7 +35,7 @@ #include "input.h" MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>"); -MODULE_DESCRIPTION("caiaq USB audio, version 1.3.19"); +MODULE_DESCRIPTION("caiaq USB audio, version 1.3.20"); MODULE_LICENSE("GPL"); MODULE_SUPPORTED_DEVICE("{{Native Instruments, RigKontrol2}," "{Native Instruments, RigKontrol3}," |