cregit-Linux how code gets into the kernel

Release 4.13 sound/oss/vidc.c

Directory: sound/oss
/*
 *  linux/drivers/sound/vidc.c
 *
 *  Copyright (C) 1997-2000 by Russell King <rmk@arm.linux.org.uk>
 *
 * 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.
 *
 *  VIDC20 audio driver.
 *
 * The VIDC20 sound hardware consists of the VIDC20 itself, a DAC and a DMA
 * engine.  The DMA transfers fixed-format (16-bit little-endian linear)
 * samples to the VIDC20, which then transfers this data serially to the
 * DACs.  The samplerate is controlled by the VIDC.
 *
 * We currently support a mixer device, but it is currently non-functional.
 */

#include <linux/gfp.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>

#include <mach/hardware.h>
#include <asm/dma.h>
#include <asm/io.h>
#include <asm/hardware/iomd.h>
#include <asm/irq.h>

#include "sound_config.h"
#include "vidc.h"

#ifndef _SIOC_TYPE

#define _SIOC_TYPE(x)	_IOC_TYPE(x)
#endif
#ifndef _SIOC_NR

#define _SIOC_NR(x)	_IOC_NR(x)
#endif


#define VIDC_SOUND_CLOCK	(250000)

#define VIDC_SOUND_CLOCK_EXT	(176400)

/*
 * When using SERIAL SOUND mode (external DAC), the number of physical
 * channels is fixed at 2.
 */

static int		vidc_busy;

static int		vidc_adev;

static int		vidc_audio_rate;

static char		vidc_audio_format;

static char		vidc_audio_channels;


static unsigned char	vidc_level_l[SOUND_MIXER_NRDEVICES] = {
	85,		/* master       */
	50,		/* bass         */
	50,		/* treble       */
	0,		/* synth        */
	75,		/* pcm          */
	0,		/* speaker      */
	100,		/* ext line     */
	0,		/* mic          */
	100,		/* CD           */
	0,
};


static unsigned char	vidc_level_r[SOUND_MIXER_NRDEVICES] = {
	85,		/* master       */
	50,		/* bass         */
	50,		/* treble       */
	0,		/* synth        */
	75,		/* pcm          */
	0,		/* speaker      */
	100,		/* ext line     */
	0,		/* mic          */
	100,		/* CD           */
	0,
};


static unsigned int	vidc_audio_volume_l;	
/* left PCM vol, 0 - 65536 */

static unsigned int	vidc_audio_volume_r;	
/* right PCM vol, 0 - 65536 */

extern void	vidc_update_filler(int bits, int channels);
extern int	softoss_dev;


static void vidc_mixer_set(int mdev, unsigned int level) { unsigned int lev_l = level & 0x007f; unsigned int lev_r = (level & 0x7f00) >> 8; unsigned int mlev_l, mlev_r; if (lev_l > 100) lev_l = 100; if (lev_r > 100) lev_r = 100; #define SCALE(lev,master) ((lev) * (master) * 65536 / 10000) mlev_l = vidc_level_l[SOUND_MIXER_VOLUME]; mlev_r = vidc_level_r[SOUND_MIXER_VOLUME]; switch (mdev) { case SOUND_MIXER_VOLUME: case SOUND_MIXER_PCM: vidc_level_l[mdev] = lev_l; vidc_level_r[mdev] = lev_r; vidc_audio_volume_l = SCALE(lev_l, mlev_l); vidc_audio_volume_r = SCALE(lev_r, mlev_r); /*printk("VIDC: PCM vol %05X %05X\n", vidc_audio_volume_l, vidc_audio_volume_r);*/ break; } #undef SCALE }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)131100.00%1100.00%
Total131100.00%1100.00%


static int vidc_mixer_ioctl(int dev, unsigned int cmd, void __user *arg) { unsigned int val; unsigned int mdev; if (_SIOC_TYPE(cmd) != 'M') return -EINVAL; mdev = _SIOC_NR(cmd); if (_SIOC_DIR(cmd) & _SIOC_WRITE) { if (get_user(val, (unsigned int __user *)arg)) return -EFAULT; if (mdev < SOUND_MIXER_NRDEVICES) vidc_mixer_set(mdev, val); else return -EINVAL; } /* * Return parameters */ switch (mdev) { case SOUND_MIXER_RECSRC: val = 0; break; case SOUND_MIXER_DEVMASK: val = SOUND_MASK_VOLUME | SOUND_MASK_PCM | SOUND_MASK_SYNTH; break; case SOUND_MIXER_STEREODEVS: val = SOUND_MASK_VOLUME | SOUND_MASK_PCM | SOUND_MASK_SYNTH; break; case SOUND_MIXER_RECMASK: val = 0; break; case SOUND_MIXER_CAPS: val = 0; break; default: if (mdev < SOUND_MIXER_NRDEVICES) val = vidc_level_l[mdev] | vidc_level_r[mdev] << 8; else return -EINVAL; } return put_user(val, (unsigned int __user *)arg) ? -EFAULT : 0; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)18997.42%150.00%
Al Viro52.58%150.00%
Total194100.00%2100.00%


static unsigned int vidc_audio_set_format(int dev, unsigned int fmt) { switch (fmt) { default: fmt = AFMT_S16_LE; case AFMT_U8: case AFMT_S8: case AFMT_S16_LE: vidc_audio_format = fmt; vidc_update_filler(vidc_audio_format, vidc_audio_channels); case AFMT_QUERY: break; } return vidc_audio_format; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)52100.00%1100.00%
Total52100.00%1100.00%

#define my_abs(i) ((i)<0 ? -(i) : (i))
static int vidc_audio_set_speed(int dev, int rate) { if (rate) { unsigned int hwctrl, hwrate, hwrate_ext, rate_int, rate_ext; unsigned int diff_int, diff_ext; unsigned int newsize, new2size; hwctrl = 0x00000003; /* Using internal clock */ hwrate = (((VIDC_SOUND_CLOCK * 2) / rate) + 1) >> 1; if (hwrate < 3) hwrate = 3; if (hwrate > 255) hwrate = 255; /* Using exernal clock */ hwrate_ext = (((VIDC_SOUND_CLOCK_EXT * 2) / rate) + 1) >> 1; if (hwrate_ext < 3) hwrate_ext = 3; if (hwrate_ext > 255) hwrate_ext = 255; rate_int = VIDC_SOUND_CLOCK / hwrate; rate_ext = VIDC_SOUND_CLOCK_EXT / hwrate_ext; /* Chose between external and internal clock */ diff_int = my_abs(rate_ext-rate); diff_ext = my_abs(rate_int-rate); if (diff_ext < diff_int) { /*printk("VIDC: external %d %d %d\n", rate, rate_ext, hwrate_ext);*/ hwrate=hwrate_ext; hwctrl=0x00000002; /* Allow roughly 0.4% tolerance */ if (diff_ext > (rate/256)) rate=rate_ext; } else { /*printk("VIDC: internal %d %d %d\n", rate, rate_int, hwrate);*/ hwctrl=0x00000003; /* Allow roughly 0.4% tolerance */ if (diff_int > (rate/256)) rate=rate_int; } vidc_writel(0xb0000000 | (hwrate - 2)); vidc_writel(0xb1000000 | hwctrl); newsize = (10000 / hwrate) & ~3; if (newsize < 208) newsize = 208; if (newsize > 4096) newsize = 4096; for (new2size = 128; new2size < newsize; new2size <<= 1); if (new2size - newsize > newsize - (new2size >> 1)) new2size >>= 1; if (new2size > 4096) { printk(KERN_ERR "VIDC: error: dma buffer (%d) %d > 4K\n", newsize, new2size); new2size = 4096; } /*printk("VIDC: dma size %d\n", new2size);*/ dma_bufsize = new2size; vidc_audio_rate = rate; } return vidc_audio_rate; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)19258.90%125.00%
Peter Teichmann13140.18%125.00%
Linus Torvalds20.61%125.00%
Lucas De Marchi10.31%125.00%
Total326100.00%4100.00%


static short vidc_audio_set_channels(int dev, short channels) { switch (channels) { default: channels = 2; case 1: case 2: vidc_audio_channels = channels; vidc_update_filler(vidc_audio_format, vidc_audio_channels); case 0: break; } return vidc_audio_channels; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)47100.00%1100.00%
Total47100.00%1100.00%

/* * Open the device */
static int vidc_audio_open(int dev, int mode) { /* This audio device does not have recording capability */ if (mode == OPEN_READ) return -EPERM; if (vidc_busy) return -EBUSY; vidc_busy = 1; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)38100.00%1100.00%
Total38100.00%1100.00%

/* * Close the device */
static void vidc_audio_close(int dev) { vidc_busy = 0; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)13100.00%1100.00%
Total13100.00%1100.00%

/* * Output a block via DMA to sound device. * * We just set the DMA start and count; the DMA interrupt routine * will take care of formatting the samples (via the appropriate * vidc_filler routine), and flag via vidc_audio_dma_interrupt when * more data is required. */
static void vidc_audio_output_block(int dev, unsigned long buf, int total_count, int one) { struct dma_buffparms *dmap = audio_devs[dev]->dmap_out; unsigned long flags; local_irq_save(flags); dma_start = buf - (unsigned long)dmap->raw_buf_phys + (unsigned long)dmap->raw_buf; dma_count = total_count; local_irq_restore(flags); }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)69100.00%2100.00%
Total69100.00%2100.00%


static void vidc_audio_start_input(int dev, unsigned long buf, int count, int intrflag) { }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)18100.00%1100.00%
Total18100.00%1100.00%


static int vidc_audio_prepare_for_input(int dev, int bsize, int bcount) { return -EINVAL; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)19100.00%1100.00%
Total19100.00%1100.00%


static irqreturn_t vidc_audio_dma_interrupt(void) { DMAbuf_outputintr(vidc_adev, 1); return IRQ_HANDLED; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)1477.78%150.00%
Russell King422.22%150.00%
Total18100.00%2100.00%

/* * Prepare for outputting samples. * * Each buffer that will be passed will be `bsize' bytes long, * with a total of `bcount' buffers. */
static int vidc_audio_prepare_for_output(int dev, int bsize, int bcount) { struct audio_operations *adev = audio_devs[dev]; dma_interrupt = NULL; adev->dmap_out->flags |= DMA_NODMA; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)40100.00%1100.00%
Total40100.00%1100.00%

/* * Stop our current operation. */
static void vidc_audio_reset(int dev) { dma_interrupt = NULL; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)13100.00%1100.00%
Total13100.00%1100.00%


static int vidc_audio_local_qlen(int dev) { return /*dma_count !=*/ 0; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)13100.00%1100.00%
Total13100.00%1100.00%


static void vidc_audio_trigger(int dev, int enable_bits) { struct audio_operations *adev = audio_devs[dev]; if (enable_bits & PCM_ENABLE_OUTPUT) { if (!(adev->dmap_out->flags & DMA_ACTIVE)) { unsigned long flags; local_irq_save(flags); /* prevent recusion */ adev->dmap_out->flags |= DMA_ACTIVE; dma_interrupt = vidc_audio_dma_interrupt; vidc_sound_dma_irq(0, NULL); iomd_writeb(DMA_CR_E | 0x10, IOMD_SD0CR); local_irq_restore(flags); } } }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)8394.32%250.00%
Julia Lawall44.55%125.00%
Linus Torvalds11.14%125.00%
Total88100.00%4100.00%

static struct audio_driver vidc_audio_driver = { .owner = THIS_MODULE, .open = vidc_audio_open, .close = vidc_audio_close, .output_block = vidc_audio_output_block, .start_input = vidc_audio_start_input, .prepare_for_input = vidc_audio_prepare_for_input, .prepare_for_output = vidc_audio_prepare_for_output, .halt_io = vidc_audio_reset, .local_qlen = vidc_audio_local_qlen, .trigger = vidc_audio_trigger, .set_speed = vidc_audio_set_speed, .set_bits = vidc_audio_set_format, .set_channels = vidc_audio_set_channels }; static struct mixer_operations vidc_mixer_operations = { .owner = THIS_MODULE, .id = "VIDC", .name = "VIDCsound", .ioctl = vidc_mixer_ioctl };
void vidc_update_filler(int format, int channels) { #define TYPE(fmt,ch) (((fmt)<<2) | ((ch)&3)) switch (TYPE(format, channels)) { default: case TYPE(AFMT_U8, 1): vidc_filler = vidc_fill_1x8_u; break; case TYPE(AFMT_U8, 2): vidc_filler = vidc_fill_2x8_u; break; case TYPE(AFMT_S8, 1): vidc_filler = vidc_fill_1x8_s; break; case TYPE(AFMT_S8, 2): vidc_filler = vidc_fill_2x8_s; break; case TYPE(AFMT_S16_LE, 1): vidc_filler = vidc_fill_1x16_s; break; case TYPE(AFMT_S16_LE, 2): vidc_filler = vidc_fill_2x16_s; break; } }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)110100.00%3100.00%
Total110100.00%3100.00%


static void __init attach_vidc(struct address_info *hw_config) { char name[32]; int i, adev; sprintf(name, "VIDC %d-bit sound", hw_config->card_subtype); conf_printf(name, hw_config); memset(dma_buf, 0, sizeof(dma_buf)); adev = sound_install_audiodrv(AUDIO_DRIVER_VERSION, name, &vidc_audio_driver, sizeof(vidc_audio_driver), DMA_AUTOMODE, AFMT_U8 | AFMT_S8 | AFMT_S16_LE, NULL, hw_config->dma, hw_config->dma2); if (adev < 0) goto audio_failed; /* * 1024 bytes => 64 buffers */ audio_devs[adev]->min_fragment = 10; audio_devs[adev]->mixer_dev = num_mixers; audio_devs[adev]->mixer_dev = sound_install_mixer(MIXER_DRIVER_VERSION, name, &vidc_mixer_operations, sizeof(vidc_mixer_operations), NULL); if (audio_devs[adev]->mixer_dev < 0) goto mixer_failed; for (i = 0; i < 2; i++) { dma_buf[i] = get_zeroed_page(GFP_KERNEL); if (!dma_buf[i]) { printk(KERN_ERR "%s: can't allocate required buffers\n", name); goto mem_failed; } dma_pbuf[i] = virt_to_phys((void *)dma_buf[i]); } if (sound_alloc_dma(hw_config->dma, hw_config->name)) { printk(KERN_ERR "%s: DMA %d is in use\n", name, hw_config->dma); goto dma_failed; } if (request_irq(hw_config->irq, vidc_sound_dma_irq, 0, hw_config->name, &dma_start)) { printk(KERN_ERR "%s: IRQ %d is in use\n", name, hw_config->irq); goto irq_failed; } vidc_adev = adev; vidc_mixer_set(SOUND_MIXER_VOLUME, (85 | 85 << 8)); return; irq_failed: sound_free_dma(hw_config->dma); dma_failed: mem_failed: for (i = 0; i < 2; i++) free_page(dma_buf[i]); sound_unload_mixerdev(audio_devs[adev]->mixer_dev); mixer_failed: sound_unload_audiodev(adev); audio_failed: return; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)35599.72%583.33%
Andrew Morton10.28%116.67%
Total356100.00%6100.00%


static int __init probe_vidc(struct address_info *hw_config) { hw_config->irq = IRQ_DMAS0; hw_config->dma = DMA_VIRTUAL_SOUND; hw_config->dma2 = -1; hw_config->card_subtype = 16; hw_config->name = "VIDC20"; return 1; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)46100.00%3100.00%
Total46100.00%3100.00%


static void __exit unload_vidc(struct address_info *hw_config) { int i, adev = vidc_adev; vidc_adev = -1; free_irq(hw_config->irq, &dma_start); sound_free_dma(hw_config->dma); if (adev >= 0) { sound_unload_mixerdev(audio_devs[adev]->mixer_dev); sound_unload_audiodev(adev); for (i = 0; i < 2; i++) free_page(dma_buf[i]); } }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)85100.00%4100.00%
Total85100.00%4100.00%

static struct address_info cfg;
static int __init init_vidc(void) { if (probe_vidc(&cfg) == 0) return -ENODEV; attach_vidc(&cfg); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)32100.00%2100.00%
Total32100.00%2100.00%


static void __exit cleanup_vidc(void) { unload_vidc(&cfg); }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)15100.00%2100.00%
Total15100.00%2100.00%

module_init(init_vidc); module_exit(cleanup_vidc); MODULE_AUTHOR("Russell King"); MODULE_DESCRIPTION("VIDC20 audio driver"); MODULE_LICENSE("GPL");

Overall Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)183989.32%840.00%
Peter Teichmann1426.90%15.00%
Alan Cox341.65%15.00%
Linus Torvalds221.07%210.00%
Russell King80.39%315.00%
Al Viro50.24%15.00%
Julia Lawall40.19%15.00%
Tejun Heo30.15%15.00%
Andrew Morton10.05%15.00%
Lucas De Marchi10.05%15.00%
Total2059100.00%20100.00%
Directory: sound/oss
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.