123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421 |
- /*
- * ngene_snd.c: nGene PCIe bridge driver ALSA support
- *
- * Copyright (C) 2005-2007 Micronas
- *
- * Based on the initial ALSA support port by Thomas Eschbach.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 only, 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 Street, Fifth Floor, Boston, MA
- * 02110-1301, USA
- * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
- */
- #include <linux/version.h>
- #include <linux/module.h>
- #include "ngene.h"
- #include "ngene-ioctls.h"
- static int sound_dev;
- /* sound module parameters (see "Module Parameters") */
- static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
- static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
- static int enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 1};
- /****************************************************************************/
- /* PCM Sound Funktions ******************************************************/
- /****************************************************************************/
- static struct snd_pcm_hardware snd_mychip_capture_hw = {
- .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER),
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- .rates = (SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000
- | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000
- | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000),
- .rate_min = 11025,
- .rate_max = 48000,
- .channels_min = 2,
- .channels_max = 2,
- .buffer_bytes_max = 16384,
- .period_bytes_min = 8192,
- .period_bytes_max = 8192,
- .periods_min = 1,
- .periods_max = 2,
- };
- /* open callback */
- static int snd_mychip_capture_open(struct snd_pcm_substream *substream)
- {
- struct mychip *chip = snd_pcm_substream_chip(substream);
- struct snd_pcm_runtime *runtime = substream->runtime;
- runtime->hw = snd_mychip_capture_hw;
- chip->substream = substream;
- return 0;
- }
- /* close callback */
- static int snd_mychip_capture_close(struct snd_pcm_substream *substream)
- {
- struct mychip *chip = snd_pcm_substream_chip(substream);
- chip->substream = NULL;
- return 0;
- }
- /* hw_params callback */
- static int snd_mychip_pcm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
- {
- struct mychip *chip = snd_pcm_substream_chip(substream);
- struct ngene_channel *chan = chip->chan;
- if (chan->soundbuffisallocated == 0) {
- chan->soundbuffisallocated = 1;
- return snd_pcm_lib_malloc_pages(substream,
- params_buffer_bytes(hw_params));
- }
- return 0;
- }
- /* hw_free callback */
- static int snd_mychip_pcm_hw_free(struct snd_pcm_substream *substream)
- {
- struct mychip *chip = snd_pcm_substream_chip(substream);
- struct ngene_channel *chan = chip->chan;
- int retval = 0;
- if (chan->soundbuffisallocated == 1) {
- chan->soundbuffisallocated = 0;
- retval = snd_pcm_lib_free_pages(substream);
- }
- return retval;
- }
- /* prepare callback */
- static int snd_mychip_pcm_prepare(struct snd_pcm_substream *substream)
- {
- struct mychip *chip = snd_pcm_substream_chip(substream);
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct ngene_channel *chan = chip->chan;
- struct ngene_channel *ch = &chan->dev->channel[chan->number - 2];
- struct i2c_adapter *adap = &ch->i2c_adapter;
- if (ch->soundstreamon == 1)
- ;/*ngene_command_stream_control_sound(chan->dev, chan->number,
- 0x00, 0x00);*/
- i2c_clients_command(adap, IOCTL_MIC_DEC_SRATE, &(runtime->rate));
- mdelay(80);
- if (ch->soundstreamon == 1)
- ;/*ngene_command_stream_control_sound(chan->dev, chan->number,
- 0x80, 0x04);*/
- return 0;
- }
- /* trigger callback */
- static int snd_mychip_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
- {
- struct mychip *chip = snd_pcm_substream_chip(substream);
- struct ngene_channel *chan = chip->chan;
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- /* do something to start the PCM engine */
- chan->sndbuffflag = 0;
- break;
- case SNDRV_PCM_TRIGGER_STOP:
- /* do something to stop the PCM engine */
- chip->substream = NULL;
- chan->sndbuffflag = 0;
- break;
- default:
- return -EINVAL;
- }
- return 0;
- }
- /* pointer callback */
- static snd_pcm_uframes_t
- snd_mychip_pcm_pointer(struct snd_pcm_substream *substream)
- {
- struct mychip *chip = snd_pcm_substream_chip(substream);
- struct ngene_channel *chan = chip->chan;
- unsigned int current_ptr;
- if (chan->sndbuffflag == 0) {
- current_ptr = (unsigned int)
- bytes_to_frames(substream->runtime, 0);
- } else {
- current_ptr = (unsigned int)
- bytes_to_frames(substream->runtime, 8192);
- }
- return current_ptr;
- }
- /*copy sound buffer to pcm middel layer*/
- static int snd_capture_copy(struct snd_pcm_substream *substream, int channel,
- snd_pcm_uframes_t pos, void *dst,
- snd_pcm_uframes_t count)
- {
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct mychip *chip = snd_pcm_substream_chip(substream);
- struct ngene_channel *chan = chip->chan;
- memcpy(dst, chan->soundbuffer, frames_to_bytes(runtime, count));
- return 0;
- }
- static int snd_pcm_capture_silence(struct snd_pcm_substream *substream,
- int channel,
- snd_pcm_uframes_t pos,
- snd_pcm_uframes_t count)
- {
- /*
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct mychip *chip = snd_pcm_substream_chip(substream);
- struct ngene_channel *chan = chip->chan;
- */
- return 0;
- }
- /* operators */
- static struct snd_pcm_ops snd_mychip_capture_ops = {
- .open = snd_mychip_capture_open,
- .close = snd_mychip_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_mychip_pcm_hw_params,
- .hw_free = snd_mychip_pcm_hw_free,
- .prepare = snd_mychip_pcm_prepare,
- .trigger = snd_mychip_pcm_trigger,
- .pointer = snd_mychip_pcm_pointer,
- .copy = snd_capture_copy,
- .silence = snd_pcm_capture_silence,
- };
- static void mychip_pcm_free(struct snd_pcm *pcm)
- {
- pcm->private_data = NULL;
- }
- /* create a pcm device */
- static int snd_mychip_new_pcm(struct mychip *chip, struct ngene_channel *chan)
- {
- struct snd_pcm *pcm;
- int err;
- char gro[10];
- sprintf(gro, "PCM%d", chan->number);
- err = snd_pcm_new(chip->card, gro, 0, 0, 1, &pcm);
- if (err < 0)
- return err;
- pcm->private_data = chip;
- pcm->private_free = mychip_pcm_free;
- sprintf(pcm->name, "MyPCM_%d", chan->number);
- chip->pcm = pcm;
- /* set operators */
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_mychip_capture_ops);
- /* pre-allocation of buffers */
- err = snd_pcm_lib_preallocate_pages_for_all(pcm,
- SNDRV_DMA_TYPE_CONTINUOUS,
- snd_dma_continuous_data(
- GFP_KERNEL),
- 0, 16 * 1024);
- return 0;
- }
- #define ngene_VOLUME(xname, xindex, addr) \
- { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
- .info = snd_volume_info, \
- .get = snd_volume_get, .put = snd_volume_put, \
- .private_value = addr }
- static int snd_volume_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
- {
- uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
- uinfo->count = 2;
- uinfo->value.integer.min = 0;
- uinfo->value.integer.max = 20;
- return 0;
- }
- static int snd_volume_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
- {
- struct mychip *chip = snd_kcontrol_chip(kcontrol);
- int addr = kcontrol->private_value;
- ucontrol->value.integer.value[0] = chip->mixer_volume[addr][0];
- ucontrol->value.integer.value[1] = chip->mixer_volume[addr][1];
- return 0;
- }
- static int snd_volume_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
- {
- struct mychip *chip = snd_kcontrol_chip(kcontrol);
- int change, addr = kcontrol->private_value;
- int left, right;
- left = ucontrol->value.integer.value[0];
- if (left < 0)
- left = 0;
- if (left > 20)
- left = 20;
- right = ucontrol->value.integer.value[1];
- if (right < 0)
- right = 0;
- if (right > 20)
- right = 20;
- spin_lock_irq(&chip->mixer_lock);
- change = chip->mixer_volume[addr][0] != left ||
- chip->mixer_volume[addr][1] != right;
- chip->mixer_volume[addr][0] = left;
- chip->mixer_volume[addr][1] = right;
- spin_unlock_irq(&chip->mixer_lock);
- return change;
- }
- #define ngene_CAPSRC(xname, xindex, addr) \
- { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
- .info = snd_capsrc_info, \
- .get = snd_capsrc_get, .put = snd_capsrc_put, \
- .private_value = addr }
- static int snd_capsrc_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
- {
- uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
- uinfo->count = 2;
- uinfo->value.integer.min = 0;
- uinfo->value.integer.max = 1;
- return 0;
- }
- static int snd_capsrc_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
- {
- struct mychip *chip = snd_kcontrol_chip(kcontrol);
- int addr = kcontrol->private_value;
- spin_lock_irq(&chip->mixer_lock);
- ucontrol->value.integer.value[0] = chip->capture_source[addr][0];
- ucontrol->value.integer.value[1] = chip->capture_source[addr][1];
- spin_unlock_irq(&chip->mixer_lock);
- return 0;
- }
- static int snd_capsrc_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
- {
- struct mychip *chip = snd_kcontrol_chip(kcontrol);
- int change, addr = kcontrol->private_value;
- int left, right;
- left = ucontrol->value.integer.value[0] & 1;
- right = ucontrol->value.integer.value[1] & 1;
- spin_lock_irq(&chip->mixer_lock);
- change = chip->capture_source[addr][0] != left ||
- chip->capture_source[addr][1] != right;
- chip->capture_source[addr][0] = left;
- chip->capture_source[addr][1] = right;
- spin_unlock_irq(&chip->mixer_lock);
- if (change)
- printk(KERN_INFO "snd_capsrc_put change\n");
- return 0;
- }
- static struct snd_kcontrol_new snd_controls[] = {
- ngene_VOLUME("Video Volume", 0, MIXER_ADDR_TVTUNER),
- ngene_CAPSRC("Video Capture Switch", 0, MIXER_ADDR_TVTUNER),
- };
- static int snd_card_new_mixer(struct mychip *chip)
- {
- struct snd_card *card = chip->card;
- unsigned int idx;
- int err;
- strcpy(card->mixername, "NgeneMixer");
- for (idx = 0; idx < ARRAY_SIZE(snd_controls); idx++) {
- err = snd_ctl_add(card, snd_ctl_new1(&snd_controls[idx], chip));
- if (err < 0)
- return err;
- }
- return 0;
- }
- int ngene_snd_init(struct ngene_channel *chan)
- {
- struct snd_card *card;
- struct mychip *chip;
- int err;
- if (sound_dev >= SNDRV_CARDS)
- return -ENODEV;
- if (!enable[sound_dev]) {
- sound_dev++;
- return -ENOENT;
- }
- card = snd_card_new(index[sound_dev], id[sound_dev],
- THIS_MODULE, sizeof(struct mychip));
- if (card == NULL)
- return -ENOMEM;
- chip = card->private_data;
- chip->card = card;
- chip->irq = -1;
- sprintf(card->shortname, "MyChip%d%d", chan->dev->nr, chan->number);
- sprintf(card->shortname, "Myown%d%d", chan->dev->nr, chan->number);
- sprintf(card->longname, "My first Own Chip on Card Nr.%d is %d",
- chan->dev->nr, chan->number);
- spin_lock_init(&chip->lock);
- spin_lock_init(&chip->mixer_lock);
- snd_card_new_mixer(chip);
- snd_mychip_new_pcm(chip, chan);
- err = snd_card_register(card);
- if (err < 0) {
- snd_card_free(card);
- return err;
- }
- chan->soundcard = card;
- chan->mychip = chip;
- chip->chan = chan;
- sound_dev++;
- return 0;
- }
- int ngene_snd_exit(struct ngene_channel *chan)
- {
- snd_card_free(chan->soundcard);
- return 0;
- }
|