162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/* -*- linux-c -*- *
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * ALSA driver for the digigram lx6464es interface
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Copyright (c) 2008, 2009 Tim Blechmann <tim@klingt.org>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/module.h>
1062306a36Sopenharmony_ci#include <linux/init.h>
1162306a36Sopenharmony_ci#include <linux/pci.h>
1262306a36Sopenharmony_ci#include <linux/delay.h>
1362306a36Sopenharmony_ci#include <linux/slab.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include <sound/initval.h>
1662306a36Sopenharmony_ci#include <sound/control.h>
1762306a36Sopenharmony_ci#include <sound/info.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include "lx6464es.h"
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ciMODULE_AUTHOR("Tim Blechmann");
2262306a36Sopenharmony_ciMODULE_LICENSE("GPL");
2362306a36Sopenharmony_ciMODULE_DESCRIPTION("digigram lx6464es");
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistatic int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
2662306a36Sopenharmony_cistatic char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
2762306a36Sopenharmony_cistatic bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cimodule_param_array(index, int, NULL, 0444);
3062306a36Sopenharmony_ciMODULE_PARM_DESC(index, "Index value for Digigram LX6464ES interface.");
3162306a36Sopenharmony_cimodule_param_array(id, charp, NULL, 0444);
3262306a36Sopenharmony_ciMODULE_PARM_DESC(id, "ID string for  Digigram LX6464ES interface.");
3362306a36Sopenharmony_cimodule_param_array(enable, bool, NULL, 0444);
3462306a36Sopenharmony_ciMODULE_PARM_DESC(enable, "Enable/disable specific Digigram LX6464ES soundcards.");
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cistatic const char card_name[] = "LX6464ES";
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci#define PCI_DEVICE_ID_PLX_LX6464ES		PCI_DEVICE_ID_PLX_9056
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_cistatic const struct pci_device_id snd_lx6464es_ids[] = {
4262306a36Sopenharmony_ci	{ PCI_DEVICE_SUB(PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_LX6464ES,
4362306a36Sopenharmony_ci			 PCI_VENDOR_ID_DIGIGRAM,
4462306a36Sopenharmony_ci			 PCI_SUBDEVICE_ID_DIGIGRAM_LX6464ES_SERIAL_SUBSYSTEM),
4562306a36Sopenharmony_ci	},			/* LX6464ES */
4662306a36Sopenharmony_ci	{ PCI_DEVICE_SUB(PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_LX6464ES,
4762306a36Sopenharmony_ci			 PCI_VENDOR_ID_DIGIGRAM,
4862306a36Sopenharmony_ci			 PCI_SUBDEVICE_ID_DIGIGRAM_LX6464ES_CAE_SERIAL_SUBSYSTEM),
4962306a36Sopenharmony_ci	},			/* LX6464ES-CAE */
5062306a36Sopenharmony_ci	{ PCI_DEVICE_SUB(PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_LX6464ES,
5162306a36Sopenharmony_ci			 PCI_VENDOR_ID_DIGIGRAM,
5262306a36Sopenharmony_ci			 PCI_SUBDEVICE_ID_DIGIGRAM_LX6464ESE_SERIAL_SUBSYSTEM),
5362306a36Sopenharmony_ci	},			/* LX6464ESe */
5462306a36Sopenharmony_ci	{ PCI_DEVICE_SUB(PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_LX6464ES,
5562306a36Sopenharmony_ci			 PCI_VENDOR_ID_DIGIGRAM,
5662306a36Sopenharmony_ci			 PCI_SUBDEVICE_ID_DIGIGRAM_LX6464ESE_CAE_SERIAL_SUBSYSTEM),
5762306a36Sopenharmony_ci	},			/* LX6464ESe-CAE */
5862306a36Sopenharmony_ci	{ 0, },
5962306a36Sopenharmony_ci};
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, snd_lx6464es_ids);
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci/* PGO pour USERo dans le registre pci_0x06/loc_0xEC */
6662306a36Sopenharmony_ci#define CHIPSC_RESET_XILINX (1L<<16)
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci/* alsa callbacks */
7062306a36Sopenharmony_cistatic const struct snd_pcm_hardware lx_caps = {
7162306a36Sopenharmony_ci	.info             = (SNDRV_PCM_INFO_MMAP |
7262306a36Sopenharmony_ci			     SNDRV_PCM_INFO_INTERLEAVED |
7362306a36Sopenharmony_ci			     SNDRV_PCM_INFO_MMAP_VALID |
7462306a36Sopenharmony_ci			     SNDRV_PCM_INFO_SYNC_START),
7562306a36Sopenharmony_ci	.formats	  = (SNDRV_PCM_FMTBIT_S16_LE |
7662306a36Sopenharmony_ci			     SNDRV_PCM_FMTBIT_S16_BE |
7762306a36Sopenharmony_ci			     SNDRV_PCM_FMTBIT_S24_3LE |
7862306a36Sopenharmony_ci			     SNDRV_PCM_FMTBIT_S24_3BE),
7962306a36Sopenharmony_ci	.rates            = (SNDRV_PCM_RATE_CONTINUOUS |
8062306a36Sopenharmony_ci			     SNDRV_PCM_RATE_8000_192000),
8162306a36Sopenharmony_ci	.rate_min         = 8000,
8262306a36Sopenharmony_ci	.rate_max         = 192000,
8362306a36Sopenharmony_ci	.channels_min     = 2,
8462306a36Sopenharmony_ci	.channels_max     = 64,
8562306a36Sopenharmony_ci	.buffer_bytes_max = 64*2*3*MICROBLAZE_IBL_MAX*MAX_STREAM_BUFFER,
8662306a36Sopenharmony_ci	.period_bytes_min = (2*2*MICROBLAZE_IBL_MIN*2),
8762306a36Sopenharmony_ci	.period_bytes_max = (4*64*MICROBLAZE_IBL_MAX*MAX_STREAM_BUFFER),
8862306a36Sopenharmony_ci	.periods_min      = 2,
8962306a36Sopenharmony_ci	.periods_max      = MAX_STREAM_BUFFER,
9062306a36Sopenharmony_ci};
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_cistatic int lx_set_granularity(struct lx6464es *chip, u32 gran);
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_cistatic int lx_hardware_open(struct lx6464es *chip,
9662306a36Sopenharmony_ci			    struct snd_pcm_substream *substream)
9762306a36Sopenharmony_ci{
9862306a36Sopenharmony_ci	int err = 0;
9962306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
10062306a36Sopenharmony_ci	int channels = runtime->channels;
10162306a36Sopenharmony_ci	int is_capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	snd_pcm_uframes_t period_size = runtime->period_size;
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	dev_dbg(chip->card->dev, "allocating pipe for %d channels\n", channels);
10662306a36Sopenharmony_ci	err = lx_pipe_allocate(chip, 0, is_capture, channels);
10762306a36Sopenharmony_ci	if (err < 0) {
10862306a36Sopenharmony_ci		dev_err(chip->card->dev, LXP "allocating pipe failed\n");
10962306a36Sopenharmony_ci		return err;
11062306a36Sopenharmony_ci	}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	err = lx_set_granularity(chip, period_size);
11362306a36Sopenharmony_ci	if (err < 0) {
11462306a36Sopenharmony_ci		dev_err(chip->card->dev, "setting granularity to %ld failed\n",
11562306a36Sopenharmony_ci			   period_size);
11662306a36Sopenharmony_ci		return err;
11762306a36Sopenharmony_ci	}
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	return 0;
12062306a36Sopenharmony_ci}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_cistatic int lx_hardware_start(struct lx6464es *chip,
12362306a36Sopenharmony_ci			     struct snd_pcm_substream *substream)
12462306a36Sopenharmony_ci{
12562306a36Sopenharmony_ci	int err = 0;
12662306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
12762306a36Sopenharmony_ci	int is_capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	dev_dbg(chip->card->dev, "setting stream format\n");
13062306a36Sopenharmony_ci	err = lx_stream_set_format(chip, runtime, 0, is_capture);
13162306a36Sopenharmony_ci	if (err < 0) {
13262306a36Sopenharmony_ci		dev_err(chip->card->dev, "setting stream format failed\n");
13362306a36Sopenharmony_ci		return err;
13462306a36Sopenharmony_ci	}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	dev_dbg(chip->card->dev, "starting pipe\n");
13762306a36Sopenharmony_ci	err = lx_pipe_start(chip, 0, is_capture);
13862306a36Sopenharmony_ci	if (err < 0) {
13962306a36Sopenharmony_ci		dev_err(chip->card->dev, "starting pipe failed\n");
14062306a36Sopenharmony_ci		return err;
14162306a36Sopenharmony_ci	}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	dev_dbg(chip->card->dev, "waiting for pipe to start\n");
14462306a36Sopenharmony_ci	err = lx_pipe_wait_for_start(chip, 0, is_capture);
14562306a36Sopenharmony_ci	if (err < 0) {
14662306a36Sopenharmony_ci		dev_err(chip->card->dev, "waiting for pipe failed\n");
14762306a36Sopenharmony_ci		return err;
14862306a36Sopenharmony_ci	}
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	return err;
15162306a36Sopenharmony_ci}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_cistatic int lx_hardware_stop(struct lx6464es *chip,
15562306a36Sopenharmony_ci			    struct snd_pcm_substream *substream)
15662306a36Sopenharmony_ci{
15762306a36Sopenharmony_ci	int err = 0;
15862306a36Sopenharmony_ci	int is_capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	dev_dbg(chip->card->dev, "pausing pipe\n");
16162306a36Sopenharmony_ci	err = lx_pipe_pause(chip, 0, is_capture);
16262306a36Sopenharmony_ci	if (err < 0) {
16362306a36Sopenharmony_ci		dev_err(chip->card->dev, "pausing pipe failed\n");
16462306a36Sopenharmony_ci		return err;
16562306a36Sopenharmony_ci	}
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	dev_dbg(chip->card->dev, "waiting for pipe to become idle\n");
16862306a36Sopenharmony_ci	err = lx_pipe_wait_for_idle(chip, 0, is_capture);
16962306a36Sopenharmony_ci	if (err < 0) {
17062306a36Sopenharmony_ci		dev_err(chip->card->dev, "waiting for pipe failed\n");
17162306a36Sopenharmony_ci		return err;
17262306a36Sopenharmony_ci	}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	dev_dbg(chip->card->dev, "stopping pipe\n");
17562306a36Sopenharmony_ci	err = lx_pipe_stop(chip, 0, is_capture);
17662306a36Sopenharmony_ci	if (err < 0) {
17762306a36Sopenharmony_ci		dev_err(chip->card->dev, "stopping pipe failed\n");
17862306a36Sopenharmony_ci		return err;
17962306a36Sopenharmony_ci	}
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	return err;
18262306a36Sopenharmony_ci}
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_cistatic int lx_hardware_close(struct lx6464es *chip,
18662306a36Sopenharmony_ci			     struct snd_pcm_substream *substream)
18762306a36Sopenharmony_ci{
18862306a36Sopenharmony_ci	int err = 0;
18962306a36Sopenharmony_ci	int is_capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	dev_dbg(chip->card->dev, "releasing pipe\n");
19262306a36Sopenharmony_ci	err = lx_pipe_release(chip, 0, is_capture);
19362306a36Sopenharmony_ci	if (err < 0) {
19462306a36Sopenharmony_ci		dev_err(chip->card->dev, "releasing pipe failed\n");
19562306a36Sopenharmony_ci		return err;
19662306a36Sopenharmony_ci	}
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	return err;
19962306a36Sopenharmony_ci}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_cistatic int lx_pcm_open(struct snd_pcm_substream *substream)
20362306a36Sopenharmony_ci{
20462306a36Sopenharmony_ci	struct lx6464es *chip = snd_pcm_substream_chip(substream);
20562306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
20662306a36Sopenharmony_ci	int err = 0;
20762306a36Sopenharmony_ci	int board_rate;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	dev_dbg(chip->card->dev, "->lx_pcm_open\n");
21062306a36Sopenharmony_ci	mutex_lock(&chip->setup_mutex);
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	/* copy the struct snd_pcm_hardware struct */
21362306a36Sopenharmony_ci	runtime->hw = lx_caps;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci#if 0
21662306a36Sopenharmony_ci	/* buffer-size should better be multiple of period-size */
21762306a36Sopenharmony_ci	err = snd_pcm_hw_constraint_integer(runtime,
21862306a36Sopenharmony_ci					    SNDRV_PCM_HW_PARAM_PERIODS);
21962306a36Sopenharmony_ci	if (err < 0) {
22062306a36Sopenharmony_ci		dev_warn(chip->card->dev, "could not constrain periods\n");
22162306a36Sopenharmony_ci		goto exit;
22262306a36Sopenharmony_ci	}
22362306a36Sopenharmony_ci#endif
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	/* the clock rate cannot be changed */
22662306a36Sopenharmony_ci	board_rate = chip->board_sample_rate;
22762306a36Sopenharmony_ci	err = snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_RATE,
22862306a36Sopenharmony_ci					   board_rate);
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	if (err < 0) {
23162306a36Sopenharmony_ci		dev_warn(chip->card->dev, "could not constrain periods\n");
23262306a36Sopenharmony_ci		goto exit;
23362306a36Sopenharmony_ci	}
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	/* constrain period size */
23662306a36Sopenharmony_ci	err = snd_pcm_hw_constraint_minmax(runtime,
23762306a36Sopenharmony_ci					   SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
23862306a36Sopenharmony_ci					   MICROBLAZE_IBL_MIN,
23962306a36Sopenharmony_ci					   MICROBLAZE_IBL_MAX);
24062306a36Sopenharmony_ci	if (err < 0) {
24162306a36Sopenharmony_ci		dev_warn(chip->card->dev,
24262306a36Sopenharmony_ci			   "could not constrain period size\n");
24362306a36Sopenharmony_ci		goto exit;
24462306a36Sopenharmony_ci	}
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	snd_pcm_hw_constraint_step(runtime, 0,
24762306a36Sopenharmony_ci				   SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 32);
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	snd_pcm_set_sync(substream);
25062306a36Sopenharmony_ci	err = 0;
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ciexit:
25362306a36Sopenharmony_ci	runtime->private_data = chip;
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	mutex_unlock(&chip->setup_mutex);
25662306a36Sopenharmony_ci	dev_dbg(chip->card->dev, "<-lx_pcm_open, %d\n", err);
25762306a36Sopenharmony_ci	return err;
25862306a36Sopenharmony_ci}
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_cistatic int lx_pcm_close(struct snd_pcm_substream *substream)
26162306a36Sopenharmony_ci{
26262306a36Sopenharmony_ci	dev_dbg(substream->pcm->card->dev, "->lx_pcm_close\n");
26362306a36Sopenharmony_ci	return 0;
26462306a36Sopenharmony_ci}
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_cistatic snd_pcm_uframes_t lx_pcm_stream_pointer(struct snd_pcm_substream
26762306a36Sopenharmony_ci					       *substream)
26862306a36Sopenharmony_ci{
26962306a36Sopenharmony_ci	struct lx6464es *chip = snd_pcm_substream_chip(substream);
27062306a36Sopenharmony_ci	snd_pcm_uframes_t pos;
27162306a36Sopenharmony_ci	int is_capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	struct lx_stream *lx_stream = is_capture ? &chip->capture_stream :
27462306a36Sopenharmony_ci		&chip->playback_stream;
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	dev_dbg(chip->card->dev, "->lx_pcm_stream_pointer\n");
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	mutex_lock(&chip->lock);
27962306a36Sopenharmony_ci	pos = lx_stream->frame_pos * substream->runtime->period_size;
28062306a36Sopenharmony_ci	mutex_unlock(&chip->lock);
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	dev_dbg(chip->card->dev, "stream_pointer at %ld\n", pos);
28362306a36Sopenharmony_ci	return pos;
28462306a36Sopenharmony_ci}
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_cistatic int lx_pcm_prepare(struct snd_pcm_substream *substream)
28762306a36Sopenharmony_ci{
28862306a36Sopenharmony_ci	struct lx6464es *chip = snd_pcm_substream_chip(substream);
28962306a36Sopenharmony_ci	int err = 0;
29062306a36Sopenharmony_ci	const int is_capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	dev_dbg(chip->card->dev, "->lx_pcm_prepare\n");
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	mutex_lock(&chip->setup_mutex);
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	if (chip->hardware_running[is_capture]) {
29762306a36Sopenharmony_ci		err = lx_hardware_stop(chip, substream);
29862306a36Sopenharmony_ci		if (err < 0) {
29962306a36Sopenharmony_ci			dev_err(chip->card->dev, "failed to stop hardware. "
30062306a36Sopenharmony_ci				   "Error code %d\n", err);
30162306a36Sopenharmony_ci			goto exit;
30262306a36Sopenharmony_ci		}
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci		err = lx_hardware_close(chip, substream);
30562306a36Sopenharmony_ci		if (err < 0) {
30662306a36Sopenharmony_ci			dev_err(chip->card->dev, "failed to close hardware. "
30762306a36Sopenharmony_ci				   "Error code %d\n", err);
30862306a36Sopenharmony_ci			goto exit;
30962306a36Sopenharmony_ci		}
31062306a36Sopenharmony_ci	}
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	dev_dbg(chip->card->dev, "opening hardware\n");
31362306a36Sopenharmony_ci	err = lx_hardware_open(chip, substream);
31462306a36Sopenharmony_ci	if (err < 0) {
31562306a36Sopenharmony_ci		dev_err(chip->card->dev, "failed to open hardware. "
31662306a36Sopenharmony_ci			   "Error code %d\n", err);
31762306a36Sopenharmony_ci		goto exit;
31862306a36Sopenharmony_ci	}
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	err = lx_hardware_start(chip, substream);
32162306a36Sopenharmony_ci	if (err < 0) {
32262306a36Sopenharmony_ci		dev_err(chip->card->dev, "failed to start hardware. "
32362306a36Sopenharmony_ci			   "Error code %d\n", err);
32462306a36Sopenharmony_ci		goto exit;
32562306a36Sopenharmony_ci	}
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	chip->hardware_running[is_capture] = 1;
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	if (chip->board_sample_rate != substream->runtime->rate) {
33062306a36Sopenharmony_ci		if (!err)
33162306a36Sopenharmony_ci			chip->board_sample_rate = substream->runtime->rate;
33262306a36Sopenharmony_ci	}
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ciexit:
33562306a36Sopenharmony_ci	mutex_unlock(&chip->setup_mutex);
33662306a36Sopenharmony_ci	return err;
33762306a36Sopenharmony_ci}
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_cistatic int lx_pcm_hw_params(struct snd_pcm_substream *substream,
34062306a36Sopenharmony_ci			    struct snd_pcm_hw_params *hw_params, int is_capture)
34162306a36Sopenharmony_ci{
34262306a36Sopenharmony_ci	struct lx6464es *chip = snd_pcm_substream_chip(substream);
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	dev_dbg(chip->card->dev, "->lx_pcm_hw_params\n");
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	mutex_lock(&chip->setup_mutex);
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	if (is_capture)
34962306a36Sopenharmony_ci		chip->capture_stream.stream = substream;
35062306a36Sopenharmony_ci	else
35162306a36Sopenharmony_ci		chip->playback_stream.stream = substream;
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	mutex_unlock(&chip->setup_mutex);
35462306a36Sopenharmony_ci	return 0;
35562306a36Sopenharmony_ci}
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_cistatic int lx_pcm_hw_params_playback(struct snd_pcm_substream *substream,
35862306a36Sopenharmony_ci				 struct snd_pcm_hw_params *hw_params)
35962306a36Sopenharmony_ci{
36062306a36Sopenharmony_ci	return lx_pcm_hw_params(substream, hw_params, 0);
36162306a36Sopenharmony_ci}
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_cistatic int lx_pcm_hw_params_capture(struct snd_pcm_substream *substream,
36462306a36Sopenharmony_ci				 struct snd_pcm_hw_params *hw_params)
36562306a36Sopenharmony_ci{
36662306a36Sopenharmony_ci	return lx_pcm_hw_params(substream, hw_params, 1);
36762306a36Sopenharmony_ci}
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_cistatic int lx_pcm_hw_free(struct snd_pcm_substream *substream)
37062306a36Sopenharmony_ci{
37162306a36Sopenharmony_ci	struct lx6464es *chip = snd_pcm_substream_chip(substream);
37262306a36Sopenharmony_ci	int err = 0;
37362306a36Sopenharmony_ci	int is_capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	dev_dbg(chip->card->dev, "->lx_pcm_hw_free\n");
37662306a36Sopenharmony_ci	mutex_lock(&chip->setup_mutex);
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	if (chip->hardware_running[is_capture]) {
37962306a36Sopenharmony_ci		err = lx_hardware_stop(chip, substream);
38062306a36Sopenharmony_ci		if (err < 0) {
38162306a36Sopenharmony_ci			dev_err(chip->card->dev, "failed to stop hardware. "
38262306a36Sopenharmony_ci				   "Error code %d\n", err);
38362306a36Sopenharmony_ci			goto exit;
38462306a36Sopenharmony_ci		}
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci		err = lx_hardware_close(chip, substream);
38762306a36Sopenharmony_ci		if (err < 0) {
38862306a36Sopenharmony_ci			dev_err(chip->card->dev, "failed to close hardware. "
38962306a36Sopenharmony_ci				   "Error code %d\n", err);
39062306a36Sopenharmony_ci			goto exit;
39162306a36Sopenharmony_ci		}
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci		chip->hardware_running[is_capture] = 0;
39462306a36Sopenharmony_ci	}
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	if (is_capture)
39762306a36Sopenharmony_ci		chip->capture_stream.stream = NULL;
39862306a36Sopenharmony_ci	else
39962306a36Sopenharmony_ci		chip->playback_stream.stream = NULL;
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ciexit:
40262306a36Sopenharmony_ci	mutex_unlock(&chip->setup_mutex);
40362306a36Sopenharmony_ci	return err;
40462306a36Sopenharmony_ci}
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_cistatic void lx_trigger_start(struct lx6464es *chip, struct lx_stream *lx_stream)
40762306a36Sopenharmony_ci{
40862306a36Sopenharmony_ci	struct snd_pcm_substream *substream = lx_stream->stream;
40962306a36Sopenharmony_ci	const unsigned int is_capture = lx_stream->is_capture;
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	int err;
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	const u32 channels = substream->runtime->channels;
41462306a36Sopenharmony_ci	const u32 bytes_per_frame = channels * 3;
41562306a36Sopenharmony_ci	const u32 period_size = substream->runtime->period_size;
41662306a36Sopenharmony_ci	const u32 periods = substream->runtime->periods;
41762306a36Sopenharmony_ci	const u32 period_bytes = period_size * bytes_per_frame;
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	dma_addr_t buf = substream->dma_buffer.addr;
42062306a36Sopenharmony_ci	int i;
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	u32 needed, freed;
42362306a36Sopenharmony_ci	u32 size_array[5];
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	for (i = 0; i != periods; ++i) {
42662306a36Sopenharmony_ci		u32 buffer_index = 0;
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci		err = lx_buffer_ask(chip, 0, is_capture, &needed, &freed,
42962306a36Sopenharmony_ci				    size_array);
43062306a36Sopenharmony_ci		dev_dbg(chip->card->dev, "starting: needed %d, freed %d\n",
43162306a36Sopenharmony_ci			    needed, freed);
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci		err = lx_buffer_give(chip, 0, is_capture, period_bytes,
43462306a36Sopenharmony_ci				     lower_32_bits(buf), upper_32_bits(buf),
43562306a36Sopenharmony_ci				     &buffer_index);
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci		dev_dbg(chip->card->dev, "starting: buffer index %x on 0x%lx (%d bytes)\n",
43862306a36Sopenharmony_ci			    buffer_index, (unsigned long)buf, period_bytes);
43962306a36Sopenharmony_ci		buf += period_bytes;
44062306a36Sopenharmony_ci	}
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	err = lx_buffer_ask(chip, 0, is_capture, &needed, &freed, size_array);
44362306a36Sopenharmony_ci	dev_dbg(chip->card->dev, "starting: needed %d, freed %d\n", needed, freed);
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	dev_dbg(chip->card->dev, "starting: starting stream\n");
44662306a36Sopenharmony_ci	err = lx_stream_start(chip, 0, is_capture);
44762306a36Sopenharmony_ci	if (err < 0)
44862306a36Sopenharmony_ci		dev_err(chip->card->dev, "couldn't start stream\n");
44962306a36Sopenharmony_ci	else
45062306a36Sopenharmony_ci		lx_stream->status = LX_STREAM_STATUS_RUNNING;
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	lx_stream->frame_pos = 0;
45362306a36Sopenharmony_ci}
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_cistatic void lx_trigger_stop(struct lx6464es *chip, struct lx_stream *lx_stream)
45662306a36Sopenharmony_ci{
45762306a36Sopenharmony_ci	const unsigned int is_capture = lx_stream->is_capture;
45862306a36Sopenharmony_ci	int err;
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	dev_dbg(chip->card->dev, "stopping: stopping stream\n");
46162306a36Sopenharmony_ci	err = lx_stream_stop(chip, 0, is_capture);
46262306a36Sopenharmony_ci	if (err < 0)
46362306a36Sopenharmony_ci		dev_err(chip->card->dev, "couldn't stop stream\n");
46462306a36Sopenharmony_ci	else
46562306a36Sopenharmony_ci		lx_stream->status = LX_STREAM_STATUS_FREE;
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci}
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_cistatic void lx_trigger_dispatch_stream(struct lx6464es *chip,
47062306a36Sopenharmony_ci				       struct lx_stream *lx_stream)
47162306a36Sopenharmony_ci{
47262306a36Sopenharmony_ci	switch (lx_stream->status) {
47362306a36Sopenharmony_ci	case LX_STREAM_STATUS_SCHEDULE_RUN:
47462306a36Sopenharmony_ci		lx_trigger_start(chip, lx_stream);
47562306a36Sopenharmony_ci		break;
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	case LX_STREAM_STATUS_SCHEDULE_STOP:
47862306a36Sopenharmony_ci		lx_trigger_stop(chip, lx_stream);
47962306a36Sopenharmony_ci		break;
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	default:
48262306a36Sopenharmony_ci		break;
48362306a36Sopenharmony_ci	}
48462306a36Sopenharmony_ci}
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_cistatic int lx_pcm_trigger_dispatch(struct lx6464es *chip,
48762306a36Sopenharmony_ci				   struct lx_stream *lx_stream, int cmd)
48862306a36Sopenharmony_ci{
48962306a36Sopenharmony_ci	int err = 0;
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	mutex_lock(&chip->lock);
49262306a36Sopenharmony_ci	switch (cmd) {
49362306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
49462306a36Sopenharmony_ci		lx_stream->status = LX_STREAM_STATUS_SCHEDULE_RUN;
49562306a36Sopenharmony_ci		break;
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
49862306a36Sopenharmony_ci		lx_stream->status = LX_STREAM_STATUS_SCHEDULE_STOP;
49962306a36Sopenharmony_ci		break;
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	default:
50262306a36Sopenharmony_ci		err = -EINVAL;
50362306a36Sopenharmony_ci		goto exit;
50462306a36Sopenharmony_ci	}
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	lx_trigger_dispatch_stream(chip, &chip->capture_stream);
50762306a36Sopenharmony_ci	lx_trigger_dispatch_stream(chip, &chip->playback_stream);
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ciexit:
51062306a36Sopenharmony_ci	mutex_unlock(&chip->lock);
51162306a36Sopenharmony_ci	return err;
51262306a36Sopenharmony_ci}
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_cistatic int lx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
51662306a36Sopenharmony_ci{
51762306a36Sopenharmony_ci	struct lx6464es *chip = snd_pcm_substream_chip(substream);
51862306a36Sopenharmony_ci	const int is_capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
51962306a36Sopenharmony_ci	struct lx_stream *stream = is_capture ? &chip->capture_stream :
52062306a36Sopenharmony_ci		&chip->playback_stream;
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	dev_dbg(chip->card->dev, "->lx_pcm_trigger\n");
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	return lx_pcm_trigger_dispatch(chip, stream, cmd);
52562306a36Sopenharmony_ci}
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_cistatic void snd_lx6464es_free(struct snd_card *card)
52862306a36Sopenharmony_ci{
52962306a36Sopenharmony_ci	struct lx6464es *chip = card->private_data;
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	lx_irq_disable(chip);
53262306a36Sopenharmony_ci}
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci/* reset the dsp during initialization */
53562306a36Sopenharmony_cistatic int lx_init_xilinx_reset(struct lx6464es *chip)
53662306a36Sopenharmony_ci{
53762306a36Sopenharmony_ci	int i;
53862306a36Sopenharmony_ci	u32 plx_reg = lx_plx_reg_read(chip, ePLX_CHIPSC);
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	dev_dbg(chip->card->dev, "->lx_init_xilinx_reset\n");
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	/* activate reset of xilinx */
54362306a36Sopenharmony_ci	plx_reg &= ~CHIPSC_RESET_XILINX;
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	lx_plx_reg_write(chip, ePLX_CHIPSC, plx_reg);
54662306a36Sopenharmony_ci	msleep(1);
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	lx_plx_reg_write(chip, ePLX_MBOX3, 0);
54962306a36Sopenharmony_ci	msleep(1);
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci	plx_reg |= CHIPSC_RESET_XILINX;
55262306a36Sopenharmony_ci	lx_plx_reg_write(chip, ePLX_CHIPSC, plx_reg);
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	/* deactivate reset of xilinx */
55562306a36Sopenharmony_ci	for (i = 0; i != 100; ++i) {
55662306a36Sopenharmony_ci		u32 reg_mbox3;
55762306a36Sopenharmony_ci		msleep(10);
55862306a36Sopenharmony_ci		reg_mbox3 = lx_plx_reg_read(chip, ePLX_MBOX3);
55962306a36Sopenharmony_ci		if (reg_mbox3) {
56062306a36Sopenharmony_ci			dev_dbg(chip->card->dev, "xilinx reset done\n");
56162306a36Sopenharmony_ci			dev_dbg(chip->card->dev, "xilinx took %d loops\n", i);
56262306a36Sopenharmony_ci			break;
56362306a36Sopenharmony_ci		}
56462306a36Sopenharmony_ci	}
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	/* todo: add some error handling? */
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	/* clear mr */
56962306a36Sopenharmony_ci	lx_dsp_reg_write(chip, eReg_CSM, 0);
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	/* le xilinx ES peut ne pas etre encore pret, on attend. */
57262306a36Sopenharmony_ci	msleep(600);
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	return 0;
57562306a36Sopenharmony_ci}
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_cistatic int lx_init_xilinx_test(struct lx6464es *chip)
57862306a36Sopenharmony_ci{
57962306a36Sopenharmony_ci	u32 reg;
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	dev_dbg(chip->card->dev, "->lx_init_xilinx_test\n");
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	/* TEST if we have access to Xilinx/MicroBlaze */
58462306a36Sopenharmony_ci	lx_dsp_reg_write(chip, eReg_CSM, 0);
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci	reg = lx_dsp_reg_read(chip, eReg_CSM);
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci	if (reg) {
58962306a36Sopenharmony_ci		dev_err(chip->card->dev, "Problem: Reg_CSM %x.\n", reg);
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci		/* PCI9056_SPACE0_REMAP */
59262306a36Sopenharmony_ci		lx_plx_reg_write(chip, ePLX_PCICR, 1);
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci		reg = lx_dsp_reg_read(chip, eReg_CSM);
59562306a36Sopenharmony_ci		if (reg) {
59662306a36Sopenharmony_ci			dev_err(chip->card->dev, "Error: Reg_CSM %x.\n", reg);
59762306a36Sopenharmony_ci			return -EAGAIN; /* seems to be appropriate */
59862306a36Sopenharmony_ci		}
59962306a36Sopenharmony_ci	}
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	dev_dbg(chip->card->dev, "Xilinx/MicroBlaze access test successful\n");
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci	return 0;
60462306a36Sopenharmony_ci}
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci/* initialize ethersound */
60762306a36Sopenharmony_cistatic int lx_init_ethersound_config(struct lx6464es *chip)
60862306a36Sopenharmony_ci{
60962306a36Sopenharmony_ci	int i;
61062306a36Sopenharmony_ci	u32 orig_conf_es = lx_dsp_reg_read(chip, eReg_CONFES);
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci	/* configure 64 io channels */
61362306a36Sopenharmony_ci	u32 conf_es = (orig_conf_es & CONFES_READ_PART_MASK) |
61462306a36Sopenharmony_ci		(64 << IOCR_INPUTS_OFFSET) |
61562306a36Sopenharmony_ci		(64 << IOCR_OUTPUTS_OFFSET) |
61662306a36Sopenharmony_ci		(FREQ_RATIO_SINGLE_MODE << FREQ_RATIO_OFFSET);
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci	dev_dbg(chip->card->dev, "->lx_init_ethersound\n");
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	chip->freq_ratio = FREQ_RATIO_SINGLE_MODE;
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	/*
62362306a36Sopenharmony_ci	 * write it to the card !
62462306a36Sopenharmony_ci	 * this actually kicks the ES xilinx, the first time since poweron.
62562306a36Sopenharmony_ci	 * the MAC address in the Reg_ADMACESMSB Reg_ADMACESLSB registers
62662306a36Sopenharmony_ci	 * is not ready before this is done, and the bit 2 in Reg_CSES is set.
62762306a36Sopenharmony_ci	 * */
62862306a36Sopenharmony_ci	lx_dsp_reg_write(chip, eReg_CONFES, conf_es);
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	for (i = 0; i != 1000; ++i) {
63162306a36Sopenharmony_ci		if (lx_dsp_reg_read(chip, eReg_CSES) & 4) {
63262306a36Sopenharmony_ci			dev_dbg(chip->card->dev, "ethersound initialized after %dms\n",
63362306a36Sopenharmony_ci				   i);
63462306a36Sopenharmony_ci			goto ethersound_initialized;
63562306a36Sopenharmony_ci		}
63662306a36Sopenharmony_ci		msleep(1);
63762306a36Sopenharmony_ci	}
63862306a36Sopenharmony_ci	dev_warn(chip->card->dev,
63962306a36Sopenharmony_ci		   "ethersound could not be initialized after %dms\n", i);
64062306a36Sopenharmony_ci	return -ETIMEDOUT;
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci ethersound_initialized:
64362306a36Sopenharmony_ci	dev_dbg(chip->card->dev, "ethersound initialized\n");
64462306a36Sopenharmony_ci	return 0;
64562306a36Sopenharmony_ci}
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_cistatic int lx_init_get_version_features(struct lx6464es *chip)
64862306a36Sopenharmony_ci{
64962306a36Sopenharmony_ci	u32 dsp_version;
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci	int err;
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	dev_dbg(chip->card->dev, "->lx_init_get_version_features\n");
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	err = lx_dsp_get_version(chip, &dsp_version);
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	if (err == 0) {
65862306a36Sopenharmony_ci		u32 freq;
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci		dev_info(chip->card->dev, "DSP version: V%02d.%02d #%d\n",
66162306a36Sopenharmony_ci			   (dsp_version>>16) & 0xff, (dsp_version>>8) & 0xff,
66262306a36Sopenharmony_ci			   dsp_version & 0xff);
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci		/* later: what firmware version do we expect? */
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci		/* retrieve Play/Rec features */
66762306a36Sopenharmony_ci		/* done here because we may have to handle alternate
66862306a36Sopenharmony_ci		 * DSP files. */
66962306a36Sopenharmony_ci		/* later */
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci		/* init the EtherSound sample rate */
67262306a36Sopenharmony_ci		err = lx_dsp_get_clock_frequency(chip, &freq);
67362306a36Sopenharmony_ci		if (err == 0)
67462306a36Sopenharmony_ci			chip->board_sample_rate = freq;
67562306a36Sopenharmony_ci		dev_dbg(chip->card->dev, "actual clock frequency %d\n", freq);
67662306a36Sopenharmony_ci	} else {
67762306a36Sopenharmony_ci		dev_err(chip->card->dev, "DSP corrupted \n");
67862306a36Sopenharmony_ci		err = -EAGAIN;
67962306a36Sopenharmony_ci	}
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	return err;
68262306a36Sopenharmony_ci}
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_cistatic int lx_set_granularity(struct lx6464es *chip, u32 gran)
68562306a36Sopenharmony_ci{
68662306a36Sopenharmony_ci	int err = 0;
68762306a36Sopenharmony_ci	u32 snapped_gran = MICROBLAZE_IBL_MIN;
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	dev_dbg(chip->card->dev, "->lx_set_granularity\n");
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci	/* blocksize is a power of 2 */
69262306a36Sopenharmony_ci	while ((snapped_gran < gran) &&
69362306a36Sopenharmony_ci	       (snapped_gran < MICROBLAZE_IBL_MAX)) {
69462306a36Sopenharmony_ci		snapped_gran *= 2;
69562306a36Sopenharmony_ci	}
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci	if (snapped_gran == chip->pcm_granularity)
69862306a36Sopenharmony_ci		return 0;
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci	err = lx_dsp_set_granularity(chip, snapped_gran);
70162306a36Sopenharmony_ci	if (err < 0) {
70262306a36Sopenharmony_ci		dev_warn(chip->card->dev, "could not set granularity\n");
70362306a36Sopenharmony_ci		err = -EAGAIN;
70462306a36Sopenharmony_ci	}
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci	if (snapped_gran != gran)
70762306a36Sopenharmony_ci		dev_err(chip->card->dev, "snapped blocksize to %d\n", snapped_gran);
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	dev_dbg(chip->card->dev, "set blocksize on board %d\n", snapped_gran);
71062306a36Sopenharmony_ci	chip->pcm_granularity = snapped_gran;
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci	return err;
71362306a36Sopenharmony_ci}
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci/* initialize and test the xilinx dsp chip */
71662306a36Sopenharmony_cistatic int lx_init_dsp(struct lx6464es *chip)
71762306a36Sopenharmony_ci{
71862306a36Sopenharmony_ci	int err;
71962306a36Sopenharmony_ci	int i;
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci	dev_dbg(chip->card->dev, "->lx_init_dsp\n");
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci	dev_dbg(chip->card->dev, "initialize board\n");
72462306a36Sopenharmony_ci	err = lx_init_xilinx_reset(chip);
72562306a36Sopenharmony_ci	if (err)
72662306a36Sopenharmony_ci		return err;
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci	dev_dbg(chip->card->dev, "testing board\n");
72962306a36Sopenharmony_ci	err = lx_init_xilinx_test(chip);
73062306a36Sopenharmony_ci	if (err)
73162306a36Sopenharmony_ci		return err;
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci	dev_dbg(chip->card->dev, "initialize ethersound configuration\n");
73462306a36Sopenharmony_ci	err = lx_init_ethersound_config(chip);
73562306a36Sopenharmony_ci	if (err)
73662306a36Sopenharmony_ci		return err;
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci	lx_irq_enable(chip);
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci	/** \todo the mac address should be ready by not, but it isn't,
74162306a36Sopenharmony_ci	 *  so we wait for it */
74262306a36Sopenharmony_ci	for (i = 0; i != 1000; ++i) {
74362306a36Sopenharmony_ci		err = lx_dsp_get_mac(chip);
74462306a36Sopenharmony_ci		if (err)
74562306a36Sopenharmony_ci			return err;
74662306a36Sopenharmony_ci		if (chip->mac_address[0] || chip->mac_address[1] || chip->mac_address[2] ||
74762306a36Sopenharmony_ci		    chip->mac_address[3] || chip->mac_address[4] || chip->mac_address[5])
74862306a36Sopenharmony_ci			goto mac_ready;
74962306a36Sopenharmony_ci		msleep(1);
75062306a36Sopenharmony_ci	}
75162306a36Sopenharmony_ci	return -ETIMEDOUT;
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_cimac_ready:
75462306a36Sopenharmony_ci	dev_dbg(chip->card->dev, "mac address ready read after: %dms\n", i);
75562306a36Sopenharmony_ci	dev_info(chip->card->dev,
75662306a36Sopenharmony_ci		 "mac address: %02X.%02X.%02X.%02X.%02X.%02X\n",
75762306a36Sopenharmony_ci		   chip->mac_address[0], chip->mac_address[1], chip->mac_address[2],
75862306a36Sopenharmony_ci		   chip->mac_address[3], chip->mac_address[4], chip->mac_address[5]);
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ci	err = lx_init_get_version_features(chip);
76162306a36Sopenharmony_ci	if (err)
76262306a36Sopenharmony_ci		return err;
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci	lx_set_granularity(chip, MICROBLAZE_IBL_DEFAULT);
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci	chip->playback_mute = 0;
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci	return err;
76962306a36Sopenharmony_ci}
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_cistatic const struct snd_pcm_ops lx_ops_playback = {
77262306a36Sopenharmony_ci	.open      = lx_pcm_open,
77362306a36Sopenharmony_ci	.close     = lx_pcm_close,
77462306a36Sopenharmony_ci	.prepare   = lx_pcm_prepare,
77562306a36Sopenharmony_ci	.hw_params = lx_pcm_hw_params_playback,
77662306a36Sopenharmony_ci	.hw_free   = lx_pcm_hw_free,
77762306a36Sopenharmony_ci	.trigger   = lx_pcm_trigger,
77862306a36Sopenharmony_ci	.pointer   = lx_pcm_stream_pointer,
77962306a36Sopenharmony_ci};
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_cistatic const struct snd_pcm_ops lx_ops_capture = {
78262306a36Sopenharmony_ci	.open      = lx_pcm_open,
78362306a36Sopenharmony_ci	.close     = lx_pcm_close,
78462306a36Sopenharmony_ci	.prepare   = lx_pcm_prepare,
78562306a36Sopenharmony_ci	.hw_params = lx_pcm_hw_params_capture,
78662306a36Sopenharmony_ci	.hw_free   = lx_pcm_hw_free,
78762306a36Sopenharmony_ci	.trigger   = lx_pcm_trigger,
78862306a36Sopenharmony_ci	.pointer   = lx_pcm_stream_pointer,
78962306a36Sopenharmony_ci};
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_cistatic int lx_pcm_create(struct lx6464es *chip)
79262306a36Sopenharmony_ci{
79362306a36Sopenharmony_ci	int err;
79462306a36Sopenharmony_ci	struct snd_pcm *pcm;
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci	u32 size = 64 *		     /* channels */
79762306a36Sopenharmony_ci		3 *		     /* 24 bit samples */
79862306a36Sopenharmony_ci		MAX_STREAM_BUFFER *  /* periods */
79962306a36Sopenharmony_ci		MICROBLAZE_IBL_MAX * /* frames per period */
80062306a36Sopenharmony_ci		2;		     /* duplex */
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci	size = PAGE_ALIGN(size);
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	/* hardcoded device name & channel count */
80562306a36Sopenharmony_ci	err = snd_pcm_new(chip->card, (char *)card_name, 0,
80662306a36Sopenharmony_ci			  1, 1, &pcm);
80762306a36Sopenharmony_ci	if (err < 0)
80862306a36Sopenharmony_ci		return err;
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci	pcm->private_data = chip;
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &lx_ops_playback);
81362306a36Sopenharmony_ci	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &lx_ops_capture);
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_ci	pcm->info_flags = 0;
81662306a36Sopenharmony_ci	pcm->nonatomic = true;
81762306a36Sopenharmony_ci	strcpy(pcm->name, card_name);
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci	snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
82062306a36Sopenharmony_ci				       &chip->pci->dev, size, size);
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci	chip->pcm = pcm;
82362306a36Sopenharmony_ci	chip->capture_stream.is_capture = 1;
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci	return 0;
82662306a36Sopenharmony_ci}
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_cistatic int lx_control_playback_info(struct snd_kcontrol *kcontrol,
82962306a36Sopenharmony_ci				    struct snd_ctl_elem_info *uinfo)
83062306a36Sopenharmony_ci{
83162306a36Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
83262306a36Sopenharmony_ci	uinfo->count = 1;
83362306a36Sopenharmony_ci	uinfo->value.integer.min = 0;
83462306a36Sopenharmony_ci	uinfo->value.integer.max = 1;
83562306a36Sopenharmony_ci	return 0;
83662306a36Sopenharmony_ci}
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_cistatic int lx_control_playback_get(struct snd_kcontrol *kcontrol,
83962306a36Sopenharmony_ci				   struct snd_ctl_elem_value *ucontrol)
84062306a36Sopenharmony_ci{
84162306a36Sopenharmony_ci	struct lx6464es *chip = snd_kcontrol_chip(kcontrol);
84262306a36Sopenharmony_ci	ucontrol->value.integer.value[0] = chip->playback_mute;
84362306a36Sopenharmony_ci	return 0;
84462306a36Sopenharmony_ci}
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_cistatic int lx_control_playback_put(struct snd_kcontrol *kcontrol,
84762306a36Sopenharmony_ci				   struct snd_ctl_elem_value *ucontrol)
84862306a36Sopenharmony_ci{
84962306a36Sopenharmony_ci	struct lx6464es *chip = snd_kcontrol_chip(kcontrol);
85062306a36Sopenharmony_ci	int changed = 0;
85162306a36Sopenharmony_ci	int current_value = chip->playback_mute;
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_ci	if (current_value != ucontrol->value.integer.value[0]) {
85462306a36Sopenharmony_ci		lx_level_unmute(chip, 0, !current_value);
85562306a36Sopenharmony_ci		chip->playback_mute = !current_value;
85662306a36Sopenharmony_ci		changed = 1;
85762306a36Sopenharmony_ci	}
85862306a36Sopenharmony_ci	return changed;
85962306a36Sopenharmony_ci}
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_cistatic const struct snd_kcontrol_new lx_control_playback_switch = {
86262306a36Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
86362306a36Sopenharmony_ci	.name = "PCM Playback Switch",
86462306a36Sopenharmony_ci	.index = 0,
86562306a36Sopenharmony_ci	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
86662306a36Sopenharmony_ci	.private_value = 0,
86762306a36Sopenharmony_ci	.info = lx_control_playback_info,
86862306a36Sopenharmony_ci	.get = lx_control_playback_get,
86962306a36Sopenharmony_ci	.put = lx_control_playback_put
87062306a36Sopenharmony_ci};
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_cistatic void lx_proc_levels_read(struct snd_info_entry *entry,
87562306a36Sopenharmony_ci				struct snd_info_buffer *buffer)
87662306a36Sopenharmony_ci{
87762306a36Sopenharmony_ci	u32 levels[64];
87862306a36Sopenharmony_ci	int err;
87962306a36Sopenharmony_ci	int i, j;
88062306a36Sopenharmony_ci	struct lx6464es *chip = entry->private_data;
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci	snd_iprintf(buffer, "capture levels:\n");
88362306a36Sopenharmony_ci	err = lx_level_peaks(chip, 1, 64, levels);
88462306a36Sopenharmony_ci	if (err < 0)
88562306a36Sopenharmony_ci		return;
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_ci	for (i = 0; i != 8; ++i) {
88862306a36Sopenharmony_ci		for (j = 0; j != 8; ++j)
88962306a36Sopenharmony_ci			snd_iprintf(buffer, "%08x ", levels[i*8+j]);
89062306a36Sopenharmony_ci		snd_iprintf(buffer, "\n");
89162306a36Sopenharmony_ci	}
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_ci	snd_iprintf(buffer, "\nplayback levels:\n");
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci	err = lx_level_peaks(chip, 0, 64, levels);
89662306a36Sopenharmony_ci	if (err < 0)
89762306a36Sopenharmony_ci		return;
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_ci	for (i = 0; i != 8; ++i) {
90062306a36Sopenharmony_ci		for (j = 0; j != 8; ++j)
90162306a36Sopenharmony_ci			snd_iprintf(buffer, "%08x ", levels[i*8+j]);
90262306a36Sopenharmony_ci		snd_iprintf(buffer, "\n");
90362306a36Sopenharmony_ci	}
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci	snd_iprintf(buffer, "\n");
90662306a36Sopenharmony_ci}
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_cistatic int lx_proc_create(struct snd_card *card, struct lx6464es *chip)
90962306a36Sopenharmony_ci{
91062306a36Sopenharmony_ci	return snd_card_ro_proc_new(card, "levels", chip, lx_proc_levels_read);
91162306a36Sopenharmony_ci}
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_cistatic int snd_lx6464es_create(struct snd_card *card,
91562306a36Sopenharmony_ci			       struct pci_dev *pci)
91662306a36Sopenharmony_ci{
91762306a36Sopenharmony_ci	struct lx6464es *chip = card->private_data;
91862306a36Sopenharmony_ci	int err;
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ci	dev_dbg(card->dev, "->snd_lx6464es_create\n");
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_ci	/* enable PCI device */
92362306a36Sopenharmony_ci	err = pcim_enable_device(pci);
92462306a36Sopenharmony_ci	if (err < 0)
92562306a36Sopenharmony_ci		return err;
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_ci	pci_set_master(pci);
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_ci	/* check if we can restrict PCI DMA transfers to 32 bits */
93062306a36Sopenharmony_ci	err = dma_set_mask(&pci->dev, DMA_BIT_MASK(32));
93162306a36Sopenharmony_ci	if (err < 0) {
93262306a36Sopenharmony_ci		dev_err(card->dev,
93362306a36Sopenharmony_ci			"architecture does not support 32bit PCI busmaster DMA\n");
93462306a36Sopenharmony_ci		return -ENXIO;
93562306a36Sopenharmony_ci	}
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_ci	chip->card = card;
93862306a36Sopenharmony_ci	chip->pci = pci;
93962306a36Sopenharmony_ci	chip->irq = -1;
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_ci	/* initialize synchronization structs */
94262306a36Sopenharmony_ci	mutex_init(&chip->lock);
94362306a36Sopenharmony_ci	mutex_init(&chip->msg_lock);
94462306a36Sopenharmony_ci	mutex_init(&chip->setup_mutex);
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci	/* request resources */
94762306a36Sopenharmony_ci	err = pci_request_regions(pci, card_name);
94862306a36Sopenharmony_ci	if (err < 0)
94962306a36Sopenharmony_ci		return err;
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_ci	/* plx port */
95262306a36Sopenharmony_ci	chip->port_plx = pci_resource_start(pci, 1);
95362306a36Sopenharmony_ci	chip->port_plx_remapped = devm_ioport_map(&pci->dev, chip->port_plx,
95462306a36Sopenharmony_ci						  pci_resource_len(pci, 1));
95562306a36Sopenharmony_ci	if (!chip->port_plx_remapped)
95662306a36Sopenharmony_ci		return -ENOMEM;
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci	/* dsp port */
95962306a36Sopenharmony_ci	chip->port_dsp_bar = pcim_iomap(pci, 2, 0);
96062306a36Sopenharmony_ci	if (!chip->port_dsp_bar)
96162306a36Sopenharmony_ci		return -ENOMEM;
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ci	err = devm_request_threaded_irq(&pci->dev, pci->irq, lx_interrupt,
96462306a36Sopenharmony_ci					lx_threaded_irq, IRQF_SHARED,
96562306a36Sopenharmony_ci					KBUILD_MODNAME, chip);
96662306a36Sopenharmony_ci	if (err) {
96762306a36Sopenharmony_ci		dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq);
96862306a36Sopenharmony_ci		return err;
96962306a36Sopenharmony_ci	}
97062306a36Sopenharmony_ci	chip->irq = pci->irq;
97162306a36Sopenharmony_ci	card->sync_irq = chip->irq;
97262306a36Sopenharmony_ci	card->private_free = snd_lx6464es_free;
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_ci	err = lx_init_dsp(chip);
97562306a36Sopenharmony_ci	if (err < 0) {
97662306a36Sopenharmony_ci		dev_err(card->dev, "error during DSP initialization\n");
97762306a36Sopenharmony_ci		return err;
97862306a36Sopenharmony_ci	}
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci	err = lx_pcm_create(chip);
98162306a36Sopenharmony_ci	if (err < 0)
98262306a36Sopenharmony_ci		return err;
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci	err = lx_proc_create(card, chip);
98562306a36Sopenharmony_ci	if (err < 0)
98662306a36Sopenharmony_ci		return err;
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci	err = snd_ctl_add(card, snd_ctl_new1(&lx_control_playback_switch,
98962306a36Sopenharmony_ci					     chip));
99062306a36Sopenharmony_ci	if (err < 0)
99162306a36Sopenharmony_ci		return err;
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_ci	return 0;
99462306a36Sopenharmony_ci}
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_cistatic int snd_lx6464es_probe(struct pci_dev *pci,
99762306a36Sopenharmony_ci			      const struct pci_device_id *pci_id)
99862306a36Sopenharmony_ci{
99962306a36Sopenharmony_ci	static int dev;
100062306a36Sopenharmony_ci	struct snd_card *card;
100162306a36Sopenharmony_ci	struct lx6464es *chip;
100262306a36Sopenharmony_ci	int err;
100362306a36Sopenharmony_ci
100462306a36Sopenharmony_ci	dev_dbg(&pci->dev, "->snd_lx6464es_probe\n");
100562306a36Sopenharmony_ci
100662306a36Sopenharmony_ci	if (dev >= SNDRV_CARDS)
100762306a36Sopenharmony_ci		return -ENODEV;
100862306a36Sopenharmony_ci	if (!enable[dev]) {
100962306a36Sopenharmony_ci		dev++;
101062306a36Sopenharmony_ci		return -ENOENT;
101162306a36Sopenharmony_ci	}
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_ci	err = snd_devm_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
101462306a36Sopenharmony_ci				sizeof(*chip), &card);
101562306a36Sopenharmony_ci	if (err < 0)
101662306a36Sopenharmony_ci		return err;
101762306a36Sopenharmony_ci	chip = card->private_data;
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_ci	err = snd_lx6464es_create(card, pci);
102062306a36Sopenharmony_ci	if (err < 0) {
102162306a36Sopenharmony_ci		dev_err(card->dev, "error during snd_lx6464es_create\n");
102262306a36Sopenharmony_ci		goto error;
102362306a36Sopenharmony_ci	}
102462306a36Sopenharmony_ci
102562306a36Sopenharmony_ci	strcpy(card->driver, "LX6464ES");
102662306a36Sopenharmony_ci	sprintf(card->id, "LX6464ES_%02X%02X%02X",
102762306a36Sopenharmony_ci		chip->mac_address[3], chip->mac_address[4], chip->mac_address[5]);
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_ci	sprintf(card->shortname, "LX6464ES %02X.%02X.%02X.%02X.%02X.%02X",
103062306a36Sopenharmony_ci		chip->mac_address[0], chip->mac_address[1], chip->mac_address[2],
103162306a36Sopenharmony_ci		chip->mac_address[3], chip->mac_address[4], chip->mac_address[5]);
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_ci	sprintf(card->longname, "%s at 0x%lx, 0x%p, irq %i",
103462306a36Sopenharmony_ci		card->shortname, chip->port_plx,
103562306a36Sopenharmony_ci		chip->port_dsp_bar, chip->irq);
103662306a36Sopenharmony_ci
103762306a36Sopenharmony_ci	err = snd_card_register(card);
103862306a36Sopenharmony_ci	if (err < 0)
103962306a36Sopenharmony_ci		goto error;
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_ci	dev_dbg(chip->card->dev, "initialization successful\n");
104262306a36Sopenharmony_ci	pci_set_drvdata(pci, card);
104362306a36Sopenharmony_ci	dev++;
104462306a36Sopenharmony_ci	return 0;
104562306a36Sopenharmony_ci
104662306a36Sopenharmony_ci error:
104762306a36Sopenharmony_ci	snd_card_free(card);
104862306a36Sopenharmony_ci	return err;
104962306a36Sopenharmony_ci}
105062306a36Sopenharmony_ci
105162306a36Sopenharmony_cistatic struct pci_driver lx6464es_driver = {
105262306a36Sopenharmony_ci	.name =     KBUILD_MODNAME,
105362306a36Sopenharmony_ci	.id_table = snd_lx6464es_ids,
105462306a36Sopenharmony_ci	.probe =    snd_lx6464es_probe,
105562306a36Sopenharmony_ci};
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_cimodule_pci_driver(lx6464es_driver);
1058