/*
 * Setup FDMA transfer. Add 'oob' to list, if present.  Assumes FDMA channel
 * has been initialised, and data areas are suitably aligned.
 */
static int nand_write_dma(struct mtd_info *mtd, const uint8_t *buf, int buf_len,
			  uint8_t *oob, int oob_len)
{
	struct nand_chip *chip = mtd->priv;
	struct stm_nand_emi *data = chip->priv;
	unsigned long	nand_dma;
	dma_addr_t	buf_dma;
	dma_addr_t	oob_dma;
	unsigned long res = 0;

	/* Check channel is ready for use */
	if (dma_get_status(data->dma_chan) != DMA_CHANNEL_STATUS_IDLE) {
		printk(KERN_ERR NAME ": requested channel not idle\n");
		return 1;
	}

	/* Set up and map DMA addresses */
	nand_dma = data->nand_phys_addr;
	buf_dma = dma_map_single(NULL, buf, buf_len, DMA_TO_DEVICE);
	dma_params_addrs(&data->dma_params[0], buf_dma, nand_dma, buf_len);

	/* Are we doing data+oob linked transfer? */
	if (oob) {
		oob_dma = dma_map_single(NULL, oob, oob_len, DMA_TO_DEVICE);
		dma_params_link(&data->dma_params[0], &data->dma_params[1]);
		dma_params_addrs(&data->dma_params[1], oob_dma,
				 nand_dma, oob_len);
	} else {
		data->dma_params[0].next = NULL;
	}

	/* Compile transfer list */
	res = dma_compile_list(data->dma_chan, &data->dma_params[0],
			       GFP_ATOMIC);
	if (res != 0) {
		printk(KERN_ERR NAME
		       ": DMA compile list failed (err_code = %ld)\n", res);
		return 1;
	}

	/* Initiate transfer */
	res = dma_xfer_list(data->dma_chan, &data->dma_params[0]);
	if (res != 0) {
		printk(KERN_ERR NAME
		       ": transfer failed (err_code = %ld)\n", res);
		return 1;
	}

	/* Wait for completion... */
	dma_wait_for_completion(data->dma_chan);

	/* Unmap DMA memory */
	dma_unmap_single(NULL, buf_dma, buf_len, DMA_TO_DEVICE);
	if (oob)
		dma_unmap_single(NULL, oob_dma, oob_len, DMA_TO_DEVICE);

	return 0;
}
Example #2
0
// Callback called each time the ALSA capture gets a data period
static void period_captured_callback(struct snd_pcm_substream *substream)
{
    int result;
    struct ksnd_pcm_streaming *streaming = substream->runtime->private_data;
    const snd_pcm_channel_area_t *capture_areas;
    snd_pcm_uframes_t capture_frames;
    const snd_pcm_channel_area_t *playback_areas;
    void *dest, *src;
    ssize_t size;

    BUG_ON(streaming->magic != magic_good);

    // Check if the transfer wasn't stopped by any chance...

    if (test_bit(FLAG_STOPPED, &streaming->flags))
        return;

    // Check if there is no transfers in progress...

    if (test_and_set_bit(FLAG_BUSY, &streaming->flags) != 0)
        return;

    // Get number of available periods (modulo period size)

    capture_frames = ksnd_pcm_avail_update(streaming->capture_handle);
    capture_frames -= capture_frames % substream->runtime->period_size;

    // "Mmap" captured data

    result = ksnd_pcm_mmap_begin(streaming->capture_handle,
                                 &capture_areas, &streaming->capture_offset, &capture_frames);
    if (result < 0) {
        ERROR("Failed to mmap capture buffer!\n");
        return;
    }

    // "Mmap" playback buffer

    streaming->playback_frames = capture_frames;

    result = ksnd_pcm_mmap_begin(streaming->playback_handle,
                                 &playback_areas, &streaming->playback_offset, &streaming->playback_frames);
    if (result < 0) {
        ERROR("Failed to mmap playback buffer\n");
        return;
    }

    // Setup FDMA transfer

    src = capture_areas[0].addr + capture_areas[0].first / 8 +
          streaming->capture_offset * capture_areas[0].step / 8;
    dest = playback_areas[0].addr + playback_areas[0].first / 8 +
           streaming->playback_offset * playback_areas[0].step / 8;
    size = frames_to_bytes(substream->runtime, streaming->playback_frames);

    dma_params_addrs(&streaming->fdma_params, virt_to_phys(src), virt_to_phys(dest), size);

    result = dma_compile_list(streaming->fdma_channel, &streaming->fdma_params, GFP_KERNEL);
    if (result < 0) {
        ERROR("Can't compile FDMA parameters!\n");
        free_dma(streaming->fdma_channel);
        return;
    }

    // Launch the transfer

    result = dma_xfer_list(streaming->fdma_channel, &streaming->fdma_params);
    if (result < 0) {
        ERROR("Failed to launch FDMA tranfser!\n");
        return;
    }
}
Example #3
0
int ksnd_pcm_streaming_start(ksnd_pcm_streaming_t *handle,
                             ksnd_pcm_t *capture, ksnd_pcm_t *playback)
{
    int result;
    struct ksnd_pcm_streaming *streaming;
    const char *fdmac_id[] = { STM_DMAC_ID, NULL };
    const char *lb_cap[] = { STM_DMA_CAP_LOW_BW, NULL };
    const char *hb_cap[] = { STM_DMA_CAP_HIGH_BW, NULL };

    // Allocate and clean streaming structure

    streaming = kzalloc(sizeof(*streaming), GFP_KERNEL);
    if (streaming == NULL) {
        ERROR("Can't get memory for streaming structure!\n");
        return -ENOMEM;
    }

    // Prepare description

    streaming->capture_handle = capture;
    streaming->playback_handle = playback;
    streaming->magic = magic_good;

    // Initialize FDMA

    streaming->fdma_channel = request_dma_bycap(fdmac_id, lb_cap, "KSOUND_STREAMING");
    if (streaming->fdma_channel < 0) {
        streaming->fdma_channel = request_dma_bycap(fdmac_id, hb_cap, "KSOUND_STREAMING");
        if (streaming->fdma_channel < 0) {
            ERROR("Can't allocate FDMA channel!\n");
            kfree(streaming);
            return -EBUSY;
        }
    }

    dma_params_init(&streaming->fdma_params, MODE_FREERUNNING, STM_DMA_LIST_OPEN);

    dma_params_comp_cb(&streaming->fdma_params, transfer_done_callback,
                       (unsigned long)streaming, STM_DMA_CB_CONTEXT_TASKLET);

    dma_params_err_cb(&streaming->fdma_params, transfer_error_callback,
                      (unsigned long)streaming, STM_DMA_CB_CONTEXT_TASKLET);

    dma_params_DIM_1_x_1(&streaming->fdma_params);

    result = dma_compile_list(streaming->fdma_channel, &streaming->fdma_params, GFP_KERNEL);
    if (result < 0) {
        ERROR("Can't compile FDMA parameters!\n");
        free_dma(streaming->fdma_channel);
        kfree(streaming);
        return -EFAULT;
    }

    // Initialize ALSA

    capture->substream->runtime->transfer_ack_end = period_captured_callback;
    BUG_ON(capture->substream->runtime->private_data != NULL);  // It used to be not used ;-)
    capture->substream->runtime->private_data = streaming;

    ksnd_pcm_start(capture);

    // Return handle

    *handle = streaming;

    return 0;
}
Example #4
0
static int snd_stm_pcm_player_hw_params(struct snd_pcm_substream *substream,
		struct snd_pcm_hw_params *hw_params)
{
	int result;
	struct snd_stm_pcm_player *pcm_player =
			snd_pcm_substream_chip(substream);
	struct snd_pcm_runtime *runtime = substream->runtime;
	int buffer_bytes, frame_bytes, transfer_bytes;
	unsigned int transfer_size;
	struct stm_dma_req_config fdma_req_config = {
		.rw        = REQ_CONFIG_WRITE,
		.opcode    = REQ_CONFIG_OPCODE_4,
		.increment = 0,
		.hold_off  = 0,
		.initiator = pcm_player->info->fdma_initiator,
	};

	snd_stm_printd(1, "snd_stm_pcm_player_hw_params(substream=0x%p,"
			" hw_params=0x%p)\n", substream, hw_params);

	BUG_ON(!pcm_player);
	BUG_ON(!snd_stm_magic_valid(pcm_player));
	BUG_ON(!runtime);

	/* This function may be called many times, so let's be prepared... */
	if (snd_stm_buffer_is_allocated(pcm_player->buffer))
		snd_stm_pcm_player_hw_free(substream);

	/* Allocate buffer */

	buffer_bytes = params_buffer_bytes(hw_params);
	result = snd_stm_buffer_alloc(pcm_player->buffer, substream,
			buffer_bytes);
	if (result != 0) {
		snd_stm_printe("Can't allocate %d bytes buffer for '%s'!\n",
			       buffer_bytes, dev_name(pcm_player->device));
		result = -ENOMEM;
		goto error_buf_alloc;
	}

	/* Set FDMA transfer size (number of opcodes generated
	 * after request line assertion) */

	frame_bytes = snd_pcm_format_physical_width(params_format(hw_params)) *
			params_channels(hw_params) / 8;
	transfer_bytes = snd_stm_pcm_transfer_bytes(frame_bytes,
			pcm_player->fdma_max_transfer_size * 4);
	transfer_size = transfer_bytes / 4;
	snd_stm_printd(1, "FDMA request trigger limit and transfer size set "
			"to %d.\n", transfer_size);

	BUG_ON(buffer_bytes % transfer_bytes != 0);
	BUG_ON(transfer_size > pcm_player->fdma_max_transfer_size);
	fdma_req_config.count = transfer_size;

	BUG_ON(transfer_size != 1 && transfer_size % 2 != 0);
	BUG_ON(transfer_size >
	       mask__AUD_PCMOUT_FMT__DMA_REQ_TRIG_LMT(pcm_player));
	set__AUD_PCMOUT_FMT__DMA_REQ_TRIG_LMT(pcm_player, transfer_size);

	/* Configure FDMA transfer */

	pcm_player->fdma_request = dma_req_config(pcm_player->fdma_channel,
			pcm_player->info->fdma_request_line, &fdma_req_config);
	if (!pcm_player->fdma_request) {
		snd_stm_printe("Can't configure FDMA pacing channel for player"
			       " '%s'!\n", dev_name(pcm_player->device));
		result = -EINVAL;
		goto error_req_config;
	}

	dma_params_init(&pcm_player->fdma_params, MODE_PACED,
			STM_DMA_LIST_CIRC);

	dma_params_DIM_1_x_0(&pcm_player->fdma_params);

	dma_params_req(&pcm_player->fdma_params, pcm_player->fdma_request);

	dma_params_addrs(&pcm_player->fdma_params, runtime->dma_addr,
			pcm_player->fifo_phys_address, buffer_bytes);

	result = dma_compile_list(pcm_player->fdma_channel,
				&pcm_player->fdma_params, GFP_KERNEL);
	if (result < 0) {
		snd_stm_printe("Can't compile FDMA parameters for player"
			       " '%s'!\n", dev_name(pcm_player->device));
		goto error_compile_list;
	}

	return 0;

error_compile_list:
	dma_req_free(pcm_player->fdma_channel,
			pcm_player->fdma_request);
error_req_config:
	snd_stm_buffer_free(pcm_player->buffer);
error_buf_alloc:
	return result;
}

static int snd_stm_pcm_player_prepare(struct snd_pcm_substream *substream)
{
	struct snd_stm_pcm_player *pcm_player =
			snd_pcm_substream_chip(substream);
	struct snd_pcm_runtime *runtime = substream->runtime;
	unsigned int format, lr_pol;
	int oversampling, bits_in_output_frame;
	int result;

	snd_stm_printd(1, "snd_stm_pcm_player_prepare(substream=0x%p)\n",
			substream);

	BUG_ON(!pcm_player);
	BUG_ON(!snd_stm_magic_valid(pcm_player));
	BUG_ON(!runtime);
	BUG_ON(runtime->period_size * runtime->channels >=
	       MAX_SAMPLES_PER_PERIOD);

	/* Configure SPDIF synchronisation */

	/* TODO */

	/* Get format & oversampling value from connected converter */

	if (pcm_player->conv_group) {
		format = snd_stm_conv_get_format(pcm_player->conv_group);
		oversampling = snd_stm_conv_get_oversampling(
				pcm_player->conv_group);
		if (oversampling == 0)
			oversampling = DEFAULT_OVERSAMPLING;
	} else {
		format = DEFAULT_FORMAT;
		oversampling = DEFAULT_OVERSAMPLING;
	}

	snd_stm_printd(1, "Player %s: sampling frequency %d, oversampling %d\n",
			dev_name(pcm_player->device), runtime->rate,
			oversampling);

	BUG_ON(oversampling < 0);

	/* For 32 bits subframe oversampling must be a multiple of 128,
	 * for 16 bits - of 64 */
	BUG_ON((format & SND_STM_FORMAT__SUBFRAME_32_BITS) &&
		(oversampling % 128 != 0));
	BUG_ON(!(format & SND_STM_FORMAT__SUBFRAME_16_BITS) &&
		(oversampling % 64 != 0));

	/* Set up frequency synthesizer */

	result = clk_enable(pcm_player->clock);
	if (result != 0) {
		snd_stm_printe("Can't enable clock for player '%s'!\n",
				dev_name(pcm_player->device));
		return result;
	}

	result = clk_set_rate(pcm_player->clock,
				runtime->rate * oversampling);
	if (result != 0) {
		snd_stm_printe("Can't configure clock for player '%s'!\n",
				dev_name(pcm_player->device));
		clk_disable(pcm_player->clock);
		return result;
	}

	/* Set up player hardware */

	snd_stm_printd(1, "Player %s format configuration:\n",
			dev_name(pcm_player->device));

	/* Number of bits per subframe (which is one channel sample)
	 * on output - it determines serial clock frequency, which is
	 * 64 times sampling rate for 32 bits subframe (2 channels 32
	 * bits each means 64 bits per frame) and 32 times sampling
	 * rate for 16 bits subframe
	 * (you know why, don't you? :-) */

	switch (format & SND_STM_FORMAT__SUBFRAME_MASK) {
	case SND_STM_FORMAT__SUBFRAME_32_BITS:
		snd_stm_printd(1, "- 32 bits per subframe\n");
		set__AUD_PCMOUT_FMT__NBIT__32_BITS(pcm_player);
		if (pcm_player->ver > 5)
			set__AUD_PCMOUT_FMT__DATA_SIZE__32_BITS(pcm_player);
		else
			set__AUD_PCMOUT_FMT__DATA_SIZE__24_BITS(pcm_player);
		bits_in_output_frame = 64; /* frame = 2 * subframe */
		break;
	case SND_STM_FORMAT__SUBFRAME_16_BITS:
		snd_stm_printd(1, "- 16 bits per subframe\n");
		set__AUD_PCMOUT_FMT__NBIT__16_BITS(pcm_player);
		set__AUD_PCMOUT_FMT__DATA_SIZE__16_BITS(pcm_player);
		bits_in_output_frame = 32; /* frame = 2 * subframe */
		break;
	default:
		snd_BUG();
		return -EINVAL;
	}

	/* Serial audio interface format - for detailed explanation
	 * see ie.:
	 * http://www.cirrus.com/en/pubs/appNote/AN282REV1.pdf */

	set__AUD_PCMOUT_FMT__ORDER__MSB_FIRST(pcm_player);

	/* Value FALLING of SCLK_EDGE bit in AUD_PCMOUT_FMT register that
	 * actually means "data clocking (changing) on the falling edge"
	 * (and we usually want this...) - STx7100 and cuts < 3.0 of
	 * STx7109 have this bit inverted comparing to what their
	 * datasheets claim... (specs say 1) */

	set__AUD_PCMOUT_FMT__SCLK_EDGE__FALLING(pcm_player);

	switch (format & SND_STM_FORMAT__MASK) {
	case SND_STM_FORMAT__I2S:
		snd_stm_printd(1, "- I2S\n");
		set__AUD_PCMOUT_FMT__ALIGN__LEFT(pcm_player);
		set__AUD_PCMOUT_FMT__PADDING__1_CYCLE_DELAY(pcm_player);
		lr_pol = value__AUD_PCMOUT_FMT__LR_POL__LEFT_LOW(pcm_player);
		break;
	case SND_STM_FORMAT__LEFT_JUSTIFIED:
		snd_stm_printd(1, "- left justified\n");
		set__AUD_PCMOUT_FMT__ALIGN__LEFT(pcm_player);
		set__AUD_PCMOUT_FMT__PADDING__NO_DELAY(pcm_player);
		lr_pol = value__AUD_PCMOUT_FMT__LR_POL__LEFT_HIGH(pcm_player);
		break;
	case SND_STM_FORMAT__RIGHT_JUSTIFIED:
		snd_stm_printd(1, "- right justified\n");
		set__AUD_PCMOUT_FMT__ALIGN__RIGHT(pcm_player);
		set__AUD_PCMOUT_FMT__PADDING__NO_DELAY(pcm_player);
		lr_pol = value__AUD_PCMOUT_FMT__LR_POL__LEFT_HIGH(pcm_player);
		break;
	default:
		snd_BUG();
		return -EINVAL;
	}

	/* Configure PCM player frequency divider
	 *
	 *             Fdacclk             Fs * oversampling
	 * divider = ----------- = ------------------------------- =
	 *            2 * Fsclk     2 * Fs * bits_in_output_frame
	 *
	 *                  oversampling
	 *         = --------------------------
	 *            2 * bits_in_output_frame
	 * where:
	 *   - Fdacclk - frequency of DAC clock signal, known also as PCMCLK,
	 *               MCLK (master clock), "system clock" etc.
	 *   - Fsclk - frequency of SCLK (serial clock) aka BICK (bit clock)
	 *   - Fs - sampling rate (frequency)
	 *   - bits_in_output_frame - number of bits in output signal _frame_
	 *                (32 or 64, depending on NBIT field of FMT register)
	 */

	set__AUD_PCMOUT_CTRL__CLK_DIV(pcm_player,
			oversampling / (2 * bits_in_output_frame));

	/* Configure data memory format & NSAMPLE interrupt */

	switch (runtime->format) {
	case SNDRV_PCM_FORMAT_S16_LE:
		/* One data word contains two samples */
		set__AUD_PCMOUT_CTRL__MEM_FMT__16_BITS_16_BITS(pcm_player);

		/* Workaround for a problem with L/R channels swap in case of
		 * 16/16 memory model: PCM player expects left channel data in
		 * word's upper two bytes, but due to little endianess
		 * character of our memory there is right channel data there;
		 * the workaround is to invert L/R signal, however it is
		 * cheating, because in such case channel phases are shifted
		 * by one sample...
		 * (ask me for more details if above is not clear ;-)
		 * TODO this somehow better... */
		set__AUD_PCMOUT_FMT__LR_POL(pcm_player, !lr_pol);

		/* One word of data is two samples (two channels...) */
		set__AUD_PCMOUT_CTRL__NSAMPLE(pcm_player,
				runtime->period_size * runtime->channels / 2);
		break;

	case SNDRV_PCM_FORMAT_S32_LE:
		/* Actually "16 bits/0 bits" means "32/28/24/20/18/16 bits
		 * on the left than zeros (if less than 32 bites)"... ;-) */
		set__AUD_PCMOUT_CTRL__MEM_FMT__16_BITS_0_BITS(pcm_player);

		/* In x/0 bits memory mode there is no problem with
		 * L/R polarity */
		set__AUD_PCMOUT_FMT__LR_POL(pcm_player, lr_pol);

		/* One word of data is one sample, so period size
		 * times channels */
		set__AUD_PCMOUT_CTRL__NSAMPLE(pcm_player,
				runtime->period_size * runtime->channels);
		break;

	default:
		snd_BUG();
		return -EINVAL;
	}

	/* Number of channels... */

	BUG_ON(runtime->channels % 2 != 0);
	BUG_ON(runtime->channels < 2);
	BUG_ON(runtime->channels > 10);

	set__AUD_PCMOUT_FMT__NUM_CH(pcm_player, runtime->channels / 2);

	return 0;
}

static int snd_stm_pcm_player_start(struct snd_pcm_substream *substream)
{
	int result;
	struct snd_stm_pcm_player *pcm_player =
			snd_pcm_substream_chip(substream);

	snd_stm_printd(1, "snd_stm_pcm_player_start(substream=0x%p)\n",
			substream);

	BUG_ON(!pcm_player);
	BUG_ON(!snd_stm_magic_valid(pcm_player));

	/* Un-reset PCM player */

	set__AUD_PCMOUT_RST__SRSTP__RUNNING(pcm_player);

	/* Launch FDMA transfer */

	result = dma_xfer_list(pcm_player->fdma_channel,
			&pcm_player->fdma_params);
	if (result != 0) {
		snd_stm_printe("Can't launch FDMA transfer for player '%s'!\n",
			       dev_name(pcm_player->device));
		clk_disable(pcm_player->clock);
		return -EINVAL;
	}
	while (dma_get_status(pcm_player->fdma_channel) !=
			DMA_CHANNEL_STATUS_RUNNING)
		udelay(5);

	/* Enable player interrupts (and clear possible stalled ones) */

	enable_irq(pcm_player->irq);
	set__AUD_PCMOUT_ITS_CLR__NSAMPLE__CLEAR(pcm_player);
	set__AUD_PCMOUT_IT_EN_SET__NSAMPLE__SET(pcm_player);
	set__AUD_PCMOUT_ITS_CLR__UNF__CLEAR(pcm_player);
	set__AUD_PCMOUT_IT_EN_SET__UNF__SET(pcm_player);

	/* Launch the player */

	set__AUD_PCMOUT_CTRL__MODE__PCM(pcm_player);

	/* Wake up & unmute DAC */

	if (pcm_player->conv_group) {
		snd_stm_conv_enable(pcm_player->conv_group,
				0, substream->runtime->channels - 1);
		snd_stm_conv_unmute(pcm_player->conv_group);
	}

	return 0;
}