aboutsummaryrefslogtreecommitdiff
path: root/drivers/media/dvb/ngene/ngene-snd.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/dvb/ngene/ngene-snd.c')
-rw-r--r--drivers/media/dvb/ngene/ngene-snd.c421
1 files changed, 421 insertions, 0 deletions
diff --git a/drivers/media/dvb/ngene/ngene-snd.c b/drivers/media/dvb/ngene/ngene-snd.c
new file mode 100644
index 00000000000..1ca343236ff
--- /dev/null
+++ b/drivers/media/dvb/ngene/ngene-snd.c
@@ -0,0 +1,421 @@
+/*
+ * 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;
+}