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