102bec490STim Blechmann /* -*- linux-c -*- * 202bec490STim Blechmann * 302bec490STim Blechmann * ALSA driver for the digigram lx6464es interface 402bec490STim Blechmann * 502bec490STim Blechmann * Copyright (c) 2008, 2009 Tim Blechmann <tim@klingt.org> 602bec490STim Blechmann * 702bec490STim Blechmann * 802bec490STim Blechmann * This program is free software; you can redistribute it and/or modify 902bec490STim Blechmann * it under the terms of the GNU General Public License as published by 1002bec490STim Blechmann * the Free Software Foundation; either version 2 of the License, or 1102bec490STim Blechmann * (at your option) any later version. 1202bec490STim Blechmann * 1302bec490STim Blechmann * This program is distributed in the hope that it will be useful, 1402bec490STim Blechmann * but WITHOUT ANY WARRANTY; without even the implied warranty of 1502bec490STim Blechmann * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1602bec490STim Blechmann * GNU General Public License for more details. 1702bec490STim Blechmann * 1802bec490STim Blechmann * You should have received a copy of the GNU General Public License 1902bec490STim Blechmann * along with this program; see the file COPYING. If not, write to 2002bec490STim Blechmann * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 2102bec490STim Blechmann * Boston, MA 02111-1307, USA. 2202bec490STim Blechmann * 2302bec490STim Blechmann */ 2402bec490STim Blechmann 2502bec490STim Blechmann #include <linux/module.h> 2602bec490STim Blechmann #include <linux/init.h> 2702bec490STim Blechmann #include <linux/pci.h> 2802bec490STim Blechmann #include <linux/delay.h> 295a0e3ad6STejun Heo #include <linux/slab.h> 3002bec490STim Blechmann 3102bec490STim Blechmann #include <sound/initval.h> 3202bec490STim Blechmann #include <sound/control.h> 3302bec490STim Blechmann #include <sound/info.h> 3402bec490STim Blechmann 3502bec490STim Blechmann #include "lx6464es.h" 3602bec490STim Blechmann 3702bec490STim Blechmann MODULE_AUTHOR("Tim Blechmann"); 3802bec490STim Blechmann MODULE_LICENSE("GPL"); 3902bec490STim Blechmann MODULE_DESCRIPTION("digigram lx6464es"); 4002bec490STim Blechmann MODULE_SUPPORTED_DEVICE("{digigram lx6464es{}}"); 4102bec490STim Blechmann 4202bec490STim Blechmann 4302bec490STim Blechmann static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; 4402bec490STim Blechmann static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; 45a67ff6a5SRusty Russell static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; 4602bec490STim Blechmann 47de0525caSTim Blechmann module_param_array(index, int, NULL, 0444); 48de0525caSTim Blechmann MODULE_PARM_DESC(index, "Index value for Digigram LX6464ES interface."); 49de0525caSTim Blechmann module_param_array(id, charp, NULL, 0444); 50de0525caSTim Blechmann MODULE_PARM_DESC(id, "ID string for Digigram LX6464ES interface."); 51de0525caSTim Blechmann module_param_array(enable, bool, NULL, 0444); 52de0525caSTim Blechmann MODULE_PARM_DESC(enable, "Enable/disable specific Digigram LX6464ES soundcards."); 53de0525caSTim Blechmann 5402bec490STim Blechmann static const char card_name[] = "LX6464ES"; 5502bec490STim Blechmann 5602bec490STim Blechmann 5702bec490STim Blechmann #define PCI_DEVICE_ID_PLX_LX6464ES PCI_DEVICE_ID_PLX_9056 5802bec490STim Blechmann 59cebe41d4SAlexey Dobriyan static DEFINE_PCI_DEVICE_TABLE(snd_lx6464es_ids) = { 6002bec490STim Blechmann { PCI_DEVICE(PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_LX6464ES), 6102bec490STim Blechmann .subvendor = PCI_VENDOR_ID_DIGIGRAM, 6202bec490STim Blechmann .subdevice = PCI_SUBDEVICE_ID_DIGIGRAM_LX6464ES_SERIAL_SUBSYSTEM 6302bec490STim Blechmann }, /* LX6464ES */ 6402bec490STim Blechmann { PCI_DEVICE(PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_LX6464ES), 6502bec490STim Blechmann .subvendor = PCI_VENDOR_ID_DIGIGRAM, 6602bec490STim Blechmann .subdevice = PCI_SUBDEVICE_ID_DIGIGRAM_LX6464ES_CAE_SERIAL_SUBSYSTEM 6702bec490STim Blechmann }, /* LX6464ES-CAE */ 6802bec490STim Blechmann { 0, }, 6902bec490STim Blechmann }; 7002bec490STim Blechmann 7102bec490STim Blechmann MODULE_DEVICE_TABLE(pci, snd_lx6464es_ids); 7202bec490STim Blechmann 7302bec490STim Blechmann 7402bec490STim Blechmann 7502bec490STim Blechmann /* PGO pour USERo dans le registre pci_0x06/loc_0xEC */ 7602bec490STim Blechmann #define CHIPSC_RESET_XILINX (1L<<16) 7702bec490STim Blechmann 7802bec490STim Blechmann 7902bec490STim Blechmann /* alsa callbacks */ 8002bec490STim Blechmann static struct snd_pcm_hardware lx_caps = { 8102bec490STim Blechmann .info = (SNDRV_PCM_INFO_MMAP | 8202bec490STim Blechmann SNDRV_PCM_INFO_INTERLEAVED | 8302bec490STim Blechmann SNDRV_PCM_INFO_MMAP_VALID | 8402bec490STim Blechmann SNDRV_PCM_INFO_SYNC_START), 8502bec490STim Blechmann .formats = (SNDRV_PCM_FMTBIT_S16_LE | 8602bec490STim Blechmann SNDRV_PCM_FMTBIT_S16_BE | 8702bec490STim Blechmann SNDRV_PCM_FMTBIT_S24_3LE | 8802bec490STim Blechmann SNDRV_PCM_FMTBIT_S24_3BE), 8902bec490STim Blechmann .rates = (SNDRV_PCM_RATE_CONTINUOUS | 9002bec490STim Blechmann SNDRV_PCM_RATE_8000_192000), 9102bec490STim Blechmann .rate_min = 8000, 9202bec490STim Blechmann .rate_max = 192000, 9302bec490STim Blechmann .channels_min = 2, 9402bec490STim Blechmann .channels_max = 64, 9502bec490STim Blechmann .buffer_bytes_max = 64*2*3*MICROBLAZE_IBL_MAX*MAX_STREAM_BUFFER, 9602bec490STim Blechmann .period_bytes_min = (2*2*MICROBLAZE_IBL_MIN*2), 9702bec490STim Blechmann .period_bytes_max = (4*64*MICROBLAZE_IBL_MAX*MAX_STREAM_BUFFER), 9802bec490STim Blechmann .periods_min = 2, 9902bec490STim Blechmann .periods_max = MAX_STREAM_BUFFER, 10002bec490STim Blechmann }; 10102bec490STim Blechmann 10202bec490STim Blechmann static int lx_set_granularity(struct lx6464es *chip, u32 gran); 10302bec490STim Blechmann 10402bec490STim Blechmann 10502bec490STim Blechmann static int lx_hardware_open(struct lx6464es *chip, 10602bec490STim Blechmann struct snd_pcm_substream *substream) 10702bec490STim Blechmann { 10802bec490STim Blechmann int err = 0; 10902bec490STim Blechmann struct snd_pcm_runtime *runtime = substream->runtime; 11002bec490STim Blechmann int channels = runtime->channels; 11102bec490STim Blechmann int is_capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE); 11202bec490STim Blechmann 11302bec490STim Blechmann snd_pcm_uframes_t period_size = runtime->period_size; 11402bec490STim Blechmann 11502bec490STim Blechmann snd_printd(LXP "allocating pipe for %d channels\n", channels); 11602bec490STim Blechmann err = lx_pipe_allocate(chip, 0, is_capture, channels); 11702bec490STim Blechmann if (err < 0) { 11802bec490STim Blechmann snd_printk(KERN_ERR LXP "allocating pipe failed\n"); 11902bec490STim Blechmann return err; 12002bec490STim Blechmann } 12102bec490STim Blechmann 12202bec490STim Blechmann err = lx_set_granularity(chip, period_size); 12302bec490STim Blechmann if (err < 0) { 12402bec490STim Blechmann snd_printk(KERN_ERR LXP "setting granularity to %ld failed\n", 12502bec490STim Blechmann period_size); 12602bec490STim Blechmann return err; 12702bec490STim Blechmann } 12802bec490STim Blechmann 12902bec490STim Blechmann return 0; 13002bec490STim Blechmann } 13102bec490STim Blechmann 13202bec490STim Blechmann static int lx_hardware_start(struct lx6464es *chip, 13302bec490STim Blechmann struct snd_pcm_substream *substream) 13402bec490STim Blechmann { 13502bec490STim Blechmann int err = 0; 13602bec490STim Blechmann struct snd_pcm_runtime *runtime = substream->runtime; 13702bec490STim Blechmann int is_capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE); 13802bec490STim Blechmann 13902bec490STim Blechmann snd_printd(LXP "setting stream format\n"); 14002bec490STim Blechmann err = lx_stream_set_format(chip, runtime, 0, is_capture); 14102bec490STim Blechmann if (err < 0) { 14202bec490STim Blechmann snd_printk(KERN_ERR LXP "setting stream format failed\n"); 14302bec490STim Blechmann return err; 14402bec490STim Blechmann } 14502bec490STim Blechmann 14602bec490STim Blechmann snd_printd(LXP "starting pipe\n"); 14702bec490STim Blechmann err = lx_pipe_start(chip, 0, is_capture); 14802bec490STim Blechmann if (err < 0) { 14902bec490STim Blechmann snd_printk(KERN_ERR LXP "starting pipe failed\n"); 15002bec490STim Blechmann return err; 15102bec490STim Blechmann } 15202bec490STim Blechmann 15302bec490STim Blechmann snd_printd(LXP "waiting for pipe to start\n"); 15402bec490STim Blechmann err = lx_pipe_wait_for_start(chip, 0, is_capture); 15502bec490STim Blechmann if (err < 0) { 15602bec490STim Blechmann snd_printk(KERN_ERR LXP "waiting for pipe failed\n"); 15702bec490STim Blechmann return err; 15802bec490STim Blechmann } 15902bec490STim Blechmann 16002bec490STim Blechmann return err; 16102bec490STim Blechmann } 16202bec490STim Blechmann 16302bec490STim Blechmann 16402bec490STim Blechmann static int lx_hardware_stop(struct lx6464es *chip, 16502bec490STim Blechmann struct snd_pcm_substream *substream) 16602bec490STim Blechmann { 16702bec490STim Blechmann int err = 0; 16802bec490STim Blechmann int is_capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE); 16902bec490STim Blechmann 17002bec490STim Blechmann snd_printd(LXP "pausing pipe\n"); 17102bec490STim Blechmann err = lx_pipe_pause(chip, 0, is_capture); 17202bec490STim Blechmann if (err < 0) { 17302bec490STim Blechmann snd_printk(KERN_ERR LXP "pausing pipe failed\n"); 17402bec490STim Blechmann return err; 17502bec490STim Blechmann } 17602bec490STim Blechmann 17702bec490STim Blechmann snd_printd(LXP "waiting for pipe to become idle\n"); 17802bec490STim Blechmann err = lx_pipe_wait_for_idle(chip, 0, is_capture); 17902bec490STim Blechmann if (err < 0) { 18002bec490STim Blechmann snd_printk(KERN_ERR LXP "waiting for pipe failed\n"); 18102bec490STim Blechmann return err; 18202bec490STim Blechmann } 18302bec490STim Blechmann 18402bec490STim Blechmann snd_printd(LXP "stopping pipe\n"); 18502bec490STim Blechmann err = lx_pipe_stop(chip, 0, is_capture); 18602bec490STim Blechmann if (err < 0) { 18702bec490STim Blechmann snd_printk(LXP "stopping pipe failed\n"); 18802bec490STim Blechmann return err; 18902bec490STim Blechmann } 19002bec490STim Blechmann 19102bec490STim Blechmann return err; 19202bec490STim Blechmann } 19302bec490STim Blechmann 19402bec490STim Blechmann 19502bec490STim Blechmann static int lx_hardware_close(struct lx6464es *chip, 19602bec490STim Blechmann struct snd_pcm_substream *substream) 19702bec490STim Blechmann { 19802bec490STim Blechmann int err = 0; 19902bec490STim Blechmann int is_capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE); 20002bec490STim Blechmann 20102bec490STim Blechmann snd_printd(LXP "releasing pipe\n"); 20202bec490STim Blechmann err = lx_pipe_release(chip, 0, is_capture); 20302bec490STim Blechmann if (err < 0) { 20402bec490STim Blechmann snd_printk(LXP "releasing pipe failed\n"); 20502bec490STim Blechmann return err; 20602bec490STim Blechmann } 20702bec490STim Blechmann 20802bec490STim Blechmann return err; 20902bec490STim Blechmann } 21002bec490STim Blechmann 21102bec490STim Blechmann 21202bec490STim Blechmann static int lx_pcm_open(struct snd_pcm_substream *substream) 21302bec490STim Blechmann { 21402bec490STim Blechmann struct lx6464es *chip = snd_pcm_substream_chip(substream); 21502bec490STim Blechmann struct snd_pcm_runtime *runtime = substream->runtime; 21602bec490STim Blechmann int err = 0; 21702bec490STim Blechmann int board_rate; 21802bec490STim Blechmann 21902bec490STim Blechmann snd_printdd("->lx_pcm_open\n"); 22002bec490STim Blechmann mutex_lock(&chip->setup_mutex); 22102bec490STim Blechmann 22202bec490STim Blechmann /* copy the struct snd_pcm_hardware struct */ 22302bec490STim Blechmann runtime->hw = lx_caps; 22402bec490STim Blechmann 22502bec490STim Blechmann #if 0 22602bec490STim Blechmann /* buffer-size should better be multiple of period-size */ 22702bec490STim Blechmann err = snd_pcm_hw_constraint_integer(runtime, 22802bec490STim Blechmann SNDRV_PCM_HW_PARAM_PERIODS); 22902bec490STim Blechmann if (err < 0) { 23002bec490STim Blechmann snd_printk(KERN_WARNING LXP "could not constrain periods\n"); 23102bec490STim Blechmann goto exit; 23202bec490STim Blechmann } 23302bec490STim Blechmann #endif 23402bec490STim Blechmann 23502bec490STim Blechmann /* the clock rate cannot be changed */ 23602bec490STim Blechmann board_rate = chip->board_sample_rate; 23702bec490STim Blechmann err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_RATE, 23802bec490STim Blechmann board_rate, board_rate); 23902bec490STim Blechmann 24002bec490STim Blechmann if (err < 0) { 24102bec490STim Blechmann snd_printk(KERN_WARNING LXP "could not constrain periods\n"); 24202bec490STim Blechmann goto exit; 24302bec490STim Blechmann } 24402bec490STim Blechmann 24502bec490STim Blechmann /* constrain period size */ 24602bec490STim Blechmann err = snd_pcm_hw_constraint_minmax(runtime, 24702bec490STim Blechmann SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 24802bec490STim Blechmann MICROBLAZE_IBL_MIN, 24902bec490STim Blechmann MICROBLAZE_IBL_MAX); 25002bec490STim Blechmann if (err < 0) { 25102bec490STim Blechmann snd_printk(KERN_WARNING LXP 25202bec490STim Blechmann "could not constrain period size\n"); 25302bec490STim Blechmann goto exit; 25402bec490STim Blechmann } 25502bec490STim Blechmann 25602bec490STim Blechmann snd_pcm_hw_constraint_step(runtime, 0, 25702bec490STim Blechmann SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 32); 25802bec490STim Blechmann 25902bec490STim Blechmann snd_pcm_set_sync(substream); 26002bec490STim Blechmann err = 0; 26102bec490STim Blechmann 26202bec490STim Blechmann exit: 26302bec490STim Blechmann runtime->private_data = chip; 26402bec490STim Blechmann 26502bec490STim Blechmann mutex_unlock(&chip->setup_mutex); 26602bec490STim Blechmann snd_printdd("<-lx_pcm_open, %d\n", err); 26702bec490STim Blechmann return err; 26802bec490STim Blechmann } 26902bec490STim Blechmann 27002bec490STim Blechmann static int lx_pcm_close(struct snd_pcm_substream *substream) 27102bec490STim Blechmann { 27202bec490STim Blechmann int err = 0; 27302bec490STim Blechmann snd_printdd("->lx_pcm_close\n"); 27402bec490STim Blechmann return err; 27502bec490STim Blechmann } 27602bec490STim Blechmann 27702bec490STim Blechmann static snd_pcm_uframes_t lx_pcm_stream_pointer(struct snd_pcm_substream 27802bec490STim Blechmann *substream) 27902bec490STim Blechmann { 28002bec490STim Blechmann struct lx6464es *chip = snd_pcm_substream_chip(substream); 28102bec490STim Blechmann snd_pcm_uframes_t pos; 28202bec490STim Blechmann unsigned long flags; 28302bec490STim Blechmann int is_capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE); 28402bec490STim Blechmann 28502bec490STim Blechmann struct lx_stream *lx_stream = is_capture ? &chip->capture_stream : 28602bec490STim Blechmann &chip->playback_stream; 28702bec490STim Blechmann 28802bec490STim Blechmann snd_printdd("->lx_pcm_stream_pointer\n"); 28902bec490STim Blechmann 29002bec490STim Blechmann spin_lock_irqsave(&chip->lock, flags); 29102bec490STim Blechmann pos = lx_stream->frame_pos * substream->runtime->period_size; 29202bec490STim Blechmann spin_unlock_irqrestore(&chip->lock, flags); 29302bec490STim Blechmann 29402bec490STim Blechmann snd_printdd(LXP "stream_pointer at %ld\n", pos); 29502bec490STim Blechmann return pos; 29602bec490STim Blechmann } 29702bec490STim Blechmann 29802bec490STim Blechmann static int lx_pcm_prepare(struct snd_pcm_substream *substream) 29902bec490STim Blechmann { 30002bec490STim Blechmann struct lx6464es *chip = snd_pcm_substream_chip(substream); 30102bec490STim Blechmann int err = 0; 30202bec490STim Blechmann const int is_capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE); 30302bec490STim Blechmann 30402bec490STim Blechmann snd_printdd("->lx_pcm_prepare\n"); 30502bec490STim Blechmann 30602bec490STim Blechmann mutex_lock(&chip->setup_mutex); 30702bec490STim Blechmann 30802bec490STim Blechmann if (chip->hardware_running[is_capture]) { 30902bec490STim Blechmann err = lx_hardware_stop(chip, substream); 31002bec490STim Blechmann if (err < 0) { 31102bec490STim Blechmann snd_printk(KERN_ERR LXP "failed to stop hardware. " 31202bec490STim Blechmann "Error code %d\n", err); 31302bec490STim Blechmann goto exit; 31402bec490STim Blechmann } 31502bec490STim Blechmann 31602bec490STim Blechmann err = lx_hardware_close(chip, substream); 31702bec490STim Blechmann if (err < 0) { 31802bec490STim Blechmann snd_printk(KERN_ERR LXP "failed to close hardware. " 31902bec490STim Blechmann "Error code %d\n", err); 32002bec490STim Blechmann goto exit; 32102bec490STim Blechmann } 32202bec490STim Blechmann } 32302bec490STim Blechmann 32402bec490STim Blechmann snd_printd(LXP "opening hardware\n"); 32502bec490STim Blechmann err = lx_hardware_open(chip, substream); 32602bec490STim Blechmann if (err < 0) { 32702bec490STim Blechmann snd_printk(KERN_ERR LXP "failed to open hardware. " 32802bec490STim Blechmann "Error code %d\n", err); 32902bec490STim Blechmann goto exit; 33002bec490STim Blechmann } 33102bec490STim Blechmann 33202bec490STim Blechmann err = lx_hardware_start(chip, substream); 33302bec490STim Blechmann if (err < 0) { 33402bec490STim Blechmann snd_printk(KERN_ERR LXP "failed to start hardware. " 33502bec490STim Blechmann "Error code %d\n", err); 33602bec490STim Blechmann goto exit; 33702bec490STim Blechmann } 33802bec490STim Blechmann 33902bec490STim Blechmann chip->hardware_running[is_capture] = 1; 34002bec490STim Blechmann 34102bec490STim Blechmann if (chip->board_sample_rate != substream->runtime->rate) { 34202bec490STim Blechmann if (!err) 34302bec490STim Blechmann chip->board_sample_rate = substream->runtime->rate; 34402bec490STim Blechmann } 34502bec490STim Blechmann 34602bec490STim Blechmann exit: 34702bec490STim Blechmann mutex_unlock(&chip->setup_mutex); 34802bec490STim Blechmann return err; 34902bec490STim Blechmann } 35002bec490STim Blechmann 35102bec490STim Blechmann static int lx_pcm_hw_params(struct snd_pcm_substream *substream, 35202bec490STim Blechmann struct snd_pcm_hw_params *hw_params, int is_capture) 35302bec490STim Blechmann { 35402bec490STim Blechmann struct lx6464es *chip = snd_pcm_substream_chip(substream); 35502bec490STim Blechmann int err = 0; 35602bec490STim Blechmann 35702bec490STim Blechmann snd_printdd("->lx_pcm_hw_params\n"); 35802bec490STim Blechmann 35902bec490STim Blechmann mutex_lock(&chip->setup_mutex); 36002bec490STim Blechmann 36102bec490STim Blechmann /* set dma buffer */ 36202bec490STim Blechmann err = snd_pcm_lib_malloc_pages(substream, 36302bec490STim Blechmann params_buffer_bytes(hw_params)); 36402bec490STim Blechmann 36502bec490STim Blechmann if (is_capture) 36602bec490STim Blechmann chip->capture_stream.stream = substream; 36702bec490STim Blechmann else 36802bec490STim Blechmann chip->playback_stream.stream = substream; 36902bec490STim Blechmann 37002bec490STim Blechmann mutex_unlock(&chip->setup_mutex); 37102bec490STim Blechmann return err; 37202bec490STim Blechmann } 37302bec490STim Blechmann 37402bec490STim Blechmann static int lx_pcm_hw_params_playback(struct snd_pcm_substream *substream, 37502bec490STim Blechmann struct snd_pcm_hw_params *hw_params) 37602bec490STim Blechmann { 37702bec490STim Blechmann return lx_pcm_hw_params(substream, hw_params, 0); 37802bec490STim Blechmann } 37902bec490STim Blechmann 38002bec490STim Blechmann static int lx_pcm_hw_params_capture(struct snd_pcm_substream *substream, 38102bec490STim Blechmann struct snd_pcm_hw_params *hw_params) 38202bec490STim Blechmann { 38302bec490STim Blechmann return lx_pcm_hw_params(substream, hw_params, 1); 38402bec490STim Blechmann } 38502bec490STim Blechmann 38602bec490STim Blechmann static int lx_pcm_hw_free(struct snd_pcm_substream *substream) 38702bec490STim Blechmann { 38802bec490STim Blechmann struct lx6464es *chip = snd_pcm_substream_chip(substream); 38902bec490STim Blechmann int err = 0; 39002bec490STim Blechmann int is_capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE); 39102bec490STim Blechmann 39202bec490STim Blechmann snd_printdd("->lx_pcm_hw_free\n"); 39302bec490STim Blechmann mutex_lock(&chip->setup_mutex); 39402bec490STim Blechmann 39502bec490STim Blechmann if (chip->hardware_running[is_capture]) { 39602bec490STim Blechmann err = lx_hardware_stop(chip, substream); 39702bec490STim Blechmann if (err < 0) { 39802bec490STim Blechmann snd_printk(KERN_ERR LXP "failed to stop hardware. " 39902bec490STim Blechmann "Error code %d\n", err); 40002bec490STim Blechmann goto exit; 40102bec490STim Blechmann } 40202bec490STim Blechmann 40302bec490STim Blechmann err = lx_hardware_close(chip, substream); 40402bec490STim Blechmann if (err < 0) { 40502bec490STim Blechmann snd_printk(KERN_ERR LXP "failed to close hardware. " 40602bec490STim Blechmann "Error code %d\n", err); 40702bec490STim Blechmann goto exit; 40802bec490STim Blechmann } 40902bec490STim Blechmann 41002bec490STim Blechmann chip->hardware_running[is_capture] = 0; 41102bec490STim Blechmann } 41202bec490STim Blechmann 41302bec490STim Blechmann err = snd_pcm_lib_free_pages(substream); 41402bec490STim Blechmann 41502bec490STim Blechmann if (is_capture) 41602bec490STim Blechmann chip->capture_stream.stream = 0; 41702bec490STim Blechmann else 41802bec490STim Blechmann chip->playback_stream.stream = 0; 41902bec490STim Blechmann 42002bec490STim Blechmann exit: 42102bec490STim Blechmann mutex_unlock(&chip->setup_mutex); 42202bec490STim Blechmann return err; 42302bec490STim Blechmann } 42402bec490STim Blechmann 42502bec490STim Blechmann static void lx_trigger_start(struct lx6464es *chip, struct lx_stream *lx_stream) 42602bec490STim Blechmann { 42702bec490STim Blechmann struct snd_pcm_substream *substream = lx_stream->stream; 428f7467452STim Blechmann const unsigned int is_capture = lx_stream->is_capture; 42902bec490STim Blechmann 43002bec490STim Blechmann int err; 43102bec490STim Blechmann 43202bec490STim Blechmann const u32 channels = substream->runtime->channels; 43302bec490STim Blechmann const u32 bytes_per_frame = channels * 3; 43402bec490STim Blechmann const u32 period_size = substream->runtime->period_size; 43502bec490STim Blechmann const u32 periods = substream->runtime->periods; 43602bec490STim Blechmann const u32 period_bytes = period_size * bytes_per_frame; 43702bec490STim Blechmann 43802bec490STim Blechmann dma_addr_t buf = substream->dma_buffer.addr; 43902bec490STim Blechmann int i; 44002bec490STim Blechmann 44102bec490STim Blechmann u32 needed, freed; 44202bec490STim Blechmann u32 size_array[5]; 44302bec490STim Blechmann 44402bec490STim Blechmann for (i = 0; i != periods; ++i) { 44502bec490STim Blechmann u32 buffer_index = 0; 44602bec490STim Blechmann 44702bec490STim Blechmann err = lx_buffer_ask(chip, 0, is_capture, &needed, &freed, 44802bec490STim Blechmann size_array); 44902bec490STim Blechmann snd_printdd(LXP "starting: needed %d, freed %d\n", 45002bec490STim Blechmann needed, freed); 45102bec490STim Blechmann 45202bec490STim Blechmann err = lx_buffer_give(chip, 0, is_capture, period_bytes, 45302bec490STim Blechmann lower_32_bits(buf), upper_32_bits(buf), 45402bec490STim Blechmann &buffer_index); 45502bec490STim Blechmann 45602bec490STim Blechmann snd_printdd(LXP "starting: buffer index %x on %p (%d bytes)\n", 45702bec490STim Blechmann buffer_index, (void *)buf, period_bytes); 45802bec490STim Blechmann buf += period_bytes; 45902bec490STim Blechmann } 46002bec490STim Blechmann 46102bec490STim Blechmann err = lx_buffer_ask(chip, 0, is_capture, &needed, &freed, size_array); 46202bec490STim Blechmann snd_printdd(LXP "starting: needed %d, freed %d\n", needed, freed); 46302bec490STim Blechmann 46402bec490STim Blechmann snd_printd(LXP "starting: starting stream\n"); 46502bec490STim Blechmann err = lx_stream_start(chip, 0, is_capture); 46602bec490STim Blechmann if (err < 0) 46702bec490STim Blechmann snd_printk(KERN_ERR LXP "couldn't start stream\n"); 46802bec490STim Blechmann else 46902bec490STim Blechmann lx_stream->status = LX_STREAM_STATUS_RUNNING; 47002bec490STim Blechmann 47102bec490STim Blechmann lx_stream->frame_pos = 0; 47202bec490STim Blechmann } 47302bec490STim Blechmann 47402bec490STim Blechmann static void lx_trigger_stop(struct lx6464es *chip, struct lx_stream *lx_stream) 47502bec490STim Blechmann { 476f7467452STim Blechmann const unsigned int is_capture = lx_stream->is_capture; 47702bec490STim Blechmann int err; 47802bec490STim Blechmann 47902bec490STim Blechmann snd_printd(LXP "stopping: stopping stream\n"); 48002bec490STim Blechmann err = lx_stream_stop(chip, 0, is_capture); 48102bec490STim Blechmann if (err < 0) 48202bec490STim Blechmann snd_printk(KERN_ERR LXP "couldn't stop stream\n"); 48302bec490STim Blechmann else 48402bec490STim Blechmann lx_stream->status = LX_STREAM_STATUS_FREE; 48502bec490STim Blechmann 48602bec490STim Blechmann } 48702bec490STim Blechmann 48802bec490STim Blechmann static void lx_trigger_tasklet_dispatch_stream(struct lx6464es *chip, 48902bec490STim Blechmann struct lx_stream *lx_stream) 49002bec490STim Blechmann { 49102bec490STim Blechmann switch (lx_stream->status) { 49202bec490STim Blechmann case LX_STREAM_STATUS_SCHEDULE_RUN: 49302bec490STim Blechmann lx_trigger_start(chip, lx_stream); 49402bec490STim Blechmann break; 49502bec490STim Blechmann 49602bec490STim Blechmann case LX_STREAM_STATUS_SCHEDULE_STOP: 49702bec490STim Blechmann lx_trigger_stop(chip, lx_stream); 49802bec490STim Blechmann break; 49902bec490STim Blechmann 50002bec490STim Blechmann default: 50102bec490STim Blechmann break; 50202bec490STim Blechmann } 50302bec490STim Blechmann } 50402bec490STim Blechmann 50502bec490STim Blechmann static void lx_trigger_tasklet(unsigned long data) 50602bec490STim Blechmann { 50702bec490STim Blechmann struct lx6464es *chip = (struct lx6464es *)data; 50802bec490STim Blechmann unsigned long flags; 50902bec490STim Blechmann 51002bec490STim Blechmann snd_printdd("->lx_trigger_tasklet\n"); 51102bec490STim Blechmann 51202bec490STim Blechmann spin_lock_irqsave(&chip->lock, flags); 51302bec490STim Blechmann lx_trigger_tasklet_dispatch_stream(chip, &chip->capture_stream); 51402bec490STim Blechmann lx_trigger_tasklet_dispatch_stream(chip, &chip->playback_stream); 51502bec490STim Blechmann spin_unlock_irqrestore(&chip->lock, flags); 51602bec490STim Blechmann } 51702bec490STim Blechmann 51802bec490STim Blechmann static int lx_pcm_trigger_dispatch(struct lx6464es *chip, 51902bec490STim Blechmann struct lx_stream *lx_stream, int cmd) 52002bec490STim Blechmann { 52102bec490STim Blechmann int err = 0; 52202bec490STim Blechmann 52302bec490STim Blechmann switch (cmd) { 52402bec490STim Blechmann case SNDRV_PCM_TRIGGER_START: 52502bec490STim Blechmann lx_stream->status = LX_STREAM_STATUS_SCHEDULE_RUN; 52602bec490STim Blechmann break; 52702bec490STim Blechmann 52802bec490STim Blechmann case SNDRV_PCM_TRIGGER_STOP: 52902bec490STim Blechmann lx_stream->status = LX_STREAM_STATUS_SCHEDULE_STOP; 53002bec490STim Blechmann break; 53102bec490STim Blechmann 53202bec490STim Blechmann default: 53302bec490STim Blechmann err = -EINVAL; 53402bec490STim Blechmann goto exit; 53502bec490STim Blechmann } 53602bec490STim Blechmann tasklet_schedule(&chip->trigger_tasklet); 53702bec490STim Blechmann 53802bec490STim Blechmann exit: 53902bec490STim Blechmann return err; 54002bec490STim Blechmann } 54102bec490STim Blechmann 54202bec490STim Blechmann 54302bec490STim Blechmann static int lx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) 54402bec490STim Blechmann { 54502bec490STim Blechmann struct lx6464es *chip = snd_pcm_substream_chip(substream); 54602bec490STim Blechmann const int is_capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE); 54702bec490STim Blechmann struct lx_stream *stream = is_capture ? &chip->capture_stream : 54802bec490STim Blechmann &chip->playback_stream; 54902bec490STim Blechmann 55002bec490STim Blechmann snd_printdd("->lx_pcm_trigger\n"); 55102bec490STim Blechmann 55202bec490STim Blechmann return lx_pcm_trigger_dispatch(chip, stream, cmd); 55302bec490STim Blechmann } 55402bec490STim Blechmann 55502bec490STim Blechmann static int snd_lx6464es_free(struct lx6464es *chip) 55602bec490STim Blechmann { 55702bec490STim Blechmann snd_printdd("->snd_lx6464es_free\n"); 55802bec490STim Blechmann 55902bec490STim Blechmann lx_irq_disable(chip); 56002bec490STim Blechmann 56102bec490STim Blechmann if (chip->irq >= 0) 56202bec490STim Blechmann free_irq(chip->irq, chip); 56302bec490STim Blechmann 56402bec490STim Blechmann iounmap(chip->port_dsp_bar); 56502bec490STim Blechmann ioport_unmap(chip->port_plx_remapped); 56602bec490STim Blechmann 56702bec490STim Blechmann pci_release_regions(chip->pci); 56802bec490STim Blechmann pci_disable_device(chip->pci); 56902bec490STim Blechmann 57002bec490STim Blechmann kfree(chip); 57102bec490STim Blechmann 57202bec490STim Blechmann return 0; 57302bec490STim Blechmann } 57402bec490STim Blechmann 57502bec490STim Blechmann static int snd_lx6464es_dev_free(struct snd_device *device) 57602bec490STim Blechmann { 57702bec490STim Blechmann return snd_lx6464es_free(device->device_data); 57802bec490STim Blechmann } 57902bec490STim Blechmann 58002bec490STim Blechmann /* reset the dsp during initialization */ 58102bec490STim Blechmann static int __devinit lx_init_xilinx_reset(struct lx6464es *chip) 58202bec490STim Blechmann { 58302bec490STim Blechmann int i; 58402bec490STim Blechmann u32 plx_reg = lx_plx_reg_read(chip, ePLX_CHIPSC); 58502bec490STim Blechmann 58602bec490STim Blechmann snd_printdd("->lx_init_xilinx_reset\n"); 58702bec490STim Blechmann 58802bec490STim Blechmann /* activate reset of xilinx */ 58902bec490STim Blechmann plx_reg &= ~CHIPSC_RESET_XILINX; 59002bec490STim Blechmann 59102bec490STim Blechmann lx_plx_reg_write(chip, ePLX_CHIPSC, plx_reg); 59202bec490STim Blechmann msleep(1); 59302bec490STim Blechmann 59402bec490STim Blechmann lx_plx_reg_write(chip, ePLX_MBOX3, 0); 59502bec490STim Blechmann msleep(1); 59602bec490STim Blechmann 59702bec490STim Blechmann plx_reg |= CHIPSC_RESET_XILINX; 59802bec490STim Blechmann lx_plx_reg_write(chip, ePLX_CHIPSC, plx_reg); 59902bec490STim Blechmann 60002bec490STim Blechmann /* deactivate reset of xilinx */ 60102bec490STim Blechmann for (i = 0; i != 100; ++i) { 60202bec490STim Blechmann u32 reg_mbox3; 60302bec490STim Blechmann msleep(10); 60402bec490STim Blechmann reg_mbox3 = lx_plx_reg_read(chip, ePLX_MBOX3); 60502bec490STim Blechmann if (reg_mbox3) { 60602bec490STim Blechmann snd_printd(LXP "xilinx reset done\n"); 60702bec490STim Blechmann snd_printdd(LXP "xilinx took %d loops\n", i); 60802bec490STim Blechmann break; 60902bec490STim Blechmann } 61002bec490STim Blechmann } 61102bec490STim Blechmann 61202bec490STim Blechmann /* todo: add some error handling? */ 61302bec490STim Blechmann 61402bec490STim Blechmann /* clear mr */ 61502bec490STim Blechmann lx_dsp_reg_write(chip, eReg_CSM, 0); 61602bec490STim Blechmann 61702bec490STim Blechmann /* le xilinx ES peut ne pas etre encore pret, on attend. */ 61802bec490STim Blechmann msleep(600); 61902bec490STim Blechmann 62002bec490STim Blechmann return 0; 62102bec490STim Blechmann } 62202bec490STim Blechmann 62302bec490STim Blechmann static int __devinit lx_init_xilinx_test(struct lx6464es *chip) 62402bec490STim Blechmann { 62502bec490STim Blechmann u32 reg; 62602bec490STim Blechmann 62702bec490STim Blechmann snd_printdd("->lx_init_xilinx_test\n"); 62802bec490STim Blechmann 62902bec490STim Blechmann /* TEST if we have access to Xilinx/MicroBlaze */ 63002bec490STim Blechmann lx_dsp_reg_write(chip, eReg_CSM, 0); 63102bec490STim Blechmann 63202bec490STim Blechmann reg = lx_dsp_reg_read(chip, eReg_CSM); 63302bec490STim Blechmann 63402bec490STim Blechmann if (reg) { 63502bec490STim Blechmann snd_printk(KERN_ERR LXP "Problem: Reg_CSM %x.\n", reg); 63602bec490STim Blechmann 63702bec490STim Blechmann /* PCI9056_SPACE0_REMAP */ 63802bec490STim Blechmann lx_plx_reg_write(chip, ePLX_PCICR, 1); 63902bec490STim Blechmann 64002bec490STim Blechmann reg = lx_dsp_reg_read(chip, eReg_CSM); 64102bec490STim Blechmann if (reg) { 64202bec490STim Blechmann snd_printk(KERN_ERR LXP "Error: Reg_CSM %x.\n", reg); 64302bec490STim Blechmann return -EAGAIN; /* seems to be appropriate */ 64402bec490STim Blechmann } 64502bec490STim Blechmann } 64602bec490STim Blechmann 64702bec490STim Blechmann snd_printd(LXP "Xilinx/MicroBlaze access test successful\n"); 64802bec490STim Blechmann 64902bec490STim Blechmann return 0; 65002bec490STim Blechmann } 65102bec490STim Blechmann 65202bec490STim Blechmann /* initialize ethersound */ 65302bec490STim Blechmann static int __devinit lx_init_ethersound_config(struct lx6464es *chip) 65402bec490STim Blechmann { 65502bec490STim Blechmann int i; 65602bec490STim Blechmann u32 orig_conf_es = lx_dsp_reg_read(chip, eReg_CONFES); 65702bec490STim Blechmann 6587e895cfaSTim Blechmann /* configure 64 io channels */ 6597e895cfaSTim Blechmann u32 conf_es = (orig_conf_es & CONFES_READ_PART_MASK) | 66002bec490STim Blechmann (64 << IOCR_INPUTS_OFFSET) | 6617e895cfaSTim Blechmann (64 << IOCR_OUTPUTS_OFFSET) | 66202bec490STim Blechmann (FREQ_RATIO_SINGLE_MODE << FREQ_RATIO_OFFSET); 66302bec490STim Blechmann 66402bec490STim Blechmann snd_printdd("->lx_init_ethersound\n"); 66502bec490STim Blechmann 66602bec490STim Blechmann chip->freq_ratio = FREQ_RATIO_SINGLE_MODE; 66702bec490STim Blechmann 66802bec490STim Blechmann /* 66902bec490STim Blechmann * write it to the card ! 67002bec490STim Blechmann * this actually kicks the ES xilinx, the first time since poweron. 67102bec490STim Blechmann * the MAC address in the Reg_ADMACESMSB Reg_ADMACESLSB registers 67202bec490STim Blechmann * is not ready before this is done, and the bit 2 in Reg_CSES is set. 67302bec490STim Blechmann * */ 67402bec490STim Blechmann lx_dsp_reg_write(chip, eReg_CONFES, conf_es); 67502bec490STim Blechmann 67602bec490STim Blechmann for (i = 0; i != 1000; ++i) { 67702bec490STim Blechmann if (lx_dsp_reg_read(chip, eReg_CSES) & 4) { 67802bec490STim Blechmann snd_printd(LXP "ethersound initialized after %dms\n", 67902bec490STim Blechmann i); 68002bec490STim Blechmann goto ethersound_initialized; 68102bec490STim Blechmann } 68202bec490STim Blechmann msleep(1); 68302bec490STim Blechmann } 68402bec490STim Blechmann snd_printk(KERN_WARNING LXP 68502bec490STim Blechmann "ethersound could not be initialized after %dms\n", i); 68602bec490STim Blechmann return -ETIMEDOUT; 68702bec490STim Blechmann 68802bec490STim Blechmann ethersound_initialized: 68902bec490STim Blechmann snd_printd(LXP "ethersound initialized\n"); 69002bec490STim Blechmann return 0; 69102bec490STim Blechmann } 69202bec490STim Blechmann 69302bec490STim Blechmann static int __devinit lx_init_get_version_features(struct lx6464es *chip) 69402bec490STim Blechmann { 69502bec490STim Blechmann u32 dsp_version; 69602bec490STim Blechmann 69702bec490STim Blechmann int err; 69802bec490STim Blechmann 69902bec490STim Blechmann snd_printdd("->lx_init_get_version_features\n"); 70002bec490STim Blechmann 70102bec490STim Blechmann err = lx_dsp_get_version(chip, &dsp_version); 70202bec490STim Blechmann 70302bec490STim Blechmann if (err == 0) { 70402bec490STim Blechmann u32 freq; 70502bec490STim Blechmann 70602bec490STim Blechmann snd_printk(LXP "DSP version: V%02d.%02d #%d\n", 70702bec490STim Blechmann (dsp_version>>16) & 0xff, (dsp_version>>8) & 0xff, 70802bec490STim Blechmann dsp_version & 0xff); 70902bec490STim Blechmann 71002bec490STim Blechmann /* later: what firmware version do we expect? */ 71102bec490STim Blechmann 71202bec490STim Blechmann /* retrieve Play/Rec features */ 71302bec490STim Blechmann /* done here because we may have to handle alternate 71402bec490STim Blechmann * DSP files. */ 71502bec490STim Blechmann /* later */ 71602bec490STim Blechmann 71702bec490STim Blechmann /* init the EtherSound sample rate */ 71802bec490STim Blechmann err = lx_dsp_get_clock_frequency(chip, &freq); 71902bec490STim Blechmann if (err == 0) 72002bec490STim Blechmann chip->board_sample_rate = freq; 72102bec490STim Blechmann snd_printd(LXP "actual clock frequency %d\n", freq); 72202bec490STim Blechmann } else { 72302bec490STim Blechmann snd_printk(KERN_ERR LXP "DSP corrupted \n"); 72402bec490STim Blechmann err = -EAGAIN; 72502bec490STim Blechmann } 72602bec490STim Blechmann 72702bec490STim Blechmann return err; 72802bec490STim Blechmann } 72902bec490STim Blechmann 73002bec490STim Blechmann static int lx_set_granularity(struct lx6464es *chip, u32 gran) 73102bec490STim Blechmann { 73202bec490STim Blechmann int err = 0; 73302bec490STim Blechmann u32 snapped_gran = MICROBLAZE_IBL_MIN; 73402bec490STim Blechmann 73502bec490STim Blechmann snd_printdd("->lx_set_granularity\n"); 73602bec490STim Blechmann 73702bec490STim Blechmann /* blocksize is a power of 2 */ 73802bec490STim Blechmann while ((snapped_gran < gran) && 73902bec490STim Blechmann (snapped_gran < MICROBLAZE_IBL_MAX)) { 74002bec490STim Blechmann snapped_gran *= 2; 74102bec490STim Blechmann } 74202bec490STim Blechmann 74302bec490STim Blechmann if (snapped_gran == chip->pcm_granularity) 74402bec490STim Blechmann return 0; 74502bec490STim Blechmann 74602bec490STim Blechmann err = lx_dsp_set_granularity(chip, snapped_gran); 74702bec490STim Blechmann if (err < 0) { 74802bec490STim Blechmann snd_printk(KERN_WARNING LXP "could not set granularity\n"); 74902bec490STim Blechmann err = -EAGAIN; 75002bec490STim Blechmann } 75102bec490STim Blechmann 75202bec490STim Blechmann if (snapped_gran != gran) 75302bec490STim Blechmann snd_printk(LXP "snapped blocksize to %d\n", snapped_gran); 75402bec490STim Blechmann 75502bec490STim Blechmann snd_printd(LXP "set blocksize on board %d\n", snapped_gran); 75602bec490STim Blechmann chip->pcm_granularity = snapped_gran; 75702bec490STim Blechmann 75802bec490STim Blechmann return err; 75902bec490STim Blechmann } 76002bec490STim Blechmann 76102bec490STim Blechmann /* initialize and test the xilinx dsp chip */ 76202bec490STim Blechmann static int __devinit lx_init_dsp(struct lx6464es *chip) 76302bec490STim Blechmann { 76402bec490STim Blechmann int err; 76502bec490STim Blechmann int i; 76602bec490STim Blechmann 76702bec490STim Blechmann snd_printdd("->lx_init_dsp\n"); 76802bec490STim Blechmann 76902bec490STim Blechmann snd_printd(LXP "initialize board\n"); 77002bec490STim Blechmann err = lx_init_xilinx_reset(chip); 77102bec490STim Blechmann if (err) 77202bec490STim Blechmann return err; 77302bec490STim Blechmann 77402bec490STim Blechmann snd_printd(LXP "testing board\n"); 77502bec490STim Blechmann err = lx_init_xilinx_test(chip); 77602bec490STim Blechmann if (err) 77702bec490STim Blechmann return err; 77802bec490STim Blechmann 77902bec490STim Blechmann snd_printd(LXP "initialize ethersound configuration\n"); 78002bec490STim Blechmann err = lx_init_ethersound_config(chip); 78102bec490STim Blechmann if (err) 78202bec490STim Blechmann return err; 78302bec490STim Blechmann 78402bec490STim Blechmann lx_irq_enable(chip); 78502bec490STim Blechmann 78602bec490STim Blechmann /** \todo the mac address should be ready by not, but it isn't, 78702bec490STim Blechmann * so we wait for it */ 78802bec490STim Blechmann for (i = 0; i != 1000; ++i) { 78980b52490STim Blechmann err = lx_dsp_get_mac(chip); 79002bec490STim Blechmann if (err) 79102bec490STim Blechmann return err; 79280b52490STim Blechmann if (chip->mac_address[0] || chip->mac_address[1] || chip->mac_address[2] || 79380b52490STim Blechmann chip->mac_address[3] || chip->mac_address[4] || chip->mac_address[5]) 79402bec490STim Blechmann goto mac_ready; 79502bec490STim Blechmann msleep(1); 79602bec490STim Blechmann } 79702bec490STim Blechmann return -ETIMEDOUT; 79802bec490STim Blechmann 79902bec490STim Blechmann mac_ready: 80002bec490STim Blechmann snd_printd(LXP "mac address ready read after: %dms\n", i); 80102bec490STim Blechmann snd_printk(LXP "mac address: %02X.%02X.%02X.%02X.%02X.%02X\n", 80280b52490STim Blechmann chip->mac_address[0], chip->mac_address[1], chip->mac_address[2], 80380b52490STim Blechmann chip->mac_address[3], chip->mac_address[4], chip->mac_address[5]); 80402bec490STim Blechmann 80502bec490STim Blechmann err = lx_init_get_version_features(chip); 80602bec490STim Blechmann if (err) 80702bec490STim Blechmann return err; 80802bec490STim Blechmann 80902bec490STim Blechmann lx_set_granularity(chip, MICROBLAZE_IBL_DEFAULT); 81002bec490STim Blechmann 81102bec490STim Blechmann chip->playback_mute = 0; 81202bec490STim Blechmann 81302bec490STim Blechmann return err; 81402bec490STim Blechmann } 81502bec490STim Blechmann 81602bec490STim Blechmann static struct snd_pcm_ops lx_ops_playback = { 81702bec490STim Blechmann .open = lx_pcm_open, 81802bec490STim Blechmann .close = lx_pcm_close, 81902bec490STim Blechmann .ioctl = snd_pcm_lib_ioctl, 82002bec490STim Blechmann .prepare = lx_pcm_prepare, 82102bec490STim Blechmann .hw_params = lx_pcm_hw_params_playback, 82202bec490STim Blechmann .hw_free = lx_pcm_hw_free, 82302bec490STim Blechmann .trigger = lx_pcm_trigger, 82402bec490STim Blechmann .pointer = lx_pcm_stream_pointer, 82502bec490STim Blechmann }; 82602bec490STim Blechmann 82702bec490STim Blechmann static struct snd_pcm_ops lx_ops_capture = { 82802bec490STim Blechmann .open = lx_pcm_open, 82902bec490STim Blechmann .close = lx_pcm_close, 83002bec490STim Blechmann .ioctl = snd_pcm_lib_ioctl, 83102bec490STim Blechmann .prepare = lx_pcm_prepare, 83202bec490STim Blechmann .hw_params = lx_pcm_hw_params_capture, 83302bec490STim Blechmann .hw_free = lx_pcm_hw_free, 83402bec490STim Blechmann .trigger = lx_pcm_trigger, 83502bec490STim Blechmann .pointer = lx_pcm_stream_pointer, 83602bec490STim Blechmann }; 83702bec490STim Blechmann 83802bec490STim Blechmann static int __devinit lx_pcm_create(struct lx6464es *chip) 83902bec490STim Blechmann { 84002bec490STim Blechmann int err; 84102bec490STim Blechmann struct snd_pcm *pcm; 84202bec490STim Blechmann 84302bec490STim Blechmann u32 size = 64 * /* channels */ 84402bec490STim Blechmann 3 * /* 24 bit samples */ 84502bec490STim Blechmann MAX_STREAM_BUFFER * /* periods */ 84602bec490STim Blechmann MICROBLAZE_IBL_MAX * /* frames per period */ 84702bec490STim Blechmann 2; /* duplex */ 84802bec490STim Blechmann 84902bec490STim Blechmann size = PAGE_ALIGN(size); 85002bec490STim Blechmann 85102bec490STim Blechmann /* hardcoded device name & channel count */ 85202bec490STim Blechmann err = snd_pcm_new(chip->card, (char *)card_name, 0, 85302bec490STim Blechmann 1, 1, &pcm); 85402bec490STim Blechmann 85502bec490STim Blechmann pcm->private_data = chip; 85602bec490STim Blechmann 85702bec490STim Blechmann snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &lx_ops_playback); 85802bec490STim Blechmann snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &lx_ops_capture); 85902bec490STim Blechmann 86002bec490STim Blechmann pcm->info_flags = 0; 86102bec490STim Blechmann strcpy(pcm->name, card_name); 86202bec490STim Blechmann 86302bec490STim Blechmann err = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, 86402bec490STim Blechmann snd_dma_pci_data(chip->pci), 86502bec490STim Blechmann size, size); 86602bec490STim Blechmann if (err < 0) 86702bec490STim Blechmann return err; 86802bec490STim Blechmann 86902bec490STim Blechmann chip->pcm = pcm; 87002bec490STim Blechmann chip->capture_stream.is_capture = 1; 87102bec490STim Blechmann 87202bec490STim Blechmann return 0; 87302bec490STim Blechmann } 87402bec490STim Blechmann 87502bec490STim Blechmann static int lx_control_playback_info(struct snd_kcontrol *kcontrol, 87602bec490STim Blechmann struct snd_ctl_elem_info *uinfo) 87702bec490STim Blechmann { 87802bec490STim Blechmann uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; 87902bec490STim Blechmann uinfo->count = 1; 88002bec490STim Blechmann uinfo->value.integer.min = 0; 88102bec490STim Blechmann uinfo->value.integer.max = 1; 88202bec490STim Blechmann return 0; 88302bec490STim Blechmann } 88402bec490STim Blechmann 88502bec490STim Blechmann static int lx_control_playback_get(struct snd_kcontrol *kcontrol, 88602bec490STim Blechmann struct snd_ctl_elem_value *ucontrol) 88702bec490STim Blechmann { 88802bec490STim Blechmann struct lx6464es *chip = snd_kcontrol_chip(kcontrol); 88902bec490STim Blechmann ucontrol->value.integer.value[0] = chip->playback_mute; 89002bec490STim Blechmann return 0; 89102bec490STim Blechmann } 89202bec490STim Blechmann 89302bec490STim Blechmann static int lx_control_playback_put(struct snd_kcontrol *kcontrol, 89402bec490STim Blechmann struct snd_ctl_elem_value *ucontrol) 89502bec490STim Blechmann { 89602bec490STim Blechmann struct lx6464es *chip = snd_kcontrol_chip(kcontrol); 89702bec490STim Blechmann int changed = 0; 89802bec490STim Blechmann int current_value = chip->playback_mute; 89902bec490STim Blechmann 90002bec490STim Blechmann if (current_value != ucontrol->value.integer.value[0]) { 90102bec490STim Blechmann lx_level_unmute(chip, 0, !current_value); 90202bec490STim Blechmann chip->playback_mute = !current_value; 90302bec490STim Blechmann changed = 1; 90402bec490STim Blechmann } 90502bec490STim Blechmann return changed; 90602bec490STim Blechmann } 90702bec490STim Blechmann 90802bec490STim Blechmann static struct snd_kcontrol_new lx_control_playback_switch __devinitdata = { 90902bec490STim Blechmann .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 91002bec490STim Blechmann .name = "PCM Playback Switch", 91102bec490STim Blechmann .index = 0, 91202bec490STim Blechmann .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, 91302bec490STim Blechmann .private_value = 0, 91402bec490STim Blechmann .info = lx_control_playback_info, 91502bec490STim Blechmann .get = lx_control_playback_get, 91602bec490STim Blechmann .put = lx_control_playback_put 91702bec490STim Blechmann }; 91802bec490STim Blechmann 91902bec490STim Blechmann 92002bec490STim Blechmann 92102bec490STim Blechmann static void lx_proc_levels_read(struct snd_info_entry *entry, 92202bec490STim Blechmann struct snd_info_buffer *buffer) 92302bec490STim Blechmann { 92402bec490STim Blechmann u32 levels[64]; 92502bec490STim Blechmann int err; 92602bec490STim Blechmann int i, j; 92702bec490STim Blechmann struct lx6464es *chip = entry->private_data; 92802bec490STim Blechmann 92902bec490STim Blechmann snd_iprintf(buffer, "capture levels:\n"); 93002bec490STim Blechmann err = lx_level_peaks(chip, 1, 64, levels); 93102bec490STim Blechmann if (err < 0) 93202bec490STim Blechmann return; 93302bec490STim Blechmann 93402bec490STim Blechmann for (i = 0; i != 8; ++i) { 93502bec490STim Blechmann for (j = 0; j != 8; ++j) 93602bec490STim Blechmann snd_iprintf(buffer, "%08x ", levels[i*8+j]); 93702bec490STim Blechmann snd_iprintf(buffer, "\n"); 93802bec490STim Blechmann } 93902bec490STim Blechmann 94002bec490STim Blechmann snd_iprintf(buffer, "\nplayback levels:\n"); 94102bec490STim Blechmann 94202bec490STim Blechmann err = lx_level_peaks(chip, 0, 64, levels); 94302bec490STim Blechmann if (err < 0) 94402bec490STim Blechmann return; 94502bec490STim Blechmann 94602bec490STim Blechmann for (i = 0; i != 8; ++i) { 94702bec490STim Blechmann for (j = 0; j != 8; ++j) 94802bec490STim Blechmann snd_iprintf(buffer, "%08x ", levels[i*8+j]); 94902bec490STim Blechmann snd_iprintf(buffer, "\n"); 95002bec490STim Blechmann } 95102bec490STim Blechmann 95202bec490STim Blechmann snd_iprintf(buffer, "\n"); 95302bec490STim Blechmann } 95402bec490STim Blechmann 95502bec490STim Blechmann static int __devinit lx_proc_create(struct snd_card *card, struct lx6464es *chip) 95602bec490STim Blechmann { 95702bec490STim Blechmann struct snd_info_entry *entry; 95802bec490STim Blechmann int err = snd_card_proc_new(card, "levels", &entry); 95902bec490STim Blechmann if (err < 0) 96002bec490STim Blechmann return err; 96102bec490STim Blechmann 96202bec490STim Blechmann snd_info_set_text_ops(entry, chip, lx_proc_levels_read); 96302bec490STim Blechmann return 0; 96402bec490STim Blechmann } 96502bec490STim Blechmann 96602bec490STim Blechmann 96702bec490STim Blechmann static int __devinit snd_lx6464es_create(struct snd_card *card, 96802bec490STim Blechmann struct pci_dev *pci, 96902bec490STim Blechmann struct lx6464es **rchip) 97002bec490STim Blechmann { 97102bec490STim Blechmann struct lx6464es *chip; 97202bec490STim Blechmann int err; 97302bec490STim Blechmann 97402bec490STim Blechmann static struct snd_device_ops ops = { 97502bec490STim Blechmann .dev_free = snd_lx6464es_dev_free, 97602bec490STim Blechmann }; 97702bec490STim Blechmann 97802bec490STim Blechmann snd_printdd("->snd_lx6464es_create\n"); 97902bec490STim Blechmann 98002bec490STim Blechmann *rchip = NULL; 98102bec490STim Blechmann 98202bec490STim Blechmann /* enable PCI device */ 98302bec490STim Blechmann err = pci_enable_device(pci); 98402bec490STim Blechmann if (err < 0) 98502bec490STim Blechmann return err; 98602bec490STim Blechmann 98702bec490STim Blechmann pci_set_master(pci); 98802bec490STim Blechmann 98902bec490STim Blechmann /* check if we can restrict PCI DMA transfers to 32 bits */ 9908e20ce94SAndrew Morton err = pci_set_dma_mask(pci, DMA_BIT_MASK(32)); 99102bec490STim Blechmann if (err < 0) { 99202bec490STim Blechmann snd_printk(KERN_ERR "architecture does not support " 99302bec490STim Blechmann "32bit PCI busmaster DMA\n"); 99402bec490STim Blechmann pci_disable_device(pci); 99502bec490STim Blechmann return -ENXIO; 99602bec490STim Blechmann } 99702bec490STim Blechmann 99802bec490STim Blechmann chip = kzalloc(sizeof(*chip), GFP_KERNEL); 99902bec490STim Blechmann if (chip == NULL) { 100002bec490STim Blechmann err = -ENOMEM; 100102bec490STim Blechmann goto alloc_failed; 100202bec490STim Blechmann } 100302bec490STim Blechmann 100402bec490STim Blechmann chip->card = card; 100502bec490STim Blechmann chip->pci = pci; 100602bec490STim Blechmann chip->irq = -1; 100702bec490STim Blechmann 100802bec490STim Blechmann /* initialize synchronization structs */ 100902bec490STim Blechmann spin_lock_init(&chip->lock); 101002bec490STim Blechmann spin_lock_init(&chip->msg_lock); 101102bec490STim Blechmann mutex_init(&chip->setup_mutex); 101202bec490STim Blechmann tasklet_init(&chip->trigger_tasklet, lx_trigger_tasklet, 101302bec490STim Blechmann (unsigned long)chip); 101402bec490STim Blechmann tasklet_init(&chip->tasklet_capture, lx_tasklet_capture, 101502bec490STim Blechmann (unsigned long)chip); 101602bec490STim Blechmann tasklet_init(&chip->tasklet_playback, lx_tasklet_playback, 101702bec490STim Blechmann (unsigned long)chip); 101802bec490STim Blechmann 101902bec490STim Blechmann /* request resources */ 102002bec490STim Blechmann err = pci_request_regions(pci, card_name); 102102bec490STim Blechmann if (err < 0) 102202bec490STim Blechmann goto request_regions_failed; 102302bec490STim Blechmann 102402bec490STim Blechmann /* plx port */ 102502bec490STim Blechmann chip->port_plx = pci_resource_start(pci, 1); 102602bec490STim Blechmann chip->port_plx_remapped = ioport_map(chip->port_plx, 102702bec490STim Blechmann pci_resource_len(pci, 1)); 102802bec490STim Blechmann 102902bec490STim Blechmann /* dsp port */ 103002bec490STim Blechmann chip->port_dsp_bar = pci_ioremap_bar(pci, 2); 103102bec490STim Blechmann 103202bec490STim Blechmann err = request_irq(pci->irq, lx_interrupt, IRQF_SHARED, 1033934c2b6dSTakashi Iwai KBUILD_MODNAME, chip); 103402bec490STim Blechmann if (err) { 103502bec490STim Blechmann snd_printk(KERN_ERR LXP "unable to grab IRQ %d\n", pci->irq); 103602bec490STim Blechmann goto request_irq_failed; 103702bec490STim Blechmann } 103802bec490STim Blechmann chip->irq = pci->irq; 103902bec490STim Blechmann 104002bec490STim Blechmann err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops); 104102bec490STim Blechmann if (err < 0) 104202bec490STim Blechmann goto device_new_failed; 104302bec490STim Blechmann 104402bec490STim Blechmann err = lx_init_dsp(chip); 104502bec490STim Blechmann if (err < 0) { 104602bec490STim Blechmann snd_printk(KERN_ERR LXP "error during DSP initialization\n"); 104702bec490STim Blechmann return err; 104802bec490STim Blechmann } 104902bec490STim Blechmann 105002bec490STim Blechmann err = lx_pcm_create(chip); 105102bec490STim Blechmann if (err < 0) 105202bec490STim Blechmann return err; 105302bec490STim Blechmann 105402bec490STim Blechmann err = lx_proc_create(card, chip); 105502bec490STim Blechmann if (err < 0) 105602bec490STim Blechmann return err; 105702bec490STim Blechmann 105802bec490STim Blechmann err = snd_ctl_add(card, snd_ctl_new1(&lx_control_playback_switch, 105902bec490STim Blechmann chip)); 106002bec490STim Blechmann if (err < 0) 106102bec490STim Blechmann return err; 106202bec490STim Blechmann 106302bec490STim Blechmann snd_card_set_dev(card, &pci->dev); 106402bec490STim Blechmann 106502bec490STim Blechmann *rchip = chip; 106602bec490STim Blechmann return 0; 106702bec490STim Blechmann 106802bec490STim Blechmann device_new_failed: 106902bec490STim Blechmann free_irq(pci->irq, chip); 107002bec490STim Blechmann 107102bec490STim Blechmann request_irq_failed: 107202bec490STim Blechmann pci_release_regions(pci); 107302bec490STim Blechmann 107402bec490STim Blechmann request_regions_failed: 107502bec490STim Blechmann kfree(chip); 107602bec490STim Blechmann 107702bec490STim Blechmann alloc_failed: 107802bec490STim Blechmann pci_disable_device(pci); 107902bec490STim Blechmann 108002bec490STim Blechmann return err; 108102bec490STim Blechmann } 108202bec490STim Blechmann 108302bec490STim Blechmann static int __devinit snd_lx6464es_probe(struct pci_dev *pci, 108402bec490STim Blechmann const struct pci_device_id *pci_id) 108502bec490STim Blechmann { 108602bec490STim Blechmann static int dev; 108702bec490STim Blechmann struct snd_card *card; 108802bec490STim Blechmann struct lx6464es *chip; 108902bec490STim Blechmann int err; 109002bec490STim Blechmann 109102bec490STim Blechmann snd_printdd("->snd_lx6464es_probe\n"); 109202bec490STim Blechmann 109302bec490STim Blechmann if (dev >= SNDRV_CARDS) 109402bec490STim Blechmann return -ENODEV; 109502bec490STim Blechmann if (!enable[dev]) { 109602bec490STim Blechmann dev++; 109702bec490STim Blechmann return -ENOENT; 109802bec490STim Blechmann } 109902bec490STim Blechmann 11007852fd08STakashi Iwai err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card); 11017852fd08STakashi Iwai if (err < 0) 11027852fd08STakashi Iwai return err; 110302bec490STim Blechmann 110402bec490STim Blechmann err = snd_lx6464es_create(card, pci, &chip); 110502bec490STim Blechmann if (err < 0) { 110602bec490STim Blechmann snd_printk(KERN_ERR LXP "error during snd_lx6464es_create\n"); 110702bec490STim Blechmann goto out_free; 110802bec490STim Blechmann } 110902bec490STim Blechmann 111080b52490STim Blechmann strcpy(card->driver, "LX6464ES"); 111180b52490STim Blechmann sprintf(card->id, "LX6464ES_%02X%02X%02X", 111280b52490STim Blechmann chip->mac_address[3], chip->mac_address[4], chip->mac_address[5]); 111380b52490STim Blechmann 111480b52490STim Blechmann sprintf(card->shortname, "LX6464ES %02X.%02X.%02X.%02X.%02X.%02X", 111580b52490STim Blechmann chip->mac_address[0], chip->mac_address[1], chip->mac_address[2], 111680b52490STim Blechmann chip->mac_address[3], chip->mac_address[4], chip->mac_address[5]); 111780b52490STim Blechmann 111802bec490STim Blechmann sprintf(card->longname, "%s at 0x%lx, 0x%p, irq %i", 111902bec490STim Blechmann card->shortname, chip->port_plx, 112002bec490STim Blechmann chip->port_dsp_bar, chip->irq); 112102bec490STim Blechmann 112202bec490STim Blechmann err = snd_card_register(card); 112302bec490STim Blechmann if (err < 0) 112402bec490STim Blechmann goto out_free; 112502bec490STim Blechmann 112602bec490STim Blechmann snd_printdd(LXP "initialization successful\n"); 112702bec490STim Blechmann pci_set_drvdata(pci, card); 112802bec490STim Blechmann dev++; 112902bec490STim Blechmann return 0; 113002bec490STim Blechmann 113102bec490STim Blechmann out_free: 113202bec490STim Blechmann snd_card_free(card); 113302bec490STim Blechmann return err; 113402bec490STim Blechmann 113502bec490STim Blechmann } 113602bec490STim Blechmann 113702bec490STim Blechmann static void __devexit snd_lx6464es_remove(struct pci_dev *pci) 113802bec490STim Blechmann { 113902bec490STim Blechmann snd_card_free(pci_get_drvdata(pci)); 114002bec490STim Blechmann pci_set_drvdata(pci, NULL); 114102bec490STim Blechmann } 114202bec490STim Blechmann 114302bec490STim Blechmann 1144e9f66d9bSTakashi Iwai static struct pci_driver lx6464es_driver = { 11453733e424STakashi Iwai .name = KBUILD_MODNAME, 114602bec490STim Blechmann .id_table = snd_lx6464es_ids, 114702bec490STim Blechmann .probe = snd_lx6464es_probe, 114802bec490STim Blechmann .remove = __devexit_p(snd_lx6464es_remove), 114902bec490STim Blechmann }; 115002bec490STim Blechmann 1151e9f66d9bSTakashi Iwai module_pci_driver(lx6464es_driver); 1152