18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* -*- linux-c -*- * 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * ALSA driver for the digigram lx6464es interface 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright (c) 2008, 2009 Tim Blechmann <tim@klingt.org> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/init.h> 118c2ecf20Sopenharmony_ci#include <linux/pci.h> 128c2ecf20Sopenharmony_ci#include <linux/delay.h> 138c2ecf20Sopenharmony_ci#include <linux/slab.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include <sound/initval.h> 168c2ecf20Sopenharmony_ci#include <sound/control.h> 178c2ecf20Sopenharmony_ci#include <sound/info.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include "lx6464es.h" 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ciMODULE_AUTHOR("Tim Blechmann"); 228c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 238c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("digigram lx6464es"); 248c2ecf20Sopenharmony_ciMODULE_SUPPORTED_DEVICE("{digigram lx6464es{}}"); 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistatic int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; 288c2ecf20Sopenharmony_cistatic char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; 298c2ecf20Sopenharmony_cistatic bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cimodule_param_array(index, int, NULL, 0444); 328c2ecf20Sopenharmony_ciMODULE_PARM_DESC(index, "Index value for Digigram LX6464ES interface."); 338c2ecf20Sopenharmony_cimodule_param_array(id, charp, NULL, 0444); 348c2ecf20Sopenharmony_ciMODULE_PARM_DESC(id, "ID string for Digigram LX6464ES interface."); 358c2ecf20Sopenharmony_cimodule_param_array(enable, bool, NULL, 0444); 368c2ecf20Sopenharmony_ciMODULE_PARM_DESC(enable, "Enable/disable specific Digigram LX6464ES soundcards."); 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic const char card_name[] = "LX6464ES"; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci#define PCI_DEVICE_ID_PLX_LX6464ES PCI_DEVICE_ID_PLX_9056 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic const struct pci_device_id snd_lx6464es_ids[] = { 448c2ecf20Sopenharmony_ci { PCI_DEVICE_SUB(PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_LX6464ES, 458c2ecf20Sopenharmony_ci PCI_VENDOR_ID_DIGIGRAM, 468c2ecf20Sopenharmony_ci PCI_SUBDEVICE_ID_DIGIGRAM_LX6464ES_SERIAL_SUBSYSTEM), 478c2ecf20Sopenharmony_ci }, /* LX6464ES */ 488c2ecf20Sopenharmony_ci { PCI_DEVICE_SUB(PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_LX6464ES, 498c2ecf20Sopenharmony_ci PCI_VENDOR_ID_DIGIGRAM, 508c2ecf20Sopenharmony_ci PCI_SUBDEVICE_ID_DIGIGRAM_LX6464ES_CAE_SERIAL_SUBSYSTEM), 518c2ecf20Sopenharmony_ci }, /* LX6464ES-CAE */ 528c2ecf20Sopenharmony_ci { PCI_DEVICE_SUB(PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_LX6464ES, 538c2ecf20Sopenharmony_ci PCI_VENDOR_ID_DIGIGRAM, 548c2ecf20Sopenharmony_ci PCI_SUBDEVICE_ID_DIGIGRAM_LX6464ESE_SERIAL_SUBSYSTEM), 558c2ecf20Sopenharmony_ci }, /* LX6464ESe */ 568c2ecf20Sopenharmony_ci { PCI_DEVICE_SUB(PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_LX6464ES, 578c2ecf20Sopenharmony_ci PCI_VENDOR_ID_DIGIGRAM, 588c2ecf20Sopenharmony_ci PCI_SUBDEVICE_ID_DIGIGRAM_LX6464ESE_CAE_SERIAL_SUBSYSTEM), 598c2ecf20Sopenharmony_ci }, /* LX6464ESe-CAE */ 608c2ecf20Sopenharmony_ci { 0, }, 618c2ecf20Sopenharmony_ci}; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, snd_lx6464es_ids); 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci/* PGO pour USERo dans le registre pci_0x06/loc_0xEC */ 688c2ecf20Sopenharmony_ci#define CHIPSC_RESET_XILINX (1L<<16) 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci/* alsa callbacks */ 728c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware lx_caps = { 738c2ecf20Sopenharmony_ci .info = (SNDRV_PCM_INFO_MMAP | 748c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_INTERLEAVED | 758c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID | 768c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_SYNC_START), 778c2ecf20Sopenharmony_ci .formats = (SNDRV_PCM_FMTBIT_S16_LE | 788c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S16_BE | 798c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_3LE | 808c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_3BE), 818c2ecf20Sopenharmony_ci .rates = (SNDRV_PCM_RATE_CONTINUOUS | 828c2ecf20Sopenharmony_ci SNDRV_PCM_RATE_8000_192000), 838c2ecf20Sopenharmony_ci .rate_min = 8000, 848c2ecf20Sopenharmony_ci .rate_max = 192000, 858c2ecf20Sopenharmony_ci .channels_min = 2, 868c2ecf20Sopenharmony_ci .channels_max = 64, 878c2ecf20Sopenharmony_ci .buffer_bytes_max = 64*2*3*MICROBLAZE_IBL_MAX*MAX_STREAM_BUFFER, 888c2ecf20Sopenharmony_ci .period_bytes_min = (2*2*MICROBLAZE_IBL_MIN*2), 898c2ecf20Sopenharmony_ci .period_bytes_max = (4*64*MICROBLAZE_IBL_MAX*MAX_STREAM_BUFFER), 908c2ecf20Sopenharmony_ci .periods_min = 2, 918c2ecf20Sopenharmony_ci .periods_max = MAX_STREAM_BUFFER, 928c2ecf20Sopenharmony_ci}; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic int lx_set_granularity(struct lx6464es *chip, u32 gran); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cistatic int lx_hardware_open(struct lx6464es *chip, 988c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci int err = 0; 1018c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 1028c2ecf20Sopenharmony_ci int channels = runtime->channels; 1038c2ecf20Sopenharmony_ci int is_capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci snd_pcm_uframes_t period_size = runtime->period_size; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, "allocating pipe for %d channels\n", channels); 1088c2ecf20Sopenharmony_ci err = lx_pipe_allocate(chip, 0, is_capture, channels); 1098c2ecf20Sopenharmony_ci if (err < 0) { 1108c2ecf20Sopenharmony_ci dev_err(chip->card->dev, LXP "allocating pipe failed\n"); 1118c2ecf20Sopenharmony_ci return err; 1128c2ecf20Sopenharmony_ci } 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci err = lx_set_granularity(chip, period_size); 1158c2ecf20Sopenharmony_ci if (err < 0) { 1168c2ecf20Sopenharmony_ci dev_err(chip->card->dev, "setting granularity to %ld failed\n", 1178c2ecf20Sopenharmony_ci period_size); 1188c2ecf20Sopenharmony_ci return err; 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci return 0; 1228c2ecf20Sopenharmony_ci} 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_cistatic int lx_hardware_start(struct lx6464es *chip, 1258c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci int err = 0; 1288c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 1298c2ecf20Sopenharmony_ci int is_capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, "setting stream format\n"); 1328c2ecf20Sopenharmony_ci err = lx_stream_set_format(chip, runtime, 0, is_capture); 1338c2ecf20Sopenharmony_ci if (err < 0) { 1348c2ecf20Sopenharmony_ci dev_err(chip->card->dev, "setting stream format failed\n"); 1358c2ecf20Sopenharmony_ci return err; 1368c2ecf20Sopenharmony_ci } 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, "starting pipe\n"); 1398c2ecf20Sopenharmony_ci err = lx_pipe_start(chip, 0, is_capture); 1408c2ecf20Sopenharmony_ci if (err < 0) { 1418c2ecf20Sopenharmony_ci dev_err(chip->card->dev, "starting pipe failed\n"); 1428c2ecf20Sopenharmony_ci return err; 1438c2ecf20Sopenharmony_ci } 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, "waiting for pipe to start\n"); 1468c2ecf20Sopenharmony_ci err = lx_pipe_wait_for_start(chip, 0, is_capture); 1478c2ecf20Sopenharmony_ci if (err < 0) { 1488c2ecf20Sopenharmony_ci dev_err(chip->card->dev, "waiting for pipe failed\n"); 1498c2ecf20Sopenharmony_ci return err; 1508c2ecf20Sopenharmony_ci } 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci return err; 1538c2ecf20Sopenharmony_ci} 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistatic int lx_hardware_stop(struct lx6464es *chip, 1578c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 1588c2ecf20Sopenharmony_ci{ 1598c2ecf20Sopenharmony_ci int err = 0; 1608c2ecf20Sopenharmony_ci int is_capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, "pausing pipe\n"); 1638c2ecf20Sopenharmony_ci err = lx_pipe_pause(chip, 0, is_capture); 1648c2ecf20Sopenharmony_ci if (err < 0) { 1658c2ecf20Sopenharmony_ci dev_err(chip->card->dev, "pausing pipe failed\n"); 1668c2ecf20Sopenharmony_ci return err; 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, "waiting for pipe to become idle\n"); 1708c2ecf20Sopenharmony_ci err = lx_pipe_wait_for_idle(chip, 0, is_capture); 1718c2ecf20Sopenharmony_ci if (err < 0) { 1728c2ecf20Sopenharmony_ci dev_err(chip->card->dev, "waiting for pipe failed\n"); 1738c2ecf20Sopenharmony_ci return err; 1748c2ecf20Sopenharmony_ci } 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, "stopping pipe\n"); 1778c2ecf20Sopenharmony_ci err = lx_pipe_stop(chip, 0, is_capture); 1788c2ecf20Sopenharmony_ci if (err < 0) { 1798c2ecf20Sopenharmony_ci dev_err(chip->card->dev, "stopping pipe failed\n"); 1808c2ecf20Sopenharmony_ci return err; 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci return err; 1848c2ecf20Sopenharmony_ci} 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_cistatic int lx_hardware_close(struct lx6464es *chip, 1888c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 1898c2ecf20Sopenharmony_ci{ 1908c2ecf20Sopenharmony_ci int err = 0; 1918c2ecf20Sopenharmony_ci int is_capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, "releasing pipe\n"); 1948c2ecf20Sopenharmony_ci err = lx_pipe_release(chip, 0, is_capture); 1958c2ecf20Sopenharmony_ci if (err < 0) { 1968c2ecf20Sopenharmony_ci dev_err(chip->card->dev, "releasing pipe failed\n"); 1978c2ecf20Sopenharmony_ci return err; 1988c2ecf20Sopenharmony_ci } 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci return err; 2018c2ecf20Sopenharmony_ci} 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_cistatic int lx_pcm_open(struct snd_pcm_substream *substream) 2058c2ecf20Sopenharmony_ci{ 2068c2ecf20Sopenharmony_ci struct lx6464es *chip = snd_pcm_substream_chip(substream); 2078c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 2088c2ecf20Sopenharmony_ci int err = 0; 2098c2ecf20Sopenharmony_ci int board_rate; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, "->lx_pcm_open\n"); 2128c2ecf20Sopenharmony_ci mutex_lock(&chip->setup_mutex); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci /* copy the struct snd_pcm_hardware struct */ 2158c2ecf20Sopenharmony_ci runtime->hw = lx_caps; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci#if 0 2188c2ecf20Sopenharmony_ci /* buffer-size should better be multiple of period-size */ 2198c2ecf20Sopenharmony_ci err = snd_pcm_hw_constraint_integer(runtime, 2208c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_PERIODS); 2218c2ecf20Sopenharmony_ci if (err < 0) { 2228c2ecf20Sopenharmony_ci dev_warn(chip->card->dev, "could not constrain periods\n"); 2238c2ecf20Sopenharmony_ci goto exit; 2248c2ecf20Sopenharmony_ci } 2258c2ecf20Sopenharmony_ci#endif 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci /* the clock rate cannot be changed */ 2288c2ecf20Sopenharmony_ci board_rate = chip->board_sample_rate; 2298c2ecf20Sopenharmony_ci err = snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_RATE, 2308c2ecf20Sopenharmony_ci board_rate); 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci if (err < 0) { 2338c2ecf20Sopenharmony_ci dev_warn(chip->card->dev, "could not constrain periods\n"); 2348c2ecf20Sopenharmony_ci goto exit; 2358c2ecf20Sopenharmony_ci } 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci /* constrain period size */ 2388c2ecf20Sopenharmony_ci err = snd_pcm_hw_constraint_minmax(runtime, 2398c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 2408c2ecf20Sopenharmony_ci MICROBLAZE_IBL_MIN, 2418c2ecf20Sopenharmony_ci MICROBLAZE_IBL_MAX); 2428c2ecf20Sopenharmony_ci if (err < 0) { 2438c2ecf20Sopenharmony_ci dev_warn(chip->card->dev, 2448c2ecf20Sopenharmony_ci "could not constrain period size\n"); 2458c2ecf20Sopenharmony_ci goto exit; 2468c2ecf20Sopenharmony_ci } 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci snd_pcm_hw_constraint_step(runtime, 0, 2498c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 32); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci snd_pcm_set_sync(substream); 2528c2ecf20Sopenharmony_ci err = 0; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ciexit: 2558c2ecf20Sopenharmony_ci runtime->private_data = chip; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci mutex_unlock(&chip->setup_mutex); 2588c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, "<-lx_pcm_open, %d\n", err); 2598c2ecf20Sopenharmony_ci return err; 2608c2ecf20Sopenharmony_ci} 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_cistatic int lx_pcm_close(struct snd_pcm_substream *substream) 2638c2ecf20Sopenharmony_ci{ 2648c2ecf20Sopenharmony_ci dev_dbg(substream->pcm->card->dev, "->lx_pcm_close\n"); 2658c2ecf20Sopenharmony_ci return 0; 2668c2ecf20Sopenharmony_ci} 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t lx_pcm_stream_pointer(struct snd_pcm_substream 2698c2ecf20Sopenharmony_ci *substream) 2708c2ecf20Sopenharmony_ci{ 2718c2ecf20Sopenharmony_ci struct lx6464es *chip = snd_pcm_substream_chip(substream); 2728c2ecf20Sopenharmony_ci snd_pcm_uframes_t pos; 2738c2ecf20Sopenharmony_ci int is_capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE); 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci struct lx_stream *lx_stream = is_capture ? &chip->capture_stream : 2768c2ecf20Sopenharmony_ci &chip->playback_stream; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, "->lx_pcm_stream_pointer\n"); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci mutex_lock(&chip->lock); 2818c2ecf20Sopenharmony_ci pos = lx_stream->frame_pos * substream->runtime->period_size; 2828c2ecf20Sopenharmony_ci mutex_unlock(&chip->lock); 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, "stream_pointer at %ld\n", pos); 2858c2ecf20Sopenharmony_ci return pos; 2868c2ecf20Sopenharmony_ci} 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_cistatic int lx_pcm_prepare(struct snd_pcm_substream *substream) 2898c2ecf20Sopenharmony_ci{ 2908c2ecf20Sopenharmony_ci struct lx6464es *chip = snd_pcm_substream_chip(substream); 2918c2ecf20Sopenharmony_ci int err = 0; 2928c2ecf20Sopenharmony_ci const int is_capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, "->lx_pcm_prepare\n"); 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci mutex_lock(&chip->setup_mutex); 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci if (chip->hardware_running[is_capture]) { 2998c2ecf20Sopenharmony_ci err = lx_hardware_stop(chip, substream); 3008c2ecf20Sopenharmony_ci if (err < 0) { 3018c2ecf20Sopenharmony_ci dev_err(chip->card->dev, "failed to stop hardware. " 3028c2ecf20Sopenharmony_ci "Error code %d\n", err); 3038c2ecf20Sopenharmony_ci goto exit; 3048c2ecf20Sopenharmony_ci } 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci err = lx_hardware_close(chip, substream); 3078c2ecf20Sopenharmony_ci if (err < 0) { 3088c2ecf20Sopenharmony_ci dev_err(chip->card->dev, "failed to close hardware. " 3098c2ecf20Sopenharmony_ci "Error code %d\n", err); 3108c2ecf20Sopenharmony_ci goto exit; 3118c2ecf20Sopenharmony_ci } 3128c2ecf20Sopenharmony_ci } 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, "opening hardware\n"); 3158c2ecf20Sopenharmony_ci err = lx_hardware_open(chip, substream); 3168c2ecf20Sopenharmony_ci if (err < 0) { 3178c2ecf20Sopenharmony_ci dev_err(chip->card->dev, "failed to open hardware. " 3188c2ecf20Sopenharmony_ci "Error code %d\n", err); 3198c2ecf20Sopenharmony_ci goto exit; 3208c2ecf20Sopenharmony_ci } 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci err = lx_hardware_start(chip, substream); 3238c2ecf20Sopenharmony_ci if (err < 0) { 3248c2ecf20Sopenharmony_ci dev_err(chip->card->dev, "failed to start hardware. " 3258c2ecf20Sopenharmony_ci "Error code %d\n", err); 3268c2ecf20Sopenharmony_ci goto exit; 3278c2ecf20Sopenharmony_ci } 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci chip->hardware_running[is_capture] = 1; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci if (chip->board_sample_rate != substream->runtime->rate) { 3328c2ecf20Sopenharmony_ci if (!err) 3338c2ecf20Sopenharmony_ci chip->board_sample_rate = substream->runtime->rate; 3348c2ecf20Sopenharmony_ci } 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ciexit: 3378c2ecf20Sopenharmony_ci mutex_unlock(&chip->setup_mutex); 3388c2ecf20Sopenharmony_ci return err; 3398c2ecf20Sopenharmony_ci} 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_cistatic int lx_pcm_hw_params(struct snd_pcm_substream *substream, 3428c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *hw_params, int is_capture) 3438c2ecf20Sopenharmony_ci{ 3448c2ecf20Sopenharmony_ci struct lx6464es *chip = snd_pcm_substream_chip(substream); 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, "->lx_pcm_hw_params\n"); 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci mutex_lock(&chip->setup_mutex); 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci if (is_capture) 3518c2ecf20Sopenharmony_ci chip->capture_stream.stream = substream; 3528c2ecf20Sopenharmony_ci else 3538c2ecf20Sopenharmony_ci chip->playback_stream.stream = substream; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci mutex_unlock(&chip->setup_mutex); 3568c2ecf20Sopenharmony_ci return 0; 3578c2ecf20Sopenharmony_ci} 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_cistatic int lx_pcm_hw_params_playback(struct snd_pcm_substream *substream, 3608c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *hw_params) 3618c2ecf20Sopenharmony_ci{ 3628c2ecf20Sopenharmony_ci return lx_pcm_hw_params(substream, hw_params, 0); 3638c2ecf20Sopenharmony_ci} 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_cistatic int lx_pcm_hw_params_capture(struct snd_pcm_substream *substream, 3668c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *hw_params) 3678c2ecf20Sopenharmony_ci{ 3688c2ecf20Sopenharmony_ci return lx_pcm_hw_params(substream, hw_params, 1); 3698c2ecf20Sopenharmony_ci} 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_cistatic int lx_pcm_hw_free(struct snd_pcm_substream *substream) 3728c2ecf20Sopenharmony_ci{ 3738c2ecf20Sopenharmony_ci struct lx6464es *chip = snd_pcm_substream_chip(substream); 3748c2ecf20Sopenharmony_ci int err = 0; 3758c2ecf20Sopenharmony_ci int is_capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE); 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, "->lx_pcm_hw_free\n"); 3788c2ecf20Sopenharmony_ci mutex_lock(&chip->setup_mutex); 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci if (chip->hardware_running[is_capture]) { 3818c2ecf20Sopenharmony_ci err = lx_hardware_stop(chip, substream); 3828c2ecf20Sopenharmony_ci if (err < 0) { 3838c2ecf20Sopenharmony_ci dev_err(chip->card->dev, "failed to stop hardware. " 3848c2ecf20Sopenharmony_ci "Error code %d\n", err); 3858c2ecf20Sopenharmony_ci goto exit; 3868c2ecf20Sopenharmony_ci } 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci err = lx_hardware_close(chip, substream); 3898c2ecf20Sopenharmony_ci if (err < 0) { 3908c2ecf20Sopenharmony_ci dev_err(chip->card->dev, "failed to close hardware. " 3918c2ecf20Sopenharmony_ci "Error code %d\n", err); 3928c2ecf20Sopenharmony_ci goto exit; 3938c2ecf20Sopenharmony_ci } 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci chip->hardware_running[is_capture] = 0; 3968c2ecf20Sopenharmony_ci } 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci if (is_capture) 3998c2ecf20Sopenharmony_ci chip->capture_stream.stream = NULL; 4008c2ecf20Sopenharmony_ci else 4018c2ecf20Sopenharmony_ci chip->playback_stream.stream = NULL; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ciexit: 4048c2ecf20Sopenharmony_ci mutex_unlock(&chip->setup_mutex); 4058c2ecf20Sopenharmony_ci return err; 4068c2ecf20Sopenharmony_ci} 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_cistatic void lx_trigger_start(struct lx6464es *chip, struct lx_stream *lx_stream) 4098c2ecf20Sopenharmony_ci{ 4108c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream = lx_stream->stream; 4118c2ecf20Sopenharmony_ci const unsigned int is_capture = lx_stream->is_capture; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci int err; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci const u32 channels = substream->runtime->channels; 4168c2ecf20Sopenharmony_ci const u32 bytes_per_frame = channels * 3; 4178c2ecf20Sopenharmony_ci const u32 period_size = substream->runtime->period_size; 4188c2ecf20Sopenharmony_ci const u32 periods = substream->runtime->periods; 4198c2ecf20Sopenharmony_ci const u32 period_bytes = period_size * bytes_per_frame; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci dma_addr_t buf = substream->dma_buffer.addr; 4228c2ecf20Sopenharmony_ci int i; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci u32 needed, freed; 4258c2ecf20Sopenharmony_ci u32 size_array[5]; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci for (i = 0; i != periods; ++i) { 4288c2ecf20Sopenharmony_ci u32 buffer_index = 0; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci err = lx_buffer_ask(chip, 0, is_capture, &needed, &freed, 4318c2ecf20Sopenharmony_ci size_array); 4328c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, "starting: needed %d, freed %d\n", 4338c2ecf20Sopenharmony_ci needed, freed); 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci err = lx_buffer_give(chip, 0, is_capture, period_bytes, 4368c2ecf20Sopenharmony_ci lower_32_bits(buf), upper_32_bits(buf), 4378c2ecf20Sopenharmony_ci &buffer_index); 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, "starting: buffer index %x on 0x%lx (%d bytes)\n", 4408c2ecf20Sopenharmony_ci buffer_index, (unsigned long)buf, period_bytes); 4418c2ecf20Sopenharmony_ci buf += period_bytes; 4428c2ecf20Sopenharmony_ci } 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci err = lx_buffer_ask(chip, 0, is_capture, &needed, &freed, size_array); 4458c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, "starting: needed %d, freed %d\n", needed, freed); 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, "starting: starting stream\n"); 4488c2ecf20Sopenharmony_ci err = lx_stream_start(chip, 0, is_capture); 4498c2ecf20Sopenharmony_ci if (err < 0) 4508c2ecf20Sopenharmony_ci dev_err(chip->card->dev, "couldn't start stream\n"); 4518c2ecf20Sopenharmony_ci else 4528c2ecf20Sopenharmony_ci lx_stream->status = LX_STREAM_STATUS_RUNNING; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci lx_stream->frame_pos = 0; 4558c2ecf20Sopenharmony_ci} 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_cistatic void lx_trigger_stop(struct lx6464es *chip, struct lx_stream *lx_stream) 4588c2ecf20Sopenharmony_ci{ 4598c2ecf20Sopenharmony_ci const unsigned int is_capture = lx_stream->is_capture; 4608c2ecf20Sopenharmony_ci int err; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, "stopping: stopping stream\n"); 4638c2ecf20Sopenharmony_ci err = lx_stream_stop(chip, 0, is_capture); 4648c2ecf20Sopenharmony_ci if (err < 0) 4658c2ecf20Sopenharmony_ci dev_err(chip->card->dev, "couldn't stop stream\n"); 4668c2ecf20Sopenharmony_ci else 4678c2ecf20Sopenharmony_ci lx_stream->status = LX_STREAM_STATUS_FREE; 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci} 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_cistatic void lx_trigger_dispatch_stream(struct lx6464es *chip, 4728c2ecf20Sopenharmony_ci struct lx_stream *lx_stream) 4738c2ecf20Sopenharmony_ci{ 4748c2ecf20Sopenharmony_ci switch (lx_stream->status) { 4758c2ecf20Sopenharmony_ci case LX_STREAM_STATUS_SCHEDULE_RUN: 4768c2ecf20Sopenharmony_ci lx_trigger_start(chip, lx_stream); 4778c2ecf20Sopenharmony_ci break; 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci case LX_STREAM_STATUS_SCHEDULE_STOP: 4808c2ecf20Sopenharmony_ci lx_trigger_stop(chip, lx_stream); 4818c2ecf20Sopenharmony_ci break; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci default: 4848c2ecf20Sopenharmony_ci break; 4858c2ecf20Sopenharmony_ci } 4868c2ecf20Sopenharmony_ci} 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_cistatic int lx_pcm_trigger_dispatch(struct lx6464es *chip, 4898c2ecf20Sopenharmony_ci struct lx_stream *lx_stream, int cmd) 4908c2ecf20Sopenharmony_ci{ 4918c2ecf20Sopenharmony_ci int err = 0; 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci mutex_lock(&chip->lock); 4948c2ecf20Sopenharmony_ci switch (cmd) { 4958c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 4968c2ecf20Sopenharmony_ci lx_stream->status = LX_STREAM_STATUS_SCHEDULE_RUN; 4978c2ecf20Sopenharmony_ci break; 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 5008c2ecf20Sopenharmony_ci lx_stream->status = LX_STREAM_STATUS_SCHEDULE_STOP; 5018c2ecf20Sopenharmony_ci break; 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci default: 5048c2ecf20Sopenharmony_ci err = -EINVAL; 5058c2ecf20Sopenharmony_ci goto exit; 5068c2ecf20Sopenharmony_ci } 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci lx_trigger_dispatch_stream(chip, &chip->capture_stream); 5098c2ecf20Sopenharmony_ci lx_trigger_dispatch_stream(chip, &chip->playback_stream); 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ciexit: 5128c2ecf20Sopenharmony_ci mutex_unlock(&chip->lock); 5138c2ecf20Sopenharmony_ci return err; 5148c2ecf20Sopenharmony_ci} 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_cistatic int lx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) 5188c2ecf20Sopenharmony_ci{ 5198c2ecf20Sopenharmony_ci struct lx6464es *chip = snd_pcm_substream_chip(substream); 5208c2ecf20Sopenharmony_ci const int is_capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE); 5218c2ecf20Sopenharmony_ci struct lx_stream *stream = is_capture ? &chip->capture_stream : 5228c2ecf20Sopenharmony_ci &chip->playback_stream; 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, "->lx_pcm_trigger\n"); 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci return lx_pcm_trigger_dispatch(chip, stream, cmd); 5278c2ecf20Sopenharmony_ci} 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_cistatic int snd_lx6464es_free(struct lx6464es *chip) 5308c2ecf20Sopenharmony_ci{ 5318c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, "->snd_lx6464es_free\n"); 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci lx_irq_disable(chip); 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci if (chip->irq >= 0) 5368c2ecf20Sopenharmony_ci free_irq(chip->irq, chip); 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci iounmap(chip->port_dsp_bar); 5398c2ecf20Sopenharmony_ci ioport_unmap(chip->port_plx_remapped); 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci pci_release_regions(chip->pci); 5428c2ecf20Sopenharmony_ci pci_disable_device(chip->pci); 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci kfree(chip); 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci return 0; 5478c2ecf20Sopenharmony_ci} 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_cistatic int snd_lx6464es_dev_free(struct snd_device *device) 5508c2ecf20Sopenharmony_ci{ 5518c2ecf20Sopenharmony_ci return snd_lx6464es_free(device->device_data); 5528c2ecf20Sopenharmony_ci} 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci/* reset the dsp during initialization */ 5558c2ecf20Sopenharmony_cistatic int lx_init_xilinx_reset(struct lx6464es *chip) 5568c2ecf20Sopenharmony_ci{ 5578c2ecf20Sopenharmony_ci int i; 5588c2ecf20Sopenharmony_ci u32 plx_reg = lx_plx_reg_read(chip, ePLX_CHIPSC); 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, "->lx_init_xilinx_reset\n"); 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci /* activate reset of xilinx */ 5638c2ecf20Sopenharmony_ci plx_reg &= ~CHIPSC_RESET_XILINX; 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci lx_plx_reg_write(chip, ePLX_CHIPSC, plx_reg); 5668c2ecf20Sopenharmony_ci msleep(1); 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci lx_plx_reg_write(chip, ePLX_MBOX3, 0); 5698c2ecf20Sopenharmony_ci msleep(1); 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci plx_reg |= CHIPSC_RESET_XILINX; 5728c2ecf20Sopenharmony_ci lx_plx_reg_write(chip, ePLX_CHIPSC, plx_reg); 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci /* deactivate reset of xilinx */ 5758c2ecf20Sopenharmony_ci for (i = 0; i != 100; ++i) { 5768c2ecf20Sopenharmony_ci u32 reg_mbox3; 5778c2ecf20Sopenharmony_ci msleep(10); 5788c2ecf20Sopenharmony_ci reg_mbox3 = lx_plx_reg_read(chip, ePLX_MBOX3); 5798c2ecf20Sopenharmony_ci if (reg_mbox3) { 5808c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, "xilinx reset done\n"); 5818c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, "xilinx took %d loops\n", i); 5828c2ecf20Sopenharmony_ci break; 5838c2ecf20Sopenharmony_ci } 5848c2ecf20Sopenharmony_ci } 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci /* todo: add some error handling? */ 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci /* clear mr */ 5898c2ecf20Sopenharmony_ci lx_dsp_reg_write(chip, eReg_CSM, 0); 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci /* le xilinx ES peut ne pas etre encore pret, on attend. */ 5928c2ecf20Sopenharmony_ci msleep(600); 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci return 0; 5958c2ecf20Sopenharmony_ci} 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_cistatic int lx_init_xilinx_test(struct lx6464es *chip) 5988c2ecf20Sopenharmony_ci{ 5998c2ecf20Sopenharmony_ci u32 reg; 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, "->lx_init_xilinx_test\n"); 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci /* TEST if we have access to Xilinx/MicroBlaze */ 6048c2ecf20Sopenharmony_ci lx_dsp_reg_write(chip, eReg_CSM, 0); 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci reg = lx_dsp_reg_read(chip, eReg_CSM); 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci if (reg) { 6098c2ecf20Sopenharmony_ci dev_err(chip->card->dev, "Problem: Reg_CSM %x.\n", reg); 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci /* PCI9056_SPACE0_REMAP */ 6128c2ecf20Sopenharmony_ci lx_plx_reg_write(chip, ePLX_PCICR, 1); 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci reg = lx_dsp_reg_read(chip, eReg_CSM); 6158c2ecf20Sopenharmony_ci if (reg) { 6168c2ecf20Sopenharmony_ci dev_err(chip->card->dev, "Error: Reg_CSM %x.\n", reg); 6178c2ecf20Sopenharmony_ci return -EAGAIN; /* seems to be appropriate */ 6188c2ecf20Sopenharmony_ci } 6198c2ecf20Sopenharmony_ci } 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, "Xilinx/MicroBlaze access test successful\n"); 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci return 0; 6248c2ecf20Sopenharmony_ci} 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci/* initialize ethersound */ 6278c2ecf20Sopenharmony_cistatic int lx_init_ethersound_config(struct lx6464es *chip) 6288c2ecf20Sopenharmony_ci{ 6298c2ecf20Sopenharmony_ci int i; 6308c2ecf20Sopenharmony_ci u32 orig_conf_es = lx_dsp_reg_read(chip, eReg_CONFES); 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci /* configure 64 io channels */ 6338c2ecf20Sopenharmony_ci u32 conf_es = (orig_conf_es & CONFES_READ_PART_MASK) | 6348c2ecf20Sopenharmony_ci (64 << IOCR_INPUTS_OFFSET) | 6358c2ecf20Sopenharmony_ci (64 << IOCR_OUTPUTS_OFFSET) | 6368c2ecf20Sopenharmony_ci (FREQ_RATIO_SINGLE_MODE << FREQ_RATIO_OFFSET); 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, "->lx_init_ethersound\n"); 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci chip->freq_ratio = FREQ_RATIO_SINGLE_MODE; 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci /* 6438c2ecf20Sopenharmony_ci * write it to the card ! 6448c2ecf20Sopenharmony_ci * this actually kicks the ES xilinx, the first time since poweron. 6458c2ecf20Sopenharmony_ci * the MAC address in the Reg_ADMACESMSB Reg_ADMACESLSB registers 6468c2ecf20Sopenharmony_ci * is not ready before this is done, and the bit 2 in Reg_CSES is set. 6478c2ecf20Sopenharmony_ci * */ 6488c2ecf20Sopenharmony_ci lx_dsp_reg_write(chip, eReg_CONFES, conf_es); 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci for (i = 0; i != 1000; ++i) { 6518c2ecf20Sopenharmony_ci if (lx_dsp_reg_read(chip, eReg_CSES) & 4) { 6528c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, "ethersound initialized after %dms\n", 6538c2ecf20Sopenharmony_ci i); 6548c2ecf20Sopenharmony_ci goto ethersound_initialized; 6558c2ecf20Sopenharmony_ci } 6568c2ecf20Sopenharmony_ci msleep(1); 6578c2ecf20Sopenharmony_ci } 6588c2ecf20Sopenharmony_ci dev_warn(chip->card->dev, 6598c2ecf20Sopenharmony_ci "ethersound could not be initialized after %dms\n", i); 6608c2ecf20Sopenharmony_ci return -ETIMEDOUT; 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci ethersound_initialized: 6638c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, "ethersound initialized\n"); 6648c2ecf20Sopenharmony_ci return 0; 6658c2ecf20Sopenharmony_ci} 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_cistatic int lx_init_get_version_features(struct lx6464es *chip) 6688c2ecf20Sopenharmony_ci{ 6698c2ecf20Sopenharmony_ci u32 dsp_version; 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci int err; 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, "->lx_init_get_version_features\n"); 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci err = lx_dsp_get_version(chip, &dsp_version); 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci if (err == 0) { 6788c2ecf20Sopenharmony_ci u32 freq; 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci dev_info(chip->card->dev, "DSP version: V%02d.%02d #%d\n", 6818c2ecf20Sopenharmony_ci (dsp_version>>16) & 0xff, (dsp_version>>8) & 0xff, 6828c2ecf20Sopenharmony_ci dsp_version & 0xff); 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci /* later: what firmware version do we expect? */ 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci /* retrieve Play/Rec features */ 6878c2ecf20Sopenharmony_ci /* done here because we may have to handle alternate 6888c2ecf20Sopenharmony_ci * DSP files. */ 6898c2ecf20Sopenharmony_ci /* later */ 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci /* init the EtherSound sample rate */ 6928c2ecf20Sopenharmony_ci err = lx_dsp_get_clock_frequency(chip, &freq); 6938c2ecf20Sopenharmony_ci if (err == 0) 6948c2ecf20Sopenharmony_ci chip->board_sample_rate = freq; 6958c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, "actual clock frequency %d\n", freq); 6968c2ecf20Sopenharmony_ci } else { 6978c2ecf20Sopenharmony_ci dev_err(chip->card->dev, "DSP corrupted \n"); 6988c2ecf20Sopenharmony_ci err = -EAGAIN; 6998c2ecf20Sopenharmony_ci } 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci return err; 7028c2ecf20Sopenharmony_ci} 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_cistatic int lx_set_granularity(struct lx6464es *chip, u32 gran) 7058c2ecf20Sopenharmony_ci{ 7068c2ecf20Sopenharmony_ci int err = 0; 7078c2ecf20Sopenharmony_ci u32 snapped_gran = MICROBLAZE_IBL_MIN; 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, "->lx_set_granularity\n"); 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci /* blocksize is a power of 2 */ 7128c2ecf20Sopenharmony_ci while ((snapped_gran < gran) && 7138c2ecf20Sopenharmony_ci (snapped_gran < MICROBLAZE_IBL_MAX)) { 7148c2ecf20Sopenharmony_ci snapped_gran *= 2; 7158c2ecf20Sopenharmony_ci } 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci if (snapped_gran == chip->pcm_granularity) 7188c2ecf20Sopenharmony_ci return 0; 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci err = lx_dsp_set_granularity(chip, snapped_gran); 7218c2ecf20Sopenharmony_ci if (err < 0) { 7228c2ecf20Sopenharmony_ci dev_warn(chip->card->dev, "could not set granularity\n"); 7238c2ecf20Sopenharmony_ci err = -EAGAIN; 7248c2ecf20Sopenharmony_ci } 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci if (snapped_gran != gran) 7278c2ecf20Sopenharmony_ci dev_err(chip->card->dev, "snapped blocksize to %d\n", snapped_gran); 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, "set blocksize on board %d\n", snapped_gran); 7308c2ecf20Sopenharmony_ci chip->pcm_granularity = snapped_gran; 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci return err; 7338c2ecf20Sopenharmony_ci} 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci/* initialize and test the xilinx dsp chip */ 7368c2ecf20Sopenharmony_cistatic int lx_init_dsp(struct lx6464es *chip) 7378c2ecf20Sopenharmony_ci{ 7388c2ecf20Sopenharmony_ci int err; 7398c2ecf20Sopenharmony_ci int i; 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, "->lx_init_dsp\n"); 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, "initialize board\n"); 7448c2ecf20Sopenharmony_ci err = lx_init_xilinx_reset(chip); 7458c2ecf20Sopenharmony_ci if (err) 7468c2ecf20Sopenharmony_ci return err; 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, "testing board\n"); 7498c2ecf20Sopenharmony_ci err = lx_init_xilinx_test(chip); 7508c2ecf20Sopenharmony_ci if (err) 7518c2ecf20Sopenharmony_ci return err; 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, "initialize ethersound configuration\n"); 7548c2ecf20Sopenharmony_ci err = lx_init_ethersound_config(chip); 7558c2ecf20Sopenharmony_ci if (err) 7568c2ecf20Sopenharmony_ci return err; 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci lx_irq_enable(chip); 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci /** \todo the mac address should be ready by not, but it isn't, 7618c2ecf20Sopenharmony_ci * so we wait for it */ 7628c2ecf20Sopenharmony_ci for (i = 0; i != 1000; ++i) { 7638c2ecf20Sopenharmony_ci err = lx_dsp_get_mac(chip); 7648c2ecf20Sopenharmony_ci if (err) 7658c2ecf20Sopenharmony_ci return err; 7668c2ecf20Sopenharmony_ci if (chip->mac_address[0] || chip->mac_address[1] || chip->mac_address[2] || 7678c2ecf20Sopenharmony_ci chip->mac_address[3] || chip->mac_address[4] || chip->mac_address[5]) 7688c2ecf20Sopenharmony_ci goto mac_ready; 7698c2ecf20Sopenharmony_ci msleep(1); 7708c2ecf20Sopenharmony_ci } 7718c2ecf20Sopenharmony_ci return -ETIMEDOUT; 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_cimac_ready: 7748c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, "mac address ready read after: %dms\n", i); 7758c2ecf20Sopenharmony_ci dev_info(chip->card->dev, 7768c2ecf20Sopenharmony_ci "mac address: %02X.%02X.%02X.%02X.%02X.%02X\n", 7778c2ecf20Sopenharmony_ci chip->mac_address[0], chip->mac_address[1], chip->mac_address[2], 7788c2ecf20Sopenharmony_ci chip->mac_address[3], chip->mac_address[4], chip->mac_address[5]); 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci err = lx_init_get_version_features(chip); 7818c2ecf20Sopenharmony_ci if (err) 7828c2ecf20Sopenharmony_ci return err; 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci lx_set_granularity(chip, MICROBLAZE_IBL_DEFAULT); 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci chip->playback_mute = 0; 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci return err; 7898c2ecf20Sopenharmony_ci} 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops lx_ops_playback = { 7928c2ecf20Sopenharmony_ci .open = lx_pcm_open, 7938c2ecf20Sopenharmony_ci .close = lx_pcm_close, 7948c2ecf20Sopenharmony_ci .prepare = lx_pcm_prepare, 7958c2ecf20Sopenharmony_ci .hw_params = lx_pcm_hw_params_playback, 7968c2ecf20Sopenharmony_ci .hw_free = lx_pcm_hw_free, 7978c2ecf20Sopenharmony_ci .trigger = lx_pcm_trigger, 7988c2ecf20Sopenharmony_ci .pointer = lx_pcm_stream_pointer, 7998c2ecf20Sopenharmony_ci}; 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops lx_ops_capture = { 8028c2ecf20Sopenharmony_ci .open = lx_pcm_open, 8038c2ecf20Sopenharmony_ci .close = lx_pcm_close, 8048c2ecf20Sopenharmony_ci .prepare = lx_pcm_prepare, 8058c2ecf20Sopenharmony_ci .hw_params = lx_pcm_hw_params_capture, 8068c2ecf20Sopenharmony_ci .hw_free = lx_pcm_hw_free, 8078c2ecf20Sopenharmony_ci .trigger = lx_pcm_trigger, 8088c2ecf20Sopenharmony_ci .pointer = lx_pcm_stream_pointer, 8098c2ecf20Sopenharmony_ci}; 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_cistatic int lx_pcm_create(struct lx6464es *chip) 8128c2ecf20Sopenharmony_ci{ 8138c2ecf20Sopenharmony_ci int err; 8148c2ecf20Sopenharmony_ci struct snd_pcm *pcm; 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci u32 size = 64 * /* channels */ 8178c2ecf20Sopenharmony_ci 3 * /* 24 bit samples */ 8188c2ecf20Sopenharmony_ci MAX_STREAM_BUFFER * /* periods */ 8198c2ecf20Sopenharmony_ci MICROBLAZE_IBL_MAX * /* frames per period */ 8208c2ecf20Sopenharmony_ci 2; /* duplex */ 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci size = PAGE_ALIGN(size); 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_ci /* hardcoded device name & channel count */ 8258c2ecf20Sopenharmony_ci err = snd_pcm_new(chip->card, (char *)card_name, 0, 8268c2ecf20Sopenharmony_ci 1, 1, &pcm); 8278c2ecf20Sopenharmony_ci if (err < 0) 8288c2ecf20Sopenharmony_ci return err; 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci pcm->private_data = chip; 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &lx_ops_playback); 8338c2ecf20Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &lx_ops_capture); 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci pcm->info_flags = 0; 8368c2ecf20Sopenharmony_ci pcm->nonatomic = true; 8378c2ecf20Sopenharmony_ci strcpy(pcm->name, card_name); 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, 8408c2ecf20Sopenharmony_ci &chip->pci->dev, size, size); 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci chip->pcm = pcm; 8438c2ecf20Sopenharmony_ci chip->capture_stream.is_capture = 1; 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci return 0; 8468c2ecf20Sopenharmony_ci} 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_cistatic int lx_control_playback_info(struct snd_kcontrol *kcontrol, 8498c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 8508c2ecf20Sopenharmony_ci{ 8518c2ecf20Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; 8528c2ecf20Sopenharmony_ci uinfo->count = 1; 8538c2ecf20Sopenharmony_ci uinfo->value.integer.min = 0; 8548c2ecf20Sopenharmony_ci uinfo->value.integer.max = 1; 8558c2ecf20Sopenharmony_ci return 0; 8568c2ecf20Sopenharmony_ci} 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_cistatic int lx_control_playback_get(struct snd_kcontrol *kcontrol, 8598c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 8608c2ecf20Sopenharmony_ci{ 8618c2ecf20Sopenharmony_ci struct lx6464es *chip = snd_kcontrol_chip(kcontrol); 8628c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = chip->playback_mute; 8638c2ecf20Sopenharmony_ci return 0; 8648c2ecf20Sopenharmony_ci} 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_cistatic int lx_control_playback_put(struct snd_kcontrol *kcontrol, 8678c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 8688c2ecf20Sopenharmony_ci{ 8698c2ecf20Sopenharmony_ci struct lx6464es *chip = snd_kcontrol_chip(kcontrol); 8708c2ecf20Sopenharmony_ci int changed = 0; 8718c2ecf20Sopenharmony_ci int current_value = chip->playback_mute; 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci if (current_value != ucontrol->value.integer.value[0]) { 8748c2ecf20Sopenharmony_ci lx_level_unmute(chip, 0, !current_value); 8758c2ecf20Sopenharmony_ci chip->playback_mute = !current_value; 8768c2ecf20Sopenharmony_ci changed = 1; 8778c2ecf20Sopenharmony_ci } 8788c2ecf20Sopenharmony_ci return changed; 8798c2ecf20Sopenharmony_ci} 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new lx_control_playback_switch = { 8828c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 8838c2ecf20Sopenharmony_ci .name = "PCM Playback Switch", 8848c2ecf20Sopenharmony_ci .index = 0, 8858c2ecf20Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, 8868c2ecf20Sopenharmony_ci .private_value = 0, 8878c2ecf20Sopenharmony_ci .info = lx_control_playback_info, 8888c2ecf20Sopenharmony_ci .get = lx_control_playback_get, 8898c2ecf20Sopenharmony_ci .put = lx_control_playback_put 8908c2ecf20Sopenharmony_ci}; 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_cistatic void lx_proc_levels_read(struct snd_info_entry *entry, 8958c2ecf20Sopenharmony_ci struct snd_info_buffer *buffer) 8968c2ecf20Sopenharmony_ci{ 8978c2ecf20Sopenharmony_ci u32 levels[64]; 8988c2ecf20Sopenharmony_ci int err; 8998c2ecf20Sopenharmony_ci int i, j; 9008c2ecf20Sopenharmony_ci struct lx6464es *chip = entry->private_data; 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci snd_iprintf(buffer, "capture levels:\n"); 9038c2ecf20Sopenharmony_ci err = lx_level_peaks(chip, 1, 64, levels); 9048c2ecf20Sopenharmony_ci if (err < 0) 9058c2ecf20Sopenharmony_ci return; 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci for (i = 0; i != 8; ++i) { 9088c2ecf20Sopenharmony_ci for (j = 0; j != 8; ++j) 9098c2ecf20Sopenharmony_ci snd_iprintf(buffer, "%08x ", levels[i*8+j]); 9108c2ecf20Sopenharmony_ci snd_iprintf(buffer, "\n"); 9118c2ecf20Sopenharmony_ci } 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_ci snd_iprintf(buffer, "\nplayback levels:\n"); 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci err = lx_level_peaks(chip, 0, 64, levels); 9168c2ecf20Sopenharmony_ci if (err < 0) 9178c2ecf20Sopenharmony_ci return; 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci for (i = 0; i != 8; ++i) { 9208c2ecf20Sopenharmony_ci for (j = 0; j != 8; ++j) 9218c2ecf20Sopenharmony_ci snd_iprintf(buffer, "%08x ", levels[i*8+j]); 9228c2ecf20Sopenharmony_ci snd_iprintf(buffer, "\n"); 9238c2ecf20Sopenharmony_ci } 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_ci snd_iprintf(buffer, "\n"); 9268c2ecf20Sopenharmony_ci} 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_cistatic int lx_proc_create(struct snd_card *card, struct lx6464es *chip) 9298c2ecf20Sopenharmony_ci{ 9308c2ecf20Sopenharmony_ci return snd_card_ro_proc_new(card, "levels", chip, lx_proc_levels_read); 9318c2ecf20Sopenharmony_ci} 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_cistatic int snd_lx6464es_create(struct snd_card *card, 9358c2ecf20Sopenharmony_ci struct pci_dev *pci, 9368c2ecf20Sopenharmony_ci struct lx6464es **rchip) 9378c2ecf20Sopenharmony_ci{ 9388c2ecf20Sopenharmony_ci struct lx6464es *chip; 9398c2ecf20Sopenharmony_ci int err; 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_ci static const struct snd_device_ops ops = { 9428c2ecf20Sopenharmony_ci .dev_free = snd_lx6464es_dev_free, 9438c2ecf20Sopenharmony_ci }; 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_ci dev_dbg(card->dev, "->snd_lx6464es_create\n"); 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_ci *rchip = NULL; 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_ci /* enable PCI device */ 9508c2ecf20Sopenharmony_ci err = pci_enable_device(pci); 9518c2ecf20Sopenharmony_ci if (err < 0) 9528c2ecf20Sopenharmony_ci return err; 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_ci pci_set_master(pci); 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci /* check if we can restrict PCI DMA transfers to 32 bits */ 9578c2ecf20Sopenharmony_ci err = dma_set_mask(&pci->dev, DMA_BIT_MASK(32)); 9588c2ecf20Sopenharmony_ci if (err < 0) { 9598c2ecf20Sopenharmony_ci dev_err(card->dev, 9608c2ecf20Sopenharmony_ci "architecture does not support 32bit PCI busmaster DMA\n"); 9618c2ecf20Sopenharmony_ci pci_disable_device(pci); 9628c2ecf20Sopenharmony_ci return -ENXIO; 9638c2ecf20Sopenharmony_ci } 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci chip = kzalloc(sizeof(*chip), GFP_KERNEL); 9668c2ecf20Sopenharmony_ci if (chip == NULL) { 9678c2ecf20Sopenharmony_ci err = -ENOMEM; 9688c2ecf20Sopenharmony_ci goto alloc_failed; 9698c2ecf20Sopenharmony_ci } 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci chip->card = card; 9728c2ecf20Sopenharmony_ci chip->pci = pci; 9738c2ecf20Sopenharmony_ci chip->irq = -1; 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci /* initialize synchronization structs */ 9768c2ecf20Sopenharmony_ci mutex_init(&chip->lock); 9778c2ecf20Sopenharmony_ci mutex_init(&chip->msg_lock); 9788c2ecf20Sopenharmony_ci mutex_init(&chip->setup_mutex); 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci /* request resources */ 9818c2ecf20Sopenharmony_ci err = pci_request_regions(pci, card_name); 9828c2ecf20Sopenharmony_ci if (err < 0) 9838c2ecf20Sopenharmony_ci goto request_regions_failed; 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_ci /* plx port */ 9868c2ecf20Sopenharmony_ci chip->port_plx = pci_resource_start(pci, 1); 9878c2ecf20Sopenharmony_ci chip->port_plx_remapped = ioport_map(chip->port_plx, 9888c2ecf20Sopenharmony_ci pci_resource_len(pci, 1)); 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci /* dsp port */ 9918c2ecf20Sopenharmony_ci chip->port_dsp_bar = pci_ioremap_bar(pci, 2); 9928c2ecf20Sopenharmony_ci if (!chip->port_dsp_bar) { 9938c2ecf20Sopenharmony_ci dev_err(card->dev, "cannot remap PCI memory region\n"); 9948c2ecf20Sopenharmony_ci err = -ENOMEM; 9958c2ecf20Sopenharmony_ci goto remap_pci_failed; 9968c2ecf20Sopenharmony_ci } 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_ci err = request_threaded_irq(pci->irq, lx_interrupt, lx_threaded_irq, 9998c2ecf20Sopenharmony_ci IRQF_SHARED, KBUILD_MODNAME, chip); 10008c2ecf20Sopenharmony_ci if (err) { 10018c2ecf20Sopenharmony_ci dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq); 10028c2ecf20Sopenharmony_ci goto request_irq_failed; 10038c2ecf20Sopenharmony_ci } 10048c2ecf20Sopenharmony_ci chip->irq = pci->irq; 10058c2ecf20Sopenharmony_ci card->sync_irq = chip->irq; 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_ci err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops); 10088c2ecf20Sopenharmony_ci if (err < 0) 10098c2ecf20Sopenharmony_ci goto device_new_failed; 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_ci err = lx_init_dsp(chip); 10128c2ecf20Sopenharmony_ci if (err < 0) { 10138c2ecf20Sopenharmony_ci dev_err(card->dev, "error during DSP initialization\n"); 10148c2ecf20Sopenharmony_ci return err; 10158c2ecf20Sopenharmony_ci } 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ci err = lx_pcm_create(chip); 10188c2ecf20Sopenharmony_ci if (err < 0) 10198c2ecf20Sopenharmony_ci return err; 10208c2ecf20Sopenharmony_ci 10218c2ecf20Sopenharmony_ci err = lx_proc_create(card, chip); 10228c2ecf20Sopenharmony_ci if (err < 0) 10238c2ecf20Sopenharmony_ci return err; 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci err = snd_ctl_add(card, snd_ctl_new1(&lx_control_playback_switch, 10268c2ecf20Sopenharmony_ci chip)); 10278c2ecf20Sopenharmony_ci if (err < 0) 10288c2ecf20Sopenharmony_ci return err; 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_ci *rchip = chip; 10318c2ecf20Sopenharmony_ci return 0; 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_cidevice_new_failed: 10348c2ecf20Sopenharmony_ci free_irq(pci->irq, chip); 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_cirequest_irq_failed: 10378c2ecf20Sopenharmony_ci iounmap(chip->port_dsp_bar); 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ciremap_pci_failed: 10408c2ecf20Sopenharmony_ci pci_release_regions(pci); 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_cirequest_regions_failed: 10438c2ecf20Sopenharmony_ci kfree(chip); 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_cialloc_failed: 10468c2ecf20Sopenharmony_ci pci_disable_device(pci); 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ci return err; 10498c2ecf20Sopenharmony_ci} 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_cistatic int snd_lx6464es_probe(struct pci_dev *pci, 10528c2ecf20Sopenharmony_ci const struct pci_device_id *pci_id) 10538c2ecf20Sopenharmony_ci{ 10548c2ecf20Sopenharmony_ci static int dev; 10558c2ecf20Sopenharmony_ci struct snd_card *card; 10568c2ecf20Sopenharmony_ci struct lx6464es *chip; 10578c2ecf20Sopenharmony_ci int err; 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_ci dev_dbg(&pci->dev, "->snd_lx6464es_probe\n"); 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_ci if (dev >= SNDRV_CARDS) 10628c2ecf20Sopenharmony_ci return -ENODEV; 10638c2ecf20Sopenharmony_ci if (!enable[dev]) { 10648c2ecf20Sopenharmony_ci dev++; 10658c2ecf20Sopenharmony_ci return -ENOENT; 10668c2ecf20Sopenharmony_ci } 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_ci err = snd_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE, 10698c2ecf20Sopenharmony_ci 0, &card); 10708c2ecf20Sopenharmony_ci if (err < 0) 10718c2ecf20Sopenharmony_ci return err; 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci err = snd_lx6464es_create(card, pci, &chip); 10748c2ecf20Sopenharmony_ci if (err < 0) { 10758c2ecf20Sopenharmony_ci dev_err(card->dev, "error during snd_lx6464es_create\n"); 10768c2ecf20Sopenharmony_ci goto out_free; 10778c2ecf20Sopenharmony_ci } 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci strcpy(card->driver, "LX6464ES"); 10808c2ecf20Sopenharmony_ci sprintf(card->id, "LX6464ES_%02X%02X%02X", 10818c2ecf20Sopenharmony_ci chip->mac_address[3], chip->mac_address[4], chip->mac_address[5]); 10828c2ecf20Sopenharmony_ci 10838c2ecf20Sopenharmony_ci sprintf(card->shortname, "LX6464ES %02X.%02X.%02X.%02X.%02X.%02X", 10848c2ecf20Sopenharmony_ci chip->mac_address[0], chip->mac_address[1], chip->mac_address[2], 10858c2ecf20Sopenharmony_ci chip->mac_address[3], chip->mac_address[4], chip->mac_address[5]); 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_ci sprintf(card->longname, "%s at 0x%lx, 0x%p, irq %i", 10888c2ecf20Sopenharmony_ci card->shortname, chip->port_plx, 10898c2ecf20Sopenharmony_ci chip->port_dsp_bar, chip->irq); 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_ci err = snd_card_register(card); 10928c2ecf20Sopenharmony_ci if (err < 0) 10938c2ecf20Sopenharmony_ci goto out_free; 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, "initialization successful\n"); 10968c2ecf20Sopenharmony_ci pci_set_drvdata(pci, card); 10978c2ecf20Sopenharmony_ci dev++; 10988c2ecf20Sopenharmony_ci return 0; 10998c2ecf20Sopenharmony_ci 11008c2ecf20Sopenharmony_ciout_free: 11018c2ecf20Sopenharmony_ci snd_card_free(card); 11028c2ecf20Sopenharmony_ci return err; 11038c2ecf20Sopenharmony_ci 11048c2ecf20Sopenharmony_ci} 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_cistatic void snd_lx6464es_remove(struct pci_dev *pci) 11078c2ecf20Sopenharmony_ci{ 11088c2ecf20Sopenharmony_ci snd_card_free(pci_get_drvdata(pci)); 11098c2ecf20Sopenharmony_ci} 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_cistatic struct pci_driver lx6464es_driver = { 11138c2ecf20Sopenharmony_ci .name = KBUILD_MODNAME, 11148c2ecf20Sopenharmony_ci .id_table = snd_lx6464es_ids, 11158c2ecf20Sopenharmony_ci .probe = snd_lx6464es_probe, 11168c2ecf20Sopenharmony_ci .remove = snd_lx6464es_remove, 11178c2ecf20Sopenharmony_ci}; 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_cimodule_pci_driver(lx6464es_driver); 1120