Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit dae52d00 authored by Matthias Benesch's avatar Matthias Benesch Committed by Mauro Carvalho Chehab
Browse files

V4L/DVB: ngene: Initial check-in



Add Micronas nGene PCIe bridge driver.

The source code was provided by Micronas / Ralph Metzler,
and has been reformatted to comply with Linux Codingstyle.

Signed-off-by: default avatarMatthias Benesch <twoof7@freenet.de>
Signed-off-by: default avatarRalph Metzler <rjkm@metzlerbros.de>
Signed-off-by: default avatarOliver Endriss <o.endriss@gmx.de>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@redhat.com>
parent c22425ff
Loading
Loading
Loading
Loading
+4062 −0

File added.

Preview size limit exceeded, changes collapsed.

+216 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2006-2007 Micronas
 *
 * 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
 */

#ifndef _NGENE_IOCTLS_H_
#define _NGENE_IOCTLS_H_

#include <linux/ioctl.h>
#include <linux/types.h>

#define NGENE_MAGIC 'n'

typedef struct {
	unsigned char       I2CAddress;
	unsigned char       OutLength;      /* bytes to write first */
	unsigned char       InLength;       /* bytes to read */
	unsigned char       OutData[256];   /* output data */
	unsigned char       InData[256];    /* input data */
} MIC_I2C_READ, *PMIC_I2C_READ;

#define IOCTL_MIC_I2C_READ          _IOWR(NGENE_MAGIC, 0x00, MIC_I2C_READ)


typedef struct {
	unsigned char       I2CAddress;
	unsigned char       Length;
	unsigned char       Data[250];
} MIC_I2C_WRITE, *PMIC_I2C_WRITE;

typedef struct {
	unsigned char       Length;
	unsigned char       Data[250];
} MIC_I2C_CONTINUE_WRITE, *PMIC_I2C_CONTINUE_WRITE;

#define IOCTL_MIC_I2C_WRITE                   _IOW(NGENE_MAGIC, 0x01, \
						   MIC_I2C_WRITE)
#define IOCTL_MIC_I2C_WRITE_NOSTOP            _IOW(NGENE_MAGIC, 0x0c, \
						   MIC_I2C_WRITE)
#define IOCTL_MIC_I2C_CONTINUE_WRITE_NOSTOP   _IOW(NGENE_MAGIC, 0x0d, \
						   MIC_I2C_CONTINUE_WRITE)
#define IOCTL_MIC_I2C_CONTINUE_WRITE          _IOW(NGENE_MAGIC, 0x0e, \
						   MIC_I2C_CONTINUE_WRITE)

typedef struct {
	unsigned char       ModeSelect;     /* see bellow */
	unsigned char       OutLength;      /* bytes to write first */
	unsigned char       InLength;       /* bytes to read */
	unsigned char       OutData[250];   /* output data */
} MIC_SPI_READ, *PMIC_SPI_READ;

#define IOCTL_MIC_SPI_READ          _IOWR(NGENE_MAGIC, 0x02, MIC_SPI_READ)

typedef struct {
	unsigned char       ModeSelect;     /* see below */
	unsigned char       Length;
	unsigned char       Data[250];
} MIC_SPI_WRITE, *PMIC_SPI_WRITE;

#define IOCTL_MIC_SPI_WRITE         _IOW(NGENE_MAGIC, 0x03, MIC_SPI_READ)

#define IOCTL_MIC_DOWNLOAD_FIRMWARE _IOW(NGENE_MAGIC, 0x06, unsigned char)

#define IOCTL_MIC_NO_OP             _IO(NGENE_MAGIC, 0x18)

#define IOCTL_MIC_TUN_RDY           _IO(NGENE_MAGIC, 0x07)
#define IOCTL_MIC_DEC_SRATE         _IOW(NGENE_MAGIC, 0x0a, int)
#define IOCTL_MIC_DEC_RDY           _IO(NGENE_MAGIC, 0x09)
#define IOCTL_MIC_DEC_FREESYNC      _IOW(NGENE_MAGIC, 0x08, int)
#define IOCTL_MIC_TUN_DETECT        _IOWR(NGENE_MAGIC, 0x0b, int)

typedef struct {
	unsigned char       Stream; /* < UVI1, UVI2, or TVOUT */
	unsigned char       Control;
	unsigned char       Mode;
	unsigned short      nLines;
	unsigned short      nBytesPerLine;
	unsigned short      nVBILines;
	unsigned short      nBytesPerVBILine;
} MIC_STREAM_CONTROL, *PMIC_STREAM_CONTROL;

enum MIC_STREAM_CONTROL_MODE_BITS {
	MSC_MODE_LOOPBACK         = 0x80,
	MSC_MODE_AVLOOP           = 0x40,
	MSC_MODE_AUDIO_SPDIF      = 0x20,
	MSC_MODE_AVSYNC           = 0x10,
	MSC_MODE_TRANSPORT_STREAM = 0x08,
	MSC_MODE_AUDIO_CAPTURE    = 0x04,
	MSC_MODE_VBI_CAPTURE      = 0x02,
	MSC_MODE_VIDEO_CAPTURE    = 0x01
};

#define IOCTL_MIC_STREAM_CONTROL    _IOW(NGENE_MAGIC, 0x22, MIC_STREAM_CONTROL)

typedef struct {
	unsigned char       Stream; /* < UVI1, UVI2 */
	unsigned int        Rate;   /* < Rate in 100nsec to release the buffers
					 to the stream filters */
} MIC_SIMULATE_CONTROL, *PMIC_SIMULATE_CONTROL;

#define IOCTL_MIC_SIMULATE_CONTROL  _IOW(NGENE_MAGIC, 0x23, \
					 MIC_SIMULATE_CONTROL)

/*
 * IOCTL definitions for the test driver
 *
 *   NOTE: the test driver also supports following IOCTL defined above:
 *      IOCTL_MIC_NO_OP:
 *      IOCTL_MIC_RECEIVE_BUFFER:
 *      IOCTL_MIC_STREAM_CONTROL:
 *      IOCTL_MIC_I2C_READ:
 *      IOCTL_MIC_I2C_WRITE:
 *
 *
 *  VI2C access to NGene memory (read)
 *
 *   GETMEM  in  : ULONG start offset
 *           out : read data (length defined by size of output buffer)
 *   SETMEM  in  : ULONG start offset followed by data to be written
 *                 (length defined by size of input buffer)
 */

typedef struct {
	__u32 Start;
	__u32 Length;
	__u8 *Data;
} MIC_MEM;

#define IOCTL_MIC_TEST_GETMEM       _IOWR(NGENE_MAGIC, 0x90, MIC_MEM)
#define IOCTL_MIC_TEST_SETMEM       _IOW(NGENE_MAGIC, 0x91, MIC_MEM)

typedef struct {
	__u8 Address;
	__u8 Data;
} MIC_IMEM;

#define IOCTL_MIC_SFR_READ          _IOWR(NGENE_MAGIC, 0xa2, MIC_IMEM)
#define IOCTL_MIC_SFR_WRITE         _IOWR(NGENE_MAGIC, 0xa3, MIC_IMEM)

#define IOCTL_MIC_IRAM_READ         _IOWR(NGENE_MAGIC, 0xa4, MIC_IMEM)
#define IOCTL_MIC_IRAM_WRITE        _IOWR(NGENE_MAGIC, 0xa5, MIC_IMEM)

/*
 * Set Ngene gpio bit
 */
typedef struct {
	unsigned char   Select;
	unsigned char   Level;
} MIC_SET_GPIO_PIN, *PMIC_SET_GPIO_PIN;

#define IOCTL_MIC_SET_GPIO_PIN      _IOWR(NGENE_MAGIC, 0xa6, MIC_SET_GPIO_PIN)

/*
 * Uart ioctls:
 *   These are implemented in the test driver.
 *
 * Enable UART
 *
 *   In:  1 byte containing baud rate: 0 = 19200, 1 = 9600, 2 = 4800, 3 = 2400
 *   Out: nothing
 */
#define IOCTL_MIC_UART_ENABLE       _IOW(NGENE_MAGIC,  0xa9, unsigned char)

/*
 * Enable UART
 *
 *   In:  nothing
 *   Out: nothing
 */
#define IOCTL_MIC_UART_DISABLE      _IO(NGENE_MAGIC, 0xAA)

/*
 * Write UART
 *
 *   In:  data to write
 *   Out: nothing
 *   Note: Call returns immediatly, data are send out asynchrounsly
 */
#define IOCTL_MIC_UART_WRITE        _IOW(NGENE_MAGIC, 0xAB, unsigned char)

/*
 * Read UART
 *
 *   In:  nothing
 *   Out: Data read (since last call)
 *   Note: Call returns immediatly
 */
#define IOCTL_MIC_UART_READ         _IOR(NGENE_MAGIC, 0xAC, unsigned char)

/*
 * UART Status
 *
 *   In:  nothing
 *   Out: Byte 0 : Transmitter busy,
 *        Byte 1 : Nbr of characters available for read.
 *   Note: Call returns immediatly
 */
#define IOCTL_MIC_UART_STATUS       _IOR(NGENE_MAGIC, 0xAD, unsigned char)

#endif
+421 −0
Original line number Diff line number Diff line
/*
 * 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;
}
+1937 −0

File added.

Preview size limit exceeded, changes collapsed.

+948 −0

File added.

Preview size limit exceeded, changes collapsed.