diff options
Diffstat (limited to 'linux/drivers/media/video/ngene/ngene-snd.c')
-rw-r--r-- | linux/drivers/media/video/ngene/ngene-snd.c | 429 |
1 files changed, 429 insertions, 0 deletions
diff --git a/linux/drivers/media/video/ngene/ngene-snd.c b/linux/drivers/media/video/ngene/ngene-snd.c new file mode 100644 index 000000000..28348b4c9 --- /dev/null +++ b/linux/drivers/media/video/ngene/ngene-snd.c @@ -0,0 +1,429 @@ +/* + * 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 = 0; + +/* 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); + + if ((err = snd_pcm_new(chip->card, gro, 0, 0, 1, + &pcm)) < 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++) { + if ((err = snd_ctl_add(card, + snd_ctl_new1(&snd_controls[idx], + chip))) < 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; + } +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,31) + card = snd_card_new(index[sound_dev], id[sound_dev], + THIS_MODULE, sizeof(struct mychip)); + if (card == NULL) + return -ENOMEM; +#else + int ret = snd_card_create(index[sound_dev], id[sound_dev], THIS_MODULE, + sizeof(struct mychip), &card); + if (ret < 0) + return ret; +#endif + + 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); + if ((err = snd_card_register(card)) < 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; +} + |