Exemplo n.º 1
0
static int mixart_pcm_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
	struct snd_mixart *chip = snd_kcontrol_chip(kcontrol);
	int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); /* index */
	int changed = 0;
	int is_capture = kcontrol->private_value & MIXART_VOL_REC_MASK;
	int is_aes = kcontrol->private_value & MIXART_VOL_AES_MASK;
	int* stored_volume;
	int i;
	mutex_lock(&chip->mgr->mixer_mutex);
	if(is_capture) {
		if(is_aes)	stored_volume = chip->digital_capture_volume[1];	/* AES capture */
		else		stored_volume = chip->digital_capture_volume[0];	/* analog capture */
	} else {
		snd_assert ( idx < MIXART_PLAYBACK_STREAMS ); 
		if(is_aes)	stored_volume = chip->digital_playback_volume[MIXART_PLAYBACK_STREAMS + idx]; /* AES playback */
		else		stored_volume = chip->digital_playback_volume[idx];	/* analog playback */
	}
	for(i=0; i<2; i++) {
		if(stored_volume[i] != ucontrol->value.integer.value[i]) {
			stored_volume[i] = ucontrol->value.integer.value[i];
			changed = 1;
		}
	}
	if(changed) {
		if(is_capture)	mixart_update_capture_stream_level(chip, is_aes);
		else		mixart_update_playback_stream_level(chip, is_aes, idx);
	}
	mutex_unlock(&chip->mgr->mixer_mutex);
	return changed;
}
Exemplo n.º 2
0
static int mixart_pcm_sw_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
	struct snd_mixart *chip = snd_kcontrol_chip(kcontrol);
	int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); /* index */
	snd_assert ( idx < MIXART_PLAYBACK_STREAMS ); 
	mutex_lock(&chip->mgr->mixer_mutex);
	if(kcontrol->private_value & MIXART_VOL_AES_MASK)	/* AES playback */
		idx += MIXART_PLAYBACK_STREAMS;
	ucontrol->value.integer.value[0] = chip->digital_playback_active[idx][0];
	ucontrol->value.integer.value[1] = chip->digital_playback_active[idx][1];
	mutex_unlock(&chip->mgr->mixer_mutex);
	return 0;
}
Exemplo n.º 3
0
static int mixart_pcm_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
	struct snd_mixart *chip = snd_kcontrol_chip(kcontrol);
	int changed = 0;
	int is_aes = kcontrol->private_value & MIXART_VOL_AES_MASK;
	int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); /* index */
	int i, j;
	snd_assert ( idx < MIXART_PLAYBACK_STREAMS ); 
	mutex_lock(&chip->mgr->mixer_mutex);
	j = idx;
	if(is_aes)	j += MIXART_PLAYBACK_STREAMS;
	for(i=0; i<2; i++) {
		if(chip->digital_playback_active[j][i] != ucontrol->value.integer.value[i]) {
			chip->digital_playback_active[j][i] = ucontrol->value.integer.value[i];
			changed = 1;
		}
	}
	if(changed)	mixart_update_playback_stream_level(chip, is_aes, idx);
	mutex_unlock(&chip->mgr->mixer_mutex);
	return changed;
}
Exemplo n.º 4
0
static int mixart_pcm_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
	struct snd_mixart *chip = snd_kcontrol_chip(kcontrol);
	int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); /* index */
	int *stored_volume;
	int is_capture = kcontrol->private_value & MIXART_VOL_REC_MASK;
	int is_aes = kcontrol->private_value & MIXART_VOL_AES_MASK;
	mutex_lock(&chip->mgr->mixer_mutex);
	if(is_capture) {
		if(is_aes)	stored_volume = chip->digital_capture_volume[1];	/* AES capture */
		else		stored_volume = chip->digital_capture_volume[0];	/* analog capture */
	} else {
		snd_assert ( idx < MIXART_PLAYBACK_STREAMS ); 
		if(is_aes)	stored_volume = chip->digital_playback_volume[MIXART_PLAYBACK_STREAMS + idx]; /* AES playback */
		else		stored_volume = chip->digital_playback_volume[idx];	/* analog playback */
	}
	ucontrol->value.integer.value[0] = stored_volume[0];
	ucontrol->value.integer.value[1] = stored_volume[1];
	mutex_unlock(&chip->mgr->mixer_mutex);
	return 0;
}
Exemplo n.º 5
0
static int snd_mixart_capture_open(snd_pcm_substream_t *subs)
{
	mixart_t            *chip = snd_pcm_substream_chip(subs);
	mixart_mgr_t        *mgr = chip->mgr;
	snd_pcm_runtime_t   *runtime = subs->runtime;
	snd_pcm_t           *pcm = subs->pcm;
	mixart_stream_t     *stream;
	mixart_pipe_t       *pipe;
	int err = 0;
	int pcm_number;

	down(&mgr->setup_mutex);

	if ( pcm == chip->pcm ) {
		pcm_number = MIXART_PCM_ANALOG;
		runtime->hw = snd_mixart_analog_caps;
	} else {
		snd_assert ( pcm == chip->pcm_dig ); 
		pcm_number = MIXART_PCM_DIGITAL;
		runtime->hw = snd_mixart_digital_caps;
	}

	runtime->hw.channels_min = 2; /* for instance, no mono */

	snd_printdd("snd_mixart_capture_open C%d/P%d/Sub%d\n", chip->chip_idx, pcm_number, subs->number);

	/* get stream info */
	stream = &(chip->capture_stream[pcm_number]);

	if (stream->status != MIXART_STREAM_STATUS_FREE){
		/* streams in use */
		snd_printk(KERN_ERR "snd_mixart_capture_open C%d/P%d/Sub%d in use\n", chip->chip_idx, pcm_number, subs->number);
		err = -EBUSY;
		goto _exit_open;
	}

	/* get pipe pointer (in pipe) */
	pipe = snd_mixart_add_ref_pipe(chip, pcm_number, 1, 0);

	if (pipe == NULL) {
		err = -EINVAL;
		goto _exit_open;
	}

	/* start the pipe if necessary */
	err = mixart_set_pipe_state(chip->mgr, pipe, 1);
	if( err < 0 ) {
		snd_printk(KERN_ERR "error starting pipe!\n");
		snd_mixart_kill_ref_pipe(chip->mgr, pipe, 0);
		err = -EINVAL;
		goto _exit_open;
	}

	stream->pipe        = pipe;
	stream->pcm_number  = pcm_number;
	stream->status      = MIXART_STREAM_STATUS_OPEN;
	stream->substream   = subs;
	stream->channels    = 0; /* not configured yet */

	runtime->private_data = stream;

	snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32);
	snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 64);

	/* if a sample rate is already used, another stream cannot change */
	if(mgr->ref_count_rate++) {
		if(mgr->sample_rate) {
			runtime->hw.rate_min = runtime->hw.rate_max = mgr->sample_rate;
		}
	}

 _exit_open:
	up(&mgr->setup_mutex);

	return err;
}
Exemplo n.º 6
0
Arquivo: mixart.c Projeto: 274914765/C
int snd_mixart_kill_ref_pipe(struct mixart_mgr *mgr,
                 struct mixart_pipe *pipe, int monitoring)
{
    int err = 0;

    if(pipe->status == PIPE_UNDEFINED)
        return 0;

    if(monitoring)
        pipe->monitoring = 0;
    else
        pipe->references--;

    if((pipe->references <= 0) && (pipe->monitoring == 0)) {

        struct mixart_msg request;
        struct mixart_delete_group_resp delete_resp;

        /* release the clock */
        err = mixart_set_clock( mgr, pipe, 0);
        if( err < 0 ) {
            snd_printk(KERN_ERR "mixart_set_clock(0) return error!\n");
        }

        /* stop the pipe */
        err = mixart_set_pipe_state(mgr, pipe, 0);
        if( err < 0 ) {
            snd_printk(KERN_ERR "error stopping pipe!\n");
        }

        request.message_id = MSG_STREAM_DELETE_GROUP;
        request.uid = (struct mixart_uid){0,0};
        request.data = &pipe->group_uid;            /* the streaming group ! */
        request.size = sizeof(pipe->group_uid);

        /* delete the pipe */
        err = snd_mixart_send_msg(mgr, &request, sizeof(delete_resp), &delete_resp);
        if ((err < 0) || (delete_resp.status != 0)) {
            snd_printk(KERN_ERR "error MSG_STREAM_DELETE_GROUP err(%x), status(%x)\n", err, delete_resp.status);
        }

        pipe->group_uid = (struct mixart_uid){0,0};
        pipe->stream_count = 0;
        pipe->status = PIPE_UNDEFINED;
    }

    return err;
}

static int mixart_set_stream_state(struct mixart_stream *stream, int start)
{
    struct snd_mixart *chip;
    struct mixart_stream_state_req stream_state_req;
    struct mixart_msg request;

    if(!stream->substream)
        return -EINVAL;

    memset(&stream_state_req, 0, sizeof(stream_state_req));
    stream_state_req.stream_count = 1;
    stream_state_req.stream_info.stream_desc.uid_pipe = stream->pipe->group_uid;
    stream_state_req.stream_info.stream_desc.stream_idx = stream->substream->number;

    if (stream->substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
        request.message_id = start ? MSG_STREAM_START_INPUT_STAGE_PACKET : MSG_STREAM_STOP_INPUT_STAGE_PACKET;
    else
        request.message_id = start ? MSG_STREAM_START_OUTPUT_STAGE_PACKET : MSG_STREAM_STOP_OUTPUT_STAGE_PACKET;

    request.uid = (struct mixart_uid){0,0};
    request.data = &stream_state_req;
    request.size = sizeof(stream_state_req);

    stream->abs_period_elapsed = 0;            /* reset stream pos      */
    stream->buf_periods = 0;
    stream->buf_period_frag = 0;

    chip = snd_pcm_substream_chip(stream->substream);

    return snd_mixart_send_msg_nonblock(chip->mgr, &request);
}

/*
 *  Trigger callback
 */

static int snd_mixart_trigger(struct snd_pcm_substream *subs, int cmd)
{
    struct mixart_stream *stream = subs->runtime->private_data;

    switch (cmd) {
    case SNDRV_PCM_TRIGGER_START:

        snd_printdd("SNDRV_PCM_TRIGGER_START\n");

        /* START_STREAM */
        if( mixart_set_stream_state(stream, 1) )
            return -EINVAL;

        stream->status = MIXART_STREAM_STATUS_RUNNING;

        break;
    case SNDRV_PCM_TRIGGER_STOP:

        /* STOP_STREAM */
        if( mixart_set_stream_state(stream, 0) )
            return -EINVAL;

        stream->status = MIXART_STREAM_STATUS_OPEN;

        snd_printdd("SNDRV_PCM_TRIGGER_STOP\n");

        break;

    case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
        /* TODO */
        stream->status = MIXART_STREAM_STATUS_PAUSE;
        snd_printdd("SNDRV_PCM_PAUSE_PUSH\n");
        break;
    case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
        /* TODO */
        stream->status = MIXART_STREAM_STATUS_RUNNING;
        snd_printdd("SNDRV_PCM_PAUSE_RELEASE\n");
        break;
    default:
        return -EINVAL;
    }
    return 0;
}

static int mixart_sync_nonblock_events(struct mixart_mgr *mgr)
{
    unsigned long timeout = jiffies + HZ;
    while (atomic_read(&mgr->msg_processed) > 0) {
        if (time_after(jiffies, timeout)) {
            snd_printk(KERN_ERR "mixart: cannot process nonblock events!\n");
            return -EBUSY;
        }
        schedule_timeout_uninterruptible(1);
    }
    return 0;
}

/*
 *  prepare callback for all pcms
 */
static int snd_mixart_prepare(struct snd_pcm_substream *subs)
{
    struct snd_mixart *chip = snd_pcm_substream_chip(subs);
    struct mixart_stream *stream = subs->runtime->private_data;

    /* TODO de façon non bloquante, réappliquer les hw_params (rate, bits, codec) */

    snd_printdd("snd_mixart_prepare\n");

    mixart_sync_nonblock_events(chip->mgr);

    /* only the first stream can choose the sample rate */
    /* the further opened streams will be limited to its frequency (see open) */
    if(chip->mgr->ref_count_rate == 1)
        chip->mgr->sample_rate = subs->runtime->rate;

    /* set the clock only once (first stream) on the same pipe */
    if(stream->pipe->references == 1) {
        if( mixart_set_clock(chip->mgr, stream->pipe, subs->runtime->rate) )
            return -EINVAL;
    }

    return 0;
}


static int mixart_set_format(struct mixart_stream *stream, snd_pcm_format_t format)
{
    int err;
    struct snd_mixart *chip;
    struct mixart_msg request;
    struct mixart_stream_param_desc stream_param;
    struct mixart_return_uid resp;

    chip = snd_pcm_substream_chip(stream->substream);

    memset(&stream_param, 0, sizeof(stream_param));

    stream_param.coding_type = CT_LINEAR;
    stream_param.number_of_channel = stream->channels;

    stream_param.sampling_freq = chip->mgr->sample_rate;
    if(stream_param.sampling_freq == 0)
        stream_param.sampling_freq = 44100; /* if frequency not yet defined, use some default */

    switch(format){
    case SNDRV_PCM_FORMAT_U8:
        stream_param.sample_type = ST_INTEGER_8;
        stream_param.sample_size = 8;
        break;
    case SNDRV_PCM_FORMAT_S16_LE:
        stream_param.sample_type = ST_INTEGER_16LE;
        stream_param.sample_size = 16;
        break;
    case SNDRV_PCM_FORMAT_S16_BE:
        stream_param.sample_type = ST_INTEGER_16BE;
        stream_param.sample_size = 16;
        break;
    case SNDRV_PCM_FORMAT_S24_3LE:
        stream_param.sample_type = ST_INTEGER_24LE;
        stream_param.sample_size = 24;
        break;
    case SNDRV_PCM_FORMAT_S24_3BE:
        stream_param.sample_type = ST_INTEGER_24BE;
        stream_param.sample_size = 24;
        break;
    case SNDRV_PCM_FORMAT_FLOAT_LE:
        stream_param.sample_type = ST_FLOATING_POINT_32LE;
        stream_param.sample_size = 32;
        break;
    case  SNDRV_PCM_FORMAT_FLOAT_BE:
        stream_param.sample_type = ST_FLOATING_POINT_32BE;
        stream_param.sample_size = 32;
        break;
    default:
        snd_printk(KERN_ERR "error mixart_set_format() : unknown format\n");
        return -EINVAL;
    }

    snd_printdd("set SNDRV_PCM_FORMAT sample_type(%d) sample_size(%d) freq(%d) channels(%d)\n",
           stream_param.sample_type, stream_param.sample_size, stream_param.sampling_freq, stream->channels);

    /* TODO: what else to configure ? */
    /* stream_param.samples_per_frame = 2; */
    /* stream_param.bytes_per_frame = 4; */
    /* stream_param.bytes_per_sample = 2; */

    stream_param.pipe_count = 1;      /* set to 1 */
    stream_param.stream_count = 1;    /* set to 1 */
    stream_param.stream_desc[0].uid_pipe = stream->pipe->group_uid;
    stream_param.stream_desc[0].stream_idx = stream->substream->number;

    request.message_id = MSG_STREAM_SET_INPUT_STAGE_PARAM;
    request.uid = (struct mixart_uid){0,0};
    request.data = &stream_param;
    request.size = sizeof(stream_param);

    err = snd_mixart_send_msg(chip->mgr, &request, sizeof(resp), &resp);
    if((err < 0) || resp.error_code) {
        snd_printk(KERN_ERR "MSG_STREAM_SET_INPUT_STAGE_PARAM err=%x; resp=%x\n", err, resp.error_code);
        return -EINVAL;
    }
    return 0;
}


/*
 *  HW_PARAMS callback for all pcms
 */
static int snd_mixart_hw_params(struct snd_pcm_substream *subs,
                                struct snd_pcm_hw_params *hw)
{
    struct snd_mixart *chip = snd_pcm_substream_chip(subs);
    struct mixart_mgr *mgr = chip->mgr;
    struct mixart_stream *stream = subs->runtime->private_data;
    snd_pcm_format_t format;
    int err;
    int channels;

    /* set up channels */
    channels = params_channels(hw);

    /*  set up format for the stream */
    format = params_format(hw);

    mutex_lock(&mgr->setup_mutex);

    /* update the stream levels */
    if( stream->pcm_number <= MIXART_PCM_DIGITAL ) {
        int is_aes = stream->pcm_number > MIXART_PCM_ANALOG;
        if( subs->stream == SNDRV_PCM_STREAM_PLAYBACK )
            mixart_update_playback_stream_level(chip, is_aes, subs->number);
        else
            mixart_update_capture_stream_level( chip, is_aes);
    }

    stream->channels = channels;

    /* set the format to the board */
    err = mixart_set_format(stream, format);
    if(err < 0) {
        return err;
    }

    /* allocate buffer */
    err = snd_pcm_lib_malloc_pages(subs, params_buffer_bytes(hw));

    if (err > 0) {
        struct mixart_bufferinfo *bufferinfo;
        int i = (chip->chip_idx * MIXART_MAX_STREAM_PER_CARD) + (stream->pcm_number * (MIXART_PLAYBACK_STREAMS+MIXART_CAPTURE_STREAMS)) + subs->number;
        if( subs->stream == SNDRV_PCM_STREAM_CAPTURE ) {
            i += MIXART_PLAYBACK_STREAMS; /* in array capture is behind playback */
        }
        
        bufferinfo = (struct mixart_bufferinfo *)chip->mgr->bufferinfo.area;
        bufferinfo[i].buffer_address = subs->runtime->dma_addr;
        bufferinfo[i].available_length = subs->runtime->dma_bytes;
        /* bufferinfo[i].buffer_id  is already defined */

        snd_printdd("snd_mixart_hw_params(pcm %d) : dma_addr(%x) dma_bytes(%x) subs-number(%d)\n", i,
                bufferinfo[i].buffer_address,
                bufferinfo[i].available_length,
                subs->number);
    }
    mutex_unlock(&mgr->setup_mutex);

    return err;
}

static int snd_mixart_hw_free(struct snd_pcm_substream *subs)
{
    struct snd_mixart *chip = snd_pcm_substream_chip(subs);
    snd_pcm_lib_free_pages(subs);
    mixart_sync_nonblock_events(chip->mgr);
    return 0;
}



/*
 *  TODO CONFIGURATION SPACE for all pcms, mono pcm must update channels_max
 */
static struct snd_pcm_hardware snd_mixart_analog_caps =
{
    .info             = ( SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
                  SNDRV_PCM_INFO_MMAP_VALID |
                  SNDRV_PCM_INFO_PAUSE),
    .formats      = ( SNDRV_PCM_FMTBIT_U8 |
                  SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |
                  SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE |
                  SNDRV_PCM_FMTBIT_FLOAT_LE | SNDRV_PCM_FMTBIT_FLOAT_BE ),
    .rates            = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
    .rate_min         = 8000,
    .rate_max         = 48000,
    .channels_min     = 1,
    .channels_max     = 2,
    .buffer_bytes_max = (32*1024),
    .period_bytes_min = 256,                  /* 256 frames U8 mono*/
    .period_bytes_max = (16*1024),
    .periods_min      = 2,
    .periods_max      = (32*1024/256),
};

static struct snd_pcm_hardware snd_mixart_digital_caps =
{
    .info             = ( SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
                  SNDRV_PCM_INFO_MMAP_VALID |
                  SNDRV_PCM_INFO_PAUSE),
    .formats      = ( SNDRV_PCM_FMTBIT_U8 |
                  SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |
                  SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE |
                  SNDRV_PCM_FMTBIT_FLOAT_LE | SNDRV_PCM_FMTBIT_FLOAT_BE ),
    .rates            = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
    .rate_min         = 32000,
    .rate_max         = 48000,
    .channels_min     = 1,
    .channels_max     = 2,
    .buffer_bytes_max = (32*1024),
    .period_bytes_min = 256,                  /* 256 frames U8 mono*/
    .period_bytes_max = (16*1024),
    .periods_min      = 2,
    .periods_max      = (32*1024/256),
};


static int snd_mixart_playback_open(struct snd_pcm_substream *subs)
{
    struct snd_mixart            *chip = snd_pcm_substream_chip(subs);
    struct mixart_mgr        *mgr = chip->mgr;
    struct snd_pcm_runtime *runtime = subs->runtime;
    struct snd_pcm *pcm = subs->pcm;
    struct mixart_stream     *stream;
    struct mixart_pipe       *pipe;
    int err = 0;
    int pcm_number;

    mutex_lock(&mgr->setup_mutex);

    if ( pcm == chip->pcm ) {
        pcm_number = MIXART_PCM_ANALOG;
        runtime->hw = snd_mixart_analog_caps;
    } else {
        snd_assert ( pcm == chip->pcm_dig ); 
        pcm_number = MIXART_PCM_DIGITAL;
        runtime->hw = snd_mixart_digital_caps;
    }
    snd_printdd("snd_mixart_playback_open C%d/P%d/Sub%d\n", chip->chip_idx, pcm_number, subs->number);

    /* get stream info */
    stream = &(chip->playback_stream[pcm_number][subs->number]);

    if (stream->status != MIXART_STREAM_STATUS_FREE){
        /* streams in use */
        snd_printk(KERN_ERR "snd_mixart_playback_open C%d/P%d/Sub%d in use\n", chip->chip_idx, pcm_number, subs->number);
        err = -EBUSY;
        goto _exit_open;
    }

    /* get pipe pointer (out pipe) */
    pipe = snd_mixart_add_ref_pipe(chip, pcm_number, 0, 0);

    if (pipe == NULL) {
        err = -EINVAL;
        goto _exit_open;
    }

    /* start the pipe if necessary */
    err = mixart_set_pipe_state(chip->mgr, pipe, 1);
    if( err < 0 ) {
        snd_printk(KERN_ERR "error starting pipe!\n");
        snd_mixart_kill_ref_pipe(chip->mgr, pipe, 0);
        err = -EINVAL;
        goto _exit_open;
    }

    stream->pipe        = pipe;
    stream->pcm_number  = pcm_number;
    stream->status      = MIXART_STREAM_STATUS_OPEN;
    stream->substream   = subs;
    stream->channels    = 0; /* not configured yet */

    runtime->private_data = stream;

    snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32);
    snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 64);

    /* if a sample rate is already used, another stream cannot change */
    if(mgr->ref_count_rate++) {
        if(mgr->sample_rate) {
            runtime->hw.rate_min = runtime->hw.rate_max = mgr->sample_rate;
        }
    }

 _exit_open:
    mutex_unlock(&mgr->setup_mutex);

    return err;
}


static int snd_mixart_capture_open(struct snd_pcm_substream *subs)
{
    struct snd_mixart            *chip = snd_pcm_substream_chip(subs);
    struct mixart_mgr        *mgr = chip->mgr;
    struct snd_pcm_runtime *runtime = subs->runtime;
    struct snd_pcm *pcm = subs->pcm;
    struct mixart_stream     *stream;
    struct mixart_pipe       *pipe;
    int err = 0;
    int pcm_number;

    mutex_lock(&mgr->setup_mutex);

    if ( pcm == chip->pcm ) {
        pcm_number = MIXART_PCM_ANALOG;
        runtime->hw = snd_mixart_analog_caps;
    } else {
        snd_assert ( pcm == chip->pcm_dig ); 
        pcm_number = MIXART_PCM_DIGITAL;
        runtime->hw = snd_mixart_digital_caps;
    }

    runtime->hw.channels_min = 2; /* for instance, no mono */

    snd_printdd("snd_mixart_capture_open C%d/P%d/Sub%d\n", chip->chip_idx, pcm_number, subs->number);

    /* get stream info */
    stream = &(chip->capture_stream[pcm_number]);

    if (stream->status != MIXART_STREAM_STATUS_FREE){
        /* streams in use */
        snd_printk(KERN_ERR "snd_mixart_capture_open C%d/P%d/Sub%d in use\n", chip->chip_idx, pcm_number, subs->number);
        err = -EBUSY;
        goto _exit_open;
    }

    /* get pipe pointer (in pipe) */
    pipe = snd_mixart_add_ref_pipe(chip, pcm_number, 1, 0);

    if (pipe == NULL) {
        err = -EINVAL;
        goto _exit_open;
    }

    /* start the pipe if necessary */
    err = mixart_set_pipe_state(chip->mgr, pipe, 1);
    if( err < 0 ) {
        snd_printk(KERN_ERR "error starting pipe!\n");
        snd_mixart_kill_ref_pipe(chip->mgr, pipe, 0);
        err = -EINVAL;
        goto _exit_open;
    }

    stream->pipe        = pipe;
    stream->pcm_number  = pcm_number;
    stream->status      = MIXART_STREAM_STATUS_OPEN;
    stream->substream   = subs;
    stream->channels    = 0; /* not configured yet */

    runtime->private_data = stream;

    snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32);
    snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 64);

    /* if a sample rate is already used, another stream cannot change */
    if(mgr->ref_count_rate++) {
        if(mgr->sample_rate) {
            runtime->hw.rate_min = runtime->hw.rate_max = mgr->sample_rate;
        }
    }

 _exit_open:
    mutex_unlock(&mgr->setup_mutex);

    return err;
}



static int snd_mixart_close(struct snd_pcm_substream *subs)
{
    struct snd_mixart *chip = snd_pcm_substream_chip(subs);
    struct mixart_mgr *mgr = chip->mgr;
    struct mixart_stream *stream = subs->runtime->private_data;

    mutex_lock(&mgr->setup_mutex);

    snd_printdd("snd_mixart_close C%d/P%d/Sub%d\n", chip->chip_idx, stream->pcm_number, subs->number);

    /* sample rate released */
    if(--mgr->ref_count_rate == 0) {
        mgr->sample_rate = 0;
    }

    /* delete pipe */
    if (snd_mixart_kill_ref_pipe(mgr, stream->pipe, 0 ) < 0) {

        snd_printk(KERN_ERR "error snd_mixart_kill_ref_pipe C%dP%d\n", chip->chip_idx, stream->pcm_number);
    }

    stream->pipe      = NULL;
    stream->status    = MIXART_STREAM_STATUS_FREE;
    stream->substream = NULL;

    mutex_unlock(&mgr->setup_mutex);
    return 0;
}


static snd_pcm_uframes_t snd_mixart_stream_pointer(struct snd_pcm_substream *subs)
{
    struct snd_pcm_runtime *runtime = subs->runtime;
    struct mixart_stream   *stream  = runtime->private_data;

    return (snd_pcm_uframes_t)((stream->buf_periods * runtime->period_size) + stream->buf_period_frag);
}



static struct snd_pcm_ops snd_mixart_playback_ops = {
    .open      = snd_mixart_playback_open,
    .close     = snd_mixart_close,
    .ioctl     = snd_pcm_lib_ioctl,
    .prepare   = snd_mixart_prepare,
    .hw_params = snd_mixart_hw_params,
    .hw_free   = snd_mixart_hw_free,
    .trigger   = snd_mixart_trigger,
    .pointer   = snd_mixart_stream_pointer,
};

static struct snd_pcm_ops snd_mixart_capture_ops = {
    .open      = snd_mixart_capture_open,
    .close     = snd_mixart_close,
    .ioctl     = snd_pcm_lib_ioctl,
    .prepare   = snd_mixart_prepare,
    .hw_params = snd_mixart_hw_params,
    .hw_free   = snd_mixart_hw_free,
    .trigger   = snd_mixart_trigger,
    .pointer   = snd_mixart_stream_pointer,
};

static void preallocate_buffers(struct snd_mixart *chip, struct snd_pcm *pcm)
{
#if 0
    struct snd_pcm_substream *subs;
    int stream;

    for (stream = 0; stream < 2; stream++) {
        int idx = 0;
        for (subs = pcm->streams[stream].substream; subs; subs = subs->next, idx++)
            /* set up the unique device id with the chip index */
            subs->dma_device.id = subs->pcm->device << 16 |
                subs->stream << 8 | (subs->number + 1) |
                (chip->chip_idx + 1) << 24;
    }
#endif
    snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
                          snd_dma_pci_data(chip->mgr->pci), 32*1024, 32*1024);
}

/*
 */
static int snd_mixart_pcm_analog(struct snd_mixart *chip)
{
    int err;
    struct snd_pcm *pcm;
    char name[32];

    sprintf(name, "miXart analog %d", chip->chip_idx);
    if ((err = snd_pcm_new(chip->card, name, MIXART_PCM_ANALOG,
                   MIXART_PLAYBACK_STREAMS,
                   MIXART_CAPTURE_STREAMS, &pcm)) < 0) {
        snd_printk(KERN_ERR "cannot create the analog pcm %d\n", chip->chip_idx);
        return err;
    }

    pcm->private_data = chip;

    snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_mixart_playback_ops);
    snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_mixart_capture_ops);

    pcm->info_flags = 0;
    strcpy(pcm->name, name);

    preallocate_buffers(chip, pcm);

    chip->pcm = pcm;
    return 0;
}


/*
 */
static int snd_mixart_pcm_digital(struct snd_mixart *chip)
{
    int err;
    struct snd_pcm *pcm;
    char name[32];

    sprintf(name, "miXart AES/EBU %d", chip->chip_idx);
    if ((err = snd_pcm_new(chip->card, name, MIXART_PCM_DIGITAL,
                   MIXART_PLAYBACK_STREAMS,
                   MIXART_CAPTURE_STREAMS, &pcm)) < 0) {
        snd_printk(KERN_ERR "cannot create the digital pcm %d\n", chip->chip_idx);
        return err;
    }

    pcm->private_data = chip;

    snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_mixart_playback_ops);
    snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_mixart_capture_ops);

    pcm->info_flags = 0;
    strcpy(pcm->name, name);

    preallocate_buffers(chip, pcm);

    chip->pcm_dig = pcm;
    return 0;
}

static int snd_mixart_chip_free(struct snd_mixart *chip)
{
    kfree(chip);
    return 0;
}

static int snd_mixart_chip_dev_free(struct snd_device *device)
{
    struct snd_mixart *chip = device->device_data;
    return snd_mixart_chip_free(chip);
}


/*
 */
static int __devinit snd_mixart_create(struct mixart_mgr *mgr, struct snd_card *card, int idx)
{
    int err;
    struct snd_mixart *chip;
    static struct snd_device_ops ops = {
        .dev_free = snd_mixart_chip_dev_free,
    };

    mgr->chip[idx] = chip = kzalloc(sizeof(*chip), GFP_KERNEL);
    if (! chip) {
        snd_printk(KERN_ERR "cannot allocate chip\n");
        return -ENOMEM;
    }

    chip->card = card;
    chip->chip_idx = idx;
    chip->mgr = mgr;

    if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
        snd_mixart_chip_free(chip);
        return err;
    }

    snd_card_set_dev(card, &mgr->pci->dev);

    return 0;
}