/* * 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; }
// 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; } }
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; }
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; }