xref: /openbmc/linux/sound/pci/lx6464es/lx6464es.c (revision 5fbb73cb)
177f5075aSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
202bec490STim Blechmann /* -*- linux-c -*- *
302bec490STim Blechmann  *
402bec490STim Blechmann  * ALSA driver for the digigram lx6464es interface
502bec490STim Blechmann  *
602bec490STim Blechmann  * Copyright (c) 2008, 2009 Tim Blechmann <tim@klingt.org>
702bec490STim Blechmann  */
802bec490STim Blechmann 
902bec490STim Blechmann #include <linux/module.h>
1002bec490STim Blechmann #include <linux/init.h>
1102bec490STim Blechmann #include <linux/pci.h>
1202bec490STim Blechmann #include <linux/delay.h>
135a0e3ad6STejun Heo #include <linux/slab.h>
1402bec490STim Blechmann 
1502bec490STim Blechmann #include <sound/initval.h>
1602bec490STim Blechmann #include <sound/control.h>
1702bec490STim Blechmann #include <sound/info.h>
1802bec490STim Blechmann 
1902bec490STim Blechmann #include "lx6464es.h"
2002bec490STim Blechmann 
2102bec490STim Blechmann MODULE_AUTHOR("Tim Blechmann");
2202bec490STim Blechmann MODULE_LICENSE("GPL");
2302bec490STim Blechmann MODULE_DESCRIPTION("digigram lx6464es");
2402bec490STim Blechmann 
2502bec490STim Blechmann static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
2602bec490STim Blechmann static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
27a67ff6a5SRusty Russell static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
2802bec490STim Blechmann 
29de0525caSTim Blechmann module_param_array(index, int, NULL, 0444);
30de0525caSTim Blechmann MODULE_PARM_DESC(index, "Index value for Digigram LX6464ES interface.");
31de0525caSTim Blechmann module_param_array(id, charp, NULL, 0444);
32de0525caSTim Blechmann MODULE_PARM_DESC(id, "ID string for  Digigram LX6464ES interface.");
33de0525caSTim Blechmann module_param_array(enable, bool, NULL, 0444);
34de0525caSTim Blechmann MODULE_PARM_DESC(enable, "Enable/disable specific Digigram LX6464ES soundcards.");
35de0525caSTim Blechmann 
3602bec490STim Blechmann static const char card_name[] = "LX6464ES";
3702bec490STim Blechmann 
3802bec490STim Blechmann 
3902bec490STim Blechmann #define PCI_DEVICE_ID_PLX_LX6464ES		PCI_DEVICE_ID_PLX_9056
4002bec490STim Blechmann 
419baa3c34SBenoit Taine static const struct pci_device_id snd_lx6464es_ids[] = {
421983126fSTakashi Iwai 	{ PCI_DEVICE_SUB(PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_LX6464ES,
431983126fSTakashi Iwai 			 PCI_VENDOR_ID_DIGIGRAM,
441983126fSTakashi Iwai 			 PCI_SUBDEVICE_ID_DIGIGRAM_LX6464ES_SERIAL_SUBSYSTEM),
4502bec490STim Blechmann 	},			/* LX6464ES */
461983126fSTakashi Iwai 	{ PCI_DEVICE_SUB(PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_LX6464ES,
471983126fSTakashi Iwai 			 PCI_VENDOR_ID_DIGIGRAM,
481983126fSTakashi Iwai 			 PCI_SUBDEVICE_ID_DIGIGRAM_LX6464ES_CAE_SERIAL_SUBSYSTEM),
4902bec490STim Blechmann 	},			/* LX6464ES-CAE */
50789492f0STim Blechmann 	{ PCI_DEVICE_SUB(PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_LX6464ES,
51789492f0STim Blechmann 			 PCI_VENDOR_ID_DIGIGRAM,
52789492f0STim Blechmann 			 PCI_SUBDEVICE_ID_DIGIGRAM_LX6464ESE_SERIAL_SUBSYSTEM),
53789492f0STim Blechmann 	},			/* LX6464ESe */
54789492f0STim Blechmann 	{ PCI_DEVICE_SUB(PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_LX6464ES,
55789492f0STim Blechmann 			 PCI_VENDOR_ID_DIGIGRAM,
56789492f0STim Blechmann 			 PCI_SUBDEVICE_ID_DIGIGRAM_LX6464ESE_CAE_SERIAL_SUBSYSTEM),
57789492f0STim Blechmann 	},			/* LX6464ESe-CAE */
5802bec490STim Blechmann 	{ 0, },
5902bec490STim Blechmann };
6002bec490STim Blechmann 
6102bec490STim Blechmann MODULE_DEVICE_TABLE(pci, snd_lx6464es_ids);
6202bec490STim Blechmann 
6302bec490STim Blechmann 
6402bec490STim Blechmann 
6502bec490STim Blechmann /* PGO pour USERo dans le registre pci_0x06/loc_0xEC */
6602bec490STim Blechmann #define CHIPSC_RESET_XILINX (1L<<16)
6702bec490STim Blechmann 
6802bec490STim Blechmann 
6902bec490STim Blechmann /* alsa callbacks */
70033bb567SBhumika Goyal static const struct snd_pcm_hardware lx_caps = {
7102bec490STim Blechmann 	.info             = (SNDRV_PCM_INFO_MMAP |
7202bec490STim Blechmann 			     SNDRV_PCM_INFO_INTERLEAVED |
7302bec490STim Blechmann 			     SNDRV_PCM_INFO_MMAP_VALID |
7402bec490STim Blechmann 			     SNDRV_PCM_INFO_SYNC_START),
7502bec490STim Blechmann 	.formats	  = (SNDRV_PCM_FMTBIT_S16_LE |
7602bec490STim Blechmann 			     SNDRV_PCM_FMTBIT_S16_BE |
7702bec490STim Blechmann 			     SNDRV_PCM_FMTBIT_S24_3LE |
7802bec490STim Blechmann 			     SNDRV_PCM_FMTBIT_S24_3BE),
7902bec490STim Blechmann 	.rates            = (SNDRV_PCM_RATE_CONTINUOUS |
8002bec490STim Blechmann 			     SNDRV_PCM_RATE_8000_192000),
8102bec490STim Blechmann 	.rate_min         = 8000,
8202bec490STim Blechmann 	.rate_max         = 192000,
8302bec490STim Blechmann 	.channels_min     = 2,
8402bec490STim Blechmann 	.channels_max     = 64,
8502bec490STim Blechmann 	.buffer_bytes_max = 64*2*3*MICROBLAZE_IBL_MAX*MAX_STREAM_BUFFER,
8602bec490STim Blechmann 	.period_bytes_min = (2*2*MICROBLAZE_IBL_MIN*2),
8702bec490STim Blechmann 	.period_bytes_max = (4*64*MICROBLAZE_IBL_MAX*MAX_STREAM_BUFFER),
8802bec490STim Blechmann 	.periods_min      = 2,
8902bec490STim Blechmann 	.periods_max      = MAX_STREAM_BUFFER,
9002bec490STim Blechmann };
9102bec490STim Blechmann 
9202bec490STim Blechmann static int lx_set_granularity(struct lx6464es *chip, u32 gran);
9302bec490STim Blechmann 
9402bec490STim Blechmann 
lx_hardware_open(struct lx6464es * chip,struct snd_pcm_substream * substream)9502bec490STim Blechmann static int lx_hardware_open(struct lx6464es *chip,
9602bec490STim Blechmann 			    struct snd_pcm_substream *substream)
9702bec490STim Blechmann {
9802bec490STim Blechmann 	int err = 0;
9902bec490STim Blechmann 	struct snd_pcm_runtime *runtime = substream->runtime;
10002bec490STim Blechmann 	int channels = runtime->channels;
10102bec490STim Blechmann 	int is_capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
10202bec490STim Blechmann 
10302bec490STim Blechmann 	snd_pcm_uframes_t period_size = runtime->period_size;
10402bec490STim Blechmann 
105be4e6d3cSTakashi Iwai 	dev_dbg(chip->card->dev, "allocating pipe for %d channels\n", channels);
10602bec490STim Blechmann 	err = lx_pipe_allocate(chip, 0, is_capture, channels);
10702bec490STim Blechmann 	if (err < 0) {
108be4e6d3cSTakashi Iwai 		dev_err(chip->card->dev, LXP "allocating pipe failed\n");
10902bec490STim Blechmann 		return err;
11002bec490STim Blechmann 	}
11102bec490STim Blechmann 
11202bec490STim Blechmann 	err = lx_set_granularity(chip, period_size);
11302bec490STim Blechmann 	if (err < 0) {
114be4e6d3cSTakashi Iwai 		dev_err(chip->card->dev, "setting granularity to %ld failed\n",
11502bec490STim Blechmann 			   period_size);
11602bec490STim Blechmann 		return err;
11702bec490STim Blechmann 	}
11802bec490STim Blechmann 
11902bec490STim Blechmann 	return 0;
12002bec490STim Blechmann }
12102bec490STim Blechmann 
lx_hardware_start(struct lx6464es * chip,struct snd_pcm_substream * substream)12202bec490STim Blechmann static int lx_hardware_start(struct lx6464es *chip,
12302bec490STim Blechmann 			     struct snd_pcm_substream *substream)
12402bec490STim Blechmann {
12502bec490STim Blechmann 	int err = 0;
12602bec490STim Blechmann 	struct snd_pcm_runtime *runtime = substream->runtime;
12702bec490STim Blechmann 	int is_capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
12802bec490STim Blechmann 
129be4e6d3cSTakashi Iwai 	dev_dbg(chip->card->dev, "setting stream format\n");
13002bec490STim Blechmann 	err = lx_stream_set_format(chip, runtime, 0, is_capture);
13102bec490STim Blechmann 	if (err < 0) {
132be4e6d3cSTakashi Iwai 		dev_err(chip->card->dev, "setting stream format failed\n");
13302bec490STim Blechmann 		return err;
13402bec490STim Blechmann 	}
13502bec490STim Blechmann 
136be4e6d3cSTakashi Iwai 	dev_dbg(chip->card->dev, "starting pipe\n");
13702bec490STim Blechmann 	err = lx_pipe_start(chip, 0, is_capture);
13802bec490STim Blechmann 	if (err < 0) {
139be4e6d3cSTakashi Iwai 		dev_err(chip->card->dev, "starting pipe failed\n");
14002bec490STim Blechmann 		return err;
14102bec490STim Blechmann 	}
14202bec490STim Blechmann 
143be4e6d3cSTakashi Iwai 	dev_dbg(chip->card->dev, "waiting for pipe to start\n");
14402bec490STim Blechmann 	err = lx_pipe_wait_for_start(chip, 0, is_capture);
14502bec490STim Blechmann 	if (err < 0) {
146be4e6d3cSTakashi Iwai 		dev_err(chip->card->dev, "waiting for pipe failed\n");
14702bec490STim Blechmann 		return err;
14802bec490STim Blechmann 	}
14902bec490STim Blechmann 
15002bec490STim Blechmann 	return err;
15102bec490STim Blechmann }
15202bec490STim Blechmann 
15302bec490STim Blechmann 
lx_hardware_stop(struct lx6464es * chip,struct snd_pcm_substream * substream)15402bec490STim Blechmann static int lx_hardware_stop(struct lx6464es *chip,
15502bec490STim Blechmann 			    struct snd_pcm_substream *substream)
15602bec490STim Blechmann {
15702bec490STim Blechmann 	int err = 0;
15802bec490STim Blechmann 	int is_capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
15902bec490STim Blechmann 
160be4e6d3cSTakashi Iwai 	dev_dbg(chip->card->dev, "pausing pipe\n");
16102bec490STim Blechmann 	err = lx_pipe_pause(chip, 0, is_capture);
16202bec490STim Blechmann 	if (err < 0) {
163be4e6d3cSTakashi Iwai 		dev_err(chip->card->dev, "pausing pipe failed\n");
16402bec490STim Blechmann 		return err;
16502bec490STim Blechmann 	}
16602bec490STim Blechmann 
167be4e6d3cSTakashi Iwai 	dev_dbg(chip->card->dev, "waiting for pipe to become idle\n");
16802bec490STim Blechmann 	err = lx_pipe_wait_for_idle(chip, 0, is_capture);
16902bec490STim Blechmann 	if (err < 0) {
170be4e6d3cSTakashi Iwai 		dev_err(chip->card->dev, "waiting for pipe failed\n");
17102bec490STim Blechmann 		return err;
17202bec490STim Blechmann 	}
17302bec490STim Blechmann 
174be4e6d3cSTakashi Iwai 	dev_dbg(chip->card->dev, "stopping pipe\n");
17502bec490STim Blechmann 	err = lx_pipe_stop(chip, 0, is_capture);
17602bec490STim Blechmann 	if (err < 0) {
177be4e6d3cSTakashi Iwai 		dev_err(chip->card->dev, "stopping pipe failed\n");
17802bec490STim Blechmann 		return err;
17902bec490STim Blechmann 	}
18002bec490STim Blechmann 
18102bec490STim Blechmann 	return err;
18202bec490STim Blechmann }
18302bec490STim Blechmann 
18402bec490STim Blechmann 
lx_hardware_close(struct lx6464es * chip,struct snd_pcm_substream * substream)18502bec490STim Blechmann static int lx_hardware_close(struct lx6464es *chip,
18602bec490STim Blechmann 			     struct snd_pcm_substream *substream)
18702bec490STim Blechmann {
18802bec490STim Blechmann 	int err = 0;
18902bec490STim Blechmann 	int is_capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
19002bec490STim Blechmann 
191be4e6d3cSTakashi Iwai 	dev_dbg(chip->card->dev, "releasing pipe\n");
19202bec490STim Blechmann 	err = lx_pipe_release(chip, 0, is_capture);
19302bec490STim Blechmann 	if (err < 0) {
194be4e6d3cSTakashi Iwai 		dev_err(chip->card->dev, "releasing pipe failed\n");
19502bec490STim Blechmann 		return err;
19602bec490STim Blechmann 	}
19702bec490STim Blechmann 
19802bec490STim Blechmann 	return err;
19902bec490STim Blechmann }
20002bec490STim Blechmann 
20102bec490STim Blechmann 
lx_pcm_open(struct snd_pcm_substream * substream)20202bec490STim Blechmann static int lx_pcm_open(struct snd_pcm_substream *substream)
20302bec490STim Blechmann {
20402bec490STim Blechmann 	struct lx6464es *chip = snd_pcm_substream_chip(substream);
20502bec490STim Blechmann 	struct snd_pcm_runtime *runtime = substream->runtime;
20602bec490STim Blechmann 	int err = 0;
20702bec490STim Blechmann 	int board_rate;
20802bec490STim Blechmann 
209be4e6d3cSTakashi Iwai 	dev_dbg(chip->card->dev, "->lx_pcm_open\n");
21002bec490STim Blechmann 	mutex_lock(&chip->setup_mutex);
21102bec490STim Blechmann 
21202bec490STim Blechmann 	/* copy the struct snd_pcm_hardware struct */
21302bec490STim Blechmann 	runtime->hw = lx_caps;
21402bec490STim Blechmann 
21502bec490STim Blechmann #if 0
21602bec490STim Blechmann 	/* buffer-size should better be multiple of period-size */
21702bec490STim Blechmann 	err = snd_pcm_hw_constraint_integer(runtime,
21802bec490STim Blechmann 					    SNDRV_PCM_HW_PARAM_PERIODS);
21902bec490STim Blechmann 	if (err < 0) {
220be4e6d3cSTakashi Iwai 		dev_warn(chip->card->dev, "could not constrain periods\n");
22102bec490STim Blechmann 		goto exit;
22202bec490STim Blechmann 	}
22302bec490STim Blechmann #endif
22402bec490STim Blechmann 
22502bec490STim Blechmann 	/* the clock rate cannot be changed */
22602bec490STim Blechmann 	board_rate = chip->board_sample_rate;
227dfcdb028SLars-Peter Clausen 	err = snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_RATE,
228dfcdb028SLars-Peter Clausen 					   board_rate);
22902bec490STim Blechmann 
23002bec490STim Blechmann 	if (err < 0) {
231be4e6d3cSTakashi Iwai 		dev_warn(chip->card->dev, "could not constrain periods\n");
23202bec490STim Blechmann 		goto exit;
23302bec490STim Blechmann 	}
23402bec490STim Blechmann 
23502bec490STim Blechmann 	/* constrain period size */
23602bec490STim Blechmann 	err = snd_pcm_hw_constraint_minmax(runtime,
23702bec490STim Blechmann 					   SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
23802bec490STim Blechmann 					   MICROBLAZE_IBL_MIN,
23902bec490STim Blechmann 					   MICROBLAZE_IBL_MAX);
24002bec490STim Blechmann 	if (err < 0) {
241be4e6d3cSTakashi Iwai 		dev_warn(chip->card->dev,
24202bec490STim Blechmann 			   "could not constrain period size\n");
24302bec490STim Blechmann 		goto exit;
24402bec490STim Blechmann 	}
24502bec490STim Blechmann 
24602bec490STim Blechmann 	snd_pcm_hw_constraint_step(runtime, 0,
24702bec490STim Blechmann 				   SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 32);
24802bec490STim Blechmann 
24902bec490STim Blechmann 	snd_pcm_set_sync(substream);
25002bec490STim Blechmann 	err = 0;
25102bec490STim Blechmann 
25202bec490STim Blechmann exit:
25302bec490STim Blechmann 	runtime->private_data = chip;
25402bec490STim Blechmann 
25502bec490STim Blechmann 	mutex_unlock(&chip->setup_mutex);
256be4e6d3cSTakashi Iwai 	dev_dbg(chip->card->dev, "<-lx_pcm_open, %d\n", err);
25702bec490STim Blechmann 	return err;
25802bec490STim Blechmann }
25902bec490STim Blechmann 
lx_pcm_close(struct snd_pcm_substream * substream)26002bec490STim Blechmann static int lx_pcm_close(struct snd_pcm_substream *substream)
26102bec490STim Blechmann {
262be4e6d3cSTakashi Iwai 	dev_dbg(substream->pcm->card->dev, "->lx_pcm_close\n");
2637a3c2b69SHariprasad Kelam 	return 0;
26402bec490STim Blechmann }
26502bec490STim Blechmann 
lx_pcm_stream_pointer(struct snd_pcm_substream * substream)26602bec490STim Blechmann static snd_pcm_uframes_t lx_pcm_stream_pointer(struct snd_pcm_substream
26702bec490STim Blechmann 					       *substream)
26802bec490STim Blechmann {
26902bec490STim Blechmann 	struct lx6464es *chip = snd_pcm_substream_chip(substream);
27002bec490STim Blechmann 	snd_pcm_uframes_t pos;
27102bec490STim Blechmann 	int is_capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
27202bec490STim Blechmann 
27302bec490STim Blechmann 	struct lx_stream *lx_stream = is_capture ? &chip->capture_stream :
27402bec490STim Blechmann 		&chip->playback_stream;
27502bec490STim Blechmann 
276be4e6d3cSTakashi Iwai 	dev_dbg(chip->card->dev, "->lx_pcm_stream_pointer\n");
27702bec490STim Blechmann 
2786336c20cSTakashi Iwai 	mutex_lock(&chip->lock);
27902bec490STim Blechmann 	pos = lx_stream->frame_pos * substream->runtime->period_size;
2806336c20cSTakashi Iwai 	mutex_unlock(&chip->lock);
28102bec490STim Blechmann 
282be4e6d3cSTakashi Iwai 	dev_dbg(chip->card->dev, "stream_pointer at %ld\n", pos);
28302bec490STim Blechmann 	return pos;
28402bec490STim Blechmann }
28502bec490STim Blechmann 
lx_pcm_prepare(struct snd_pcm_substream * substream)28602bec490STim Blechmann static int lx_pcm_prepare(struct snd_pcm_substream *substream)
28702bec490STim Blechmann {
28802bec490STim Blechmann 	struct lx6464es *chip = snd_pcm_substream_chip(substream);
28902bec490STim Blechmann 	int err = 0;
29002bec490STim Blechmann 	const int is_capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
29102bec490STim Blechmann 
292be4e6d3cSTakashi Iwai 	dev_dbg(chip->card->dev, "->lx_pcm_prepare\n");
29302bec490STim Blechmann 
29402bec490STim Blechmann 	mutex_lock(&chip->setup_mutex);
29502bec490STim Blechmann 
29602bec490STim Blechmann 	if (chip->hardware_running[is_capture]) {
29702bec490STim Blechmann 		err = lx_hardware_stop(chip, substream);
29802bec490STim Blechmann 		if (err < 0) {
299be4e6d3cSTakashi Iwai 			dev_err(chip->card->dev, "failed to stop hardware. "
30002bec490STim Blechmann 				   "Error code %d\n", err);
30102bec490STim Blechmann 			goto exit;
30202bec490STim Blechmann 		}
30302bec490STim Blechmann 
30402bec490STim Blechmann 		err = lx_hardware_close(chip, substream);
30502bec490STim Blechmann 		if (err < 0) {
306be4e6d3cSTakashi Iwai 			dev_err(chip->card->dev, "failed to close hardware. "
30702bec490STim Blechmann 				   "Error code %d\n", err);
30802bec490STim Blechmann 			goto exit;
30902bec490STim Blechmann 		}
31002bec490STim Blechmann 	}
31102bec490STim Blechmann 
312be4e6d3cSTakashi Iwai 	dev_dbg(chip->card->dev, "opening hardware\n");
31302bec490STim Blechmann 	err = lx_hardware_open(chip, substream);
31402bec490STim Blechmann 	if (err < 0) {
315be4e6d3cSTakashi Iwai 		dev_err(chip->card->dev, "failed to open hardware. "
31602bec490STim Blechmann 			   "Error code %d\n", err);
31702bec490STim Blechmann 		goto exit;
31802bec490STim Blechmann 	}
31902bec490STim Blechmann 
32002bec490STim Blechmann 	err = lx_hardware_start(chip, substream);
32102bec490STim Blechmann 	if (err < 0) {
322be4e6d3cSTakashi Iwai 		dev_err(chip->card->dev, "failed to start hardware. "
32302bec490STim Blechmann 			   "Error code %d\n", err);
32402bec490STim Blechmann 		goto exit;
32502bec490STim Blechmann 	}
32602bec490STim Blechmann 
32702bec490STim Blechmann 	chip->hardware_running[is_capture] = 1;
32802bec490STim Blechmann 
32902bec490STim Blechmann 	if (chip->board_sample_rate != substream->runtime->rate) {
33002bec490STim Blechmann 		if (!err)
33102bec490STim Blechmann 			chip->board_sample_rate = substream->runtime->rate;
33202bec490STim Blechmann 	}
33302bec490STim Blechmann 
33402bec490STim Blechmann exit:
33502bec490STim Blechmann 	mutex_unlock(&chip->setup_mutex);
33602bec490STim Blechmann 	return err;
33702bec490STim Blechmann }
33802bec490STim Blechmann 
lx_pcm_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * hw_params,int is_capture)33902bec490STim Blechmann static int lx_pcm_hw_params(struct snd_pcm_substream *substream,
34002bec490STim Blechmann 			    struct snd_pcm_hw_params *hw_params, int is_capture)
34102bec490STim Blechmann {
34202bec490STim Blechmann 	struct lx6464es *chip = snd_pcm_substream_chip(substream);
34302bec490STim Blechmann 
344be4e6d3cSTakashi Iwai 	dev_dbg(chip->card->dev, "->lx_pcm_hw_params\n");
34502bec490STim Blechmann 
34602bec490STim Blechmann 	mutex_lock(&chip->setup_mutex);
34702bec490STim Blechmann 
34802bec490STim Blechmann 	if (is_capture)
34902bec490STim Blechmann 		chip->capture_stream.stream = substream;
35002bec490STim Blechmann 	else
35102bec490STim Blechmann 		chip->playback_stream.stream = substream;
35202bec490STim Blechmann 
35302bec490STim Blechmann 	mutex_unlock(&chip->setup_mutex);
354a747db23STakashi Iwai 	return 0;
35502bec490STim Blechmann }
35602bec490STim Blechmann 
lx_pcm_hw_params_playback(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * hw_params)35702bec490STim Blechmann static int lx_pcm_hw_params_playback(struct snd_pcm_substream *substream,
35802bec490STim Blechmann 				 struct snd_pcm_hw_params *hw_params)
35902bec490STim Blechmann {
36002bec490STim Blechmann 	return lx_pcm_hw_params(substream, hw_params, 0);
36102bec490STim Blechmann }
36202bec490STim Blechmann 
lx_pcm_hw_params_capture(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * hw_params)36302bec490STim Blechmann static int lx_pcm_hw_params_capture(struct snd_pcm_substream *substream,
36402bec490STim Blechmann 				 struct snd_pcm_hw_params *hw_params)
36502bec490STim Blechmann {
36602bec490STim Blechmann 	return lx_pcm_hw_params(substream, hw_params, 1);
36702bec490STim Blechmann }
36802bec490STim Blechmann 
lx_pcm_hw_free(struct snd_pcm_substream * substream)36902bec490STim Blechmann static int lx_pcm_hw_free(struct snd_pcm_substream *substream)
37002bec490STim Blechmann {
37102bec490STim Blechmann 	struct lx6464es *chip = snd_pcm_substream_chip(substream);
37202bec490STim Blechmann 	int err = 0;
37302bec490STim Blechmann 	int is_capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
37402bec490STim Blechmann 
375be4e6d3cSTakashi Iwai 	dev_dbg(chip->card->dev, "->lx_pcm_hw_free\n");
37602bec490STim Blechmann 	mutex_lock(&chip->setup_mutex);
37702bec490STim Blechmann 
37802bec490STim Blechmann 	if (chip->hardware_running[is_capture]) {
37902bec490STim Blechmann 		err = lx_hardware_stop(chip, substream);
38002bec490STim Blechmann 		if (err < 0) {
381be4e6d3cSTakashi Iwai 			dev_err(chip->card->dev, "failed to stop hardware. "
38202bec490STim Blechmann 				   "Error code %d\n", err);
38302bec490STim Blechmann 			goto exit;
38402bec490STim Blechmann 		}
38502bec490STim Blechmann 
38602bec490STim Blechmann 		err = lx_hardware_close(chip, substream);
38702bec490STim Blechmann 		if (err < 0) {
388be4e6d3cSTakashi Iwai 			dev_err(chip->card->dev, "failed to close hardware. "
38902bec490STim Blechmann 				   "Error code %d\n", err);
39002bec490STim Blechmann 			goto exit;
39102bec490STim Blechmann 		}
39202bec490STim Blechmann 
39302bec490STim Blechmann 		chip->hardware_running[is_capture] = 0;
39402bec490STim Blechmann 	}
39502bec490STim Blechmann 
39602bec490STim Blechmann 	if (is_capture)
397e42e748eSTakashi Iwai 		chip->capture_stream.stream = NULL;
39802bec490STim Blechmann 	else
399e42e748eSTakashi Iwai 		chip->playback_stream.stream = NULL;
40002bec490STim Blechmann 
40102bec490STim Blechmann exit:
40202bec490STim Blechmann 	mutex_unlock(&chip->setup_mutex);
40302bec490STim Blechmann 	return err;
40402bec490STim Blechmann }
40502bec490STim Blechmann 
lx_trigger_start(struct lx6464es * chip,struct lx_stream * lx_stream)40602bec490STim Blechmann static void lx_trigger_start(struct lx6464es *chip, struct lx_stream *lx_stream)
40702bec490STim Blechmann {
40802bec490STim Blechmann 	struct snd_pcm_substream *substream = lx_stream->stream;
409f7467452STim Blechmann 	const unsigned int is_capture = lx_stream->is_capture;
41002bec490STim Blechmann 
41102bec490STim Blechmann 	int err;
41202bec490STim Blechmann 
41302bec490STim Blechmann 	const u32 channels = substream->runtime->channels;
41402bec490STim Blechmann 	const u32 bytes_per_frame = channels * 3;
41502bec490STim Blechmann 	const u32 period_size = substream->runtime->period_size;
41602bec490STim Blechmann 	const u32 periods = substream->runtime->periods;
41702bec490STim Blechmann 	const u32 period_bytes = period_size * bytes_per_frame;
41802bec490STim Blechmann 
41902bec490STim Blechmann 	dma_addr_t buf = substream->dma_buffer.addr;
42002bec490STim Blechmann 	int i;
42102bec490STim Blechmann 
42202bec490STim Blechmann 	u32 needed, freed;
42302bec490STim Blechmann 	u32 size_array[5];
42402bec490STim Blechmann 
42502bec490STim Blechmann 	for (i = 0; i != periods; ++i) {
42602bec490STim Blechmann 		u32 buffer_index = 0;
42702bec490STim Blechmann 
42802bec490STim Blechmann 		err = lx_buffer_ask(chip, 0, is_capture, &needed, &freed,
42902bec490STim Blechmann 				    size_array);
430be4e6d3cSTakashi Iwai 		dev_dbg(chip->card->dev, "starting: needed %d, freed %d\n",
43102bec490STim Blechmann 			    needed, freed);
43202bec490STim Blechmann 
43302bec490STim Blechmann 		err = lx_buffer_give(chip, 0, is_capture, period_bytes,
43402bec490STim Blechmann 				     lower_32_bits(buf), upper_32_bits(buf),
43502bec490STim Blechmann 				     &buffer_index);
43602bec490STim Blechmann 
437be4e6d3cSTakashi Iwai 		dev_dbg(chip->card->dev, "starting: buffer index %x on 0x%lx (%d bytes)\n",
438293db842STakashi Iwai 			    buffer_index, (unsigned long)buf, period_bytes);
43902bec490STim Blechmann 		buf += period_bytes;
44002bec490STim Blechmann 	}
44102bec490STim Blechmann 
44202bec490STim Blechmann 	err = lx_buffer_ask(chip, 0, is_capture, &needed, &freed, size_array);
443be4e6d3cSTakashi Iwai 	dev_dbg(chip->card->dev, "starting: needed %d, freed %d\n", needed, freed);
44402bec490STim Blechmann 
445be4e6d3cSTakashi Iwai 	dev_dbg(chip->card->dev, "starting: starting stream\n");
44602bec490STim Blechmann 	err = lx_stream_start(chip, 0, is_capture);
44702bec490STim Blechmann 	if (err < 0)
448be4e6d3cSTakashi Iwai 		dev_err(chip->card->dev, "couldn't start stream\n");
44902bec490STim Blechmann 	else
45002bec490STim Blechmann 		lx_stream->status = LX_STREAM_STATUS_RUNNING;
45102bec490STim Blechmann 
45202bec490STim Blechmann 	lx_stream->frame_pos = 0;
45302bec490STim Blechmann }
45402bec490STim Blechmann 
lx_trigger_stop(struct lx6464es * chip,struct lx_stream * lx_stream)45502bec490STim Blechmann static void lx_trigger_stop(struct lx6464es *chip, struct lx_stream *lx_stream)
45602bec490STim Blechmann {
457f7467452STim Blechmann 	const unsigned int is_capture = lx_stream->is_capture;
45802bec490STim Blechmann 	int err;
45902bec490STim Blechmann 
460be4e6d3cSTakashi Iwai 	dev_dbg(chip->card->dev, "stopping: stopping stream\n");
46102bec490STim Blechmann 	err = lx_stream_stop(chip, 0, is_capture);
46202bec490STim Blechmann 	if (err < 0)
463be4e6d3cSTakashi Iwai 		dev_err(chip->card->dev, "couldn't stop stream\n");
46402bec490STim Blechmann 	else
46502bec490STim Blechmann 		lx_stream->status = LX_STREAM_STATUS_FREE;
46602bec490STim Blechmann 
46702bec490STim Blechmann }
46802bec490STim Blechmann 
lx_trigger_dispatch_stream(struct lx6464es * chip,struct lx_stream * lx_stream)4696336c20cSTakashi Iwai static void lx_trigger_dispatch_stream(struct lx6464es *chip,
47002bec490STim Blechmann 				       struct lx_stream *lx_stream)
47102bec490STim Blechmann {
47202bec490STim Blechmann 	switch (lx_stream->status) {
47302bec490STim Blechmann 	case LX_STREAM_STATUS_SCHEDULE_RUN:
47402bec490STim Blechmann 		lx_trigger_start(chip, lx_stream);
47502bec490STim Blechmann 		break;
47602bec490STim Blechmann 
47702bec490STim Blechmann 	case LX_STREAM_STATUS_SCHEDULE_STOP:
47802bec490STim Blechmann 		lx_trigger_stop(chip, lx_stream);
47902bec490STim Blechmann 		break;
48002bec490STim Blechmann 
48102bec490STim Blechmann 	default:
48202bec490STim Blechmann 		break;
48302bec490STim Blechmann 	}
48402bec490STim Blechmann }
48502bec490STim Blechmann 
lx_pcm_trigger_dispatch(struct lx6464es * chip,struct lx_stream * lx_stream,int cmd)48602bec490STim Blechmann static int lx_pcm_trigger_dispatch(struct lx6464es *chip,
48702bec490STim Blechmann 				   struct lx_stream *lx_stream, int cmd)
48802bec490STim Blechmann {
48902bec490STim Blechmann 	int err = 0;
49002bec490STim Blechmann 
4916336c20cSTakashi Iwai 	mutex_lock(&chip->lock);
49202bec490STim Blechmann 	switch (cmd) {
49302bec490STim Blechmann 	case SNDRV_PCM_TRIGGER_START:
49402bec490STim Blechmann 		lx_stream->status = LX_STREAM_STATUS_SCHEDULE_RUN;
49502bec490STim Blechmann 		break;
49602bec490STim Blechmann 
49702bec490STim Blechmann 	case SNDRV_PCM_TRIGGER_STOP:
49802bec490STim Blechmann 		lx_stream->status = LX_STREAM_STATUS_SCHEDULE_STOP;
49902bec490STim Blechmann 		break;
50002bec490STim Blechmann 
50102bec490STim Blechmann 	default:
50202bec490STim Blechmann 		err = -EINVAL;
50302bec490STim Blechmann 		goto exit;
50402bec490STim Blechmann 	}
5056336c20cSTakashi Iwai 
5066336c20cSTakashi Iwai 	lx_trigger_dispatch_stream(chip, &chip->capture_stream);
5076336c20cSTakashi Iwai 	lx_trigger_dispatch_stream(chip, &chip->playback_stream);
50802bec490STim Blechmann 
50902bec490STim Blechmann exit:
5106336c20cSTakashi Iwai 	mutex_unlock(&chip->lock);
51102bec490STim Blechmann 	return err;
51202bec490STim Blechmann }
51302bec490STim Blechmann 
51402bec490STim Blechmann 
lx_pcm_trigger(struct snd_pcm_substream * substream,int cmd)51502bec490STim Blechmann static int lx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
51602bec490STim Blechmann {
51702bec490STim Blechmann 	struct lx6464es *chip = snd_pcm_substream_chip(substream);
51802bec490STim Blechmann 	const int is_capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
51902bec490STim Blechmann 	struct lx_stream *stream = is_capture ? &chip->capture_stream :
52002bec490STim Blechmann 		&chip->playback_stream;
52102bec490STim Blechmann 
522be4e6d3cSTakashi Iwai 	dev_dbg(chip->card->dev, "->lx_pcm_trigger\n");
52302bec490STim Blechmann 
52402bec490STim Blechmann 	return lx_pcm_trigger_dispatch(chip, stream, cmd);
52502bec490STim Blechmann }
52602bec490STim Blechmann 
snd_lx6464es_free(struct snd_card * card)5276f16c19bSTakashi Iwai static void snd_lx6464es_free(struct snd_card *card)
52802bec490STim Blechmann {
5296f16c19bSTakashi Iwai 	struct lx6464es *chip = card->private_data;
53002bec490STim Blechmann 
53102bec490STim Blechmann 	lx_irq_disable(chip);
53202bec490STim Blechmann }
53302bec490STim Blechmann 
53402bec490STim Blechmann /* reset the dsp during initialization */
lx_init_xilinx_reset(struct lx6464es * chip)535e23e7a14SBill Pemberton static int lx_init_xilinx_reset(struct lx6464es *chip)
53602bec490STim Blechmann {
53702bec490STim Blechmann 	int i;
53802bec490STim Blechmann 	u32 plx_reg = lx_plx_reg_read(chip, ePLX_CHIPSC);
53902bec490STim Blechmann 
540be4e6d3cSTakashi Iwai 	dev_dbg(chip->card->dev, "->lx_init_xilinx_reset\n");
54102bec490STim Blechmann 
54202bec490STim Blechmann 	/* activate reset of xilinx */
54302bec490STim Blechmann 	plx_reg &= ~CHIPSC_RESET_XILINX;
54402bec490STim Blechmann 
54502bec490STim Blechmann 	lx_plx_reg_write(chip, ePLX_CHIPSC, plx_reg);
54602bec490STim Blechmann 	msleep(1);
54702bec490STim Blechmann 
54802bec490STim Blechmann 	lx_plx_reg_write(chip, ePLX_MBOX3, 0);
54902bec490STim Blechmann 	msleep(1);
55002bec490STim Blechmann 
55102bec490STim Blechmann 	plx_reg |= CHIPSC_RESET_XILINX;
55202bec490STim Blechmann 	lx_plx_reg_write(chip, ePLX_CHIPSC, plx_reg);
55302bec490STim Blechmann 
55402bec490STim Blechmann 	/* deactivate reset of xilinx */
55502bec490STim Blechmann 	for (i = 0; i != 100; ++i) {
55602bec490STim Blechmann 		u32 reg_mbox3;
55702bec490STim Blechmann 		msleep(10);
55802bec490STim Blechmann 		reg_mbox3 = lx_plx_reg_read(chip, ePLX_MBOX3);
55902bec490STim Blechmann 		if (reg_mbox3) {
560be4e6d3cSTakashi Iwai 			dev_dbg(chip->card->dev, "xilinx reset done\n");
561be4e6d3cSTakashi Iwai 			dev_dbg(chip->card->dev, "xilinx took %d loops\n", i);
56202bec490STim Blechmann 			break;
56302bec490STim Blechmann 		}
56402bec490STim Blechmann 	}
56502bec490STim Blechmann 
56602bec490STim Blechmann 	/* todo: add some error handling? */
56702bec490STim Blechmann 
56802bec490STim Blechmann 	/* clear mr */
56902bec490STim Blechmann 	lx_dsp_reg_write(chip, eReg_CSM, 0);
57002bec490STim Blechmann 
57102bec490STim Blechmann 	/* le xilinx ES peut ne pas etre encore pret, on attend. */
57202bec490STim Blechmann 	msleep(600);
57302bec490STim Blechmann 
57402bec490STim Blechmann 	return 0;
57502bec490STim Blechmann }
57602bec490STim Blechmann 
lx_init_xilinx_test(struct lx6464es * chip)577e23e7a14SBill Pemberton static int lx_init_xilinx_test(struct lx6464es *chip)
57802bec490STim Blechmann {
57902bec490STim Blechmann 	u32 reg;
58002bec490STim Blechmann 
581be4e6d3cSTakashi Iwai 	dev_dbg(chip->card->dev, "->lx_init_xilinx_test\n");
58202bec490STim Blechmann 
58302bec490STim Blechmann 	/* TEST if we have access to Xilinx/MicroBlaze */
58402bec490STim Blechmann 	lx_dsp_reg_write(chip, eReg_CSM, 0);
58502bec490STim Blechmann 
58602bec490STim Blechmann 	reg = lx_dsp_reg_read(chip, eReg_CSM);
58702bec490STim Blechmann 
58802bec490STim Blechmann 	if (reg) {
589be4e6d3cSTakashi Iwai 		dev_err(chip->card->dev, "Problem: Reg_CSM %x.\n", reg);
59002bec490STim Blechmann 
59102bec490STim Blechmann 		/* PCI9056_SPACE0_REMAP */
59202bec490STim Blechmann 		lx_plx_reg_write(chip, ePLX_PCICR, 1);
59302bec490STim Blechmann 
59402bec490STim Blechmann 		reg = lx_dsp_reg_read(chip, eReg_CSM);
59502bec490STim Blechmann 		if (reg) {
596be4e6d3cSTakashi Iwai 			dev_err(chip->card->dev, "Error: Reg_CSM %x.\n", reg);
59702bec490STim Blechmann 			return -EAGAIN; /* seems to be appropriate */
59802bec490STim Blechmann 		}
59902bec490STim Blechmann 	}
60002bec490STim Blechmann 
601be4e6d3cSTakashi Iwai 	dev_dbg(chip->card->dev, "Xilinx/MicroBlaze access test successful\n");
60202bec490STim Blechmann 
60302bec490STim Blechmann 	return 0;
60402bec490STim Blechmann }
60502bec490STim Blechmann 
60602bec490STim Blechmann /* initialize ethersound */
lx_init_ethersound_config(struct lx6464es * chip)607e23e7a14SBill Pemberton static int lx_init_ethersound_config(struct lx6464es *chip)
60802bec490STim Blechmann {
60902bec490STim Blechmann 	int i;
61002bec490STim Blechmann 	u32 orig_conf_es = lx_dsp_reg_read(chip, eReg_CONFES);
61102bec490STim Blechmann 
6127e895cfaSTim Blechmann 	/* configure 64 io channels */
6137e895cfaSTim Blechmann 	u32 conf_es = (orig_conf_es & CONFES_READ_PART_MASK) |
61402bec490STim Blechmann 		(64 << IOCR_INPUTS_OFFSET) |
6157e895cfaSTim Blechmann 		(64 << IOCR_OUTPUTS_OFFSET) |
61602bec490STim Blechmann 		(FREQ_RATIO_SINGLE_MODE << FREQ_RATIO_OFFSET);
61702bec490STim Blechmann 
618be4e6d3cSTakashi Iwai 	dev_dbg(chip->card->dev, "->lx_init_ethersound\n");
61902bec490STim Blechmann 
62002bec490STim Blechmann 	chip->freq_ratio = FREQ_RATIO_SINGLE_MODE;
62102bec490STim Blechmann 
62202bec490STim Blechmann 	/*
62302bec490STim Blechmann 	 * write it to the card !
62402bec490STim Blechmann 	 * this actually kicks the ES xilinx, the first time since poweron.
62502bec490STim Blechmann 	 * the MAC address in the Reg_ADMACESMSB Reg_ADMACESLSB registers
62602bec490STim Blechmann 	 * is not ready before this is done, and the bit 2 in Reg_CSES is set.
62702bec490STim Blechmann 	 * */
62802bec490STim Blechmann 	lx_dsp_reg_write(chip, eReg_CONFES, conf_es);
62902bec490STim Blechmann 
63002bec490STim Blechmann 	for (i = 0; i != 1000; ++i) {
63102bec490STim Blechmann 		if (lx_dsp_reg_read(chip, eReg_CSES) & 4) {
632be4e6d3cSTakashi Iwai 			dev_dbg(chip->card->dev, "ethersound initialized after %dms\n",
63302bec490STim Blechmann 				   i);
63402bec490STim Blechmann 			goto ethersound_initialized;
63502bec490STim Blechmann 		}
63602bec490STim Blechmann 		msleep(1);
63702bec490STim Blechmann 	}
638be4e6d3cSTakashi Iwai 	dev_warn(chip->card->dev,
63902bec490STim Blechmann 		   "ethersound could not be initialized after %dms\n", i);
64002bec490STim Blechmann 	return -ETIMEDOUT;
64102bec490STim Blechmann 
64202bec490STim Blechmann  ethersound_initialized:
643be4e6d3cSTakashi Iwai 	dev_dbg(chip->card->dev, "ethersound initialized\n");
64402bec490STim Blechmann 	return 0;
64502bec490STim Blechmann }
64602bec490STim Blechmann 
lx_init_get_version_features(struct lx6464es * chip)647e23e7a14SBill Pemberton static int lx_init_get_version_features(struct lx6464es *chip)
64802bec490STim Blechmann {
64902bec490STim Blechmann 	u32 dsp_version;
65002bec490STim Blechmann 
65102bec490STim Blechmann 	int err;
65202bec490STim Blechmann 
653be4e6d3cSTakashi Iwai 	dev_dbg(chip->card->dev, "->lx_init_get_version_features\n");
65402bec490STim Blechmann 
65502bec490STim Blechmann 	err = lx_dsp_get_version(chip, &dsp_version);
65602bec490STim Blechmann 
65702bec490STim Blechmann 	if (err == 0) {
65802bec490STim Blechmann 		u32 freq;
65902bec490STim Blechmann 
660be4e6d3cSTakashi Iwai 		dev_info(chip->card->dev, "DSP version: V%02d.%02d #%d\n",
66102bec490STim Blechmann 			   (dsp_version>>16) & 0xff, (dsp_version>>8) & 0xff,
66202bec490STim Blechmann 			   dsp_version & 0xff);
66302bec490STim Blechmann 
66402bec490STim Blechmann 		/* later: what firmware version do we expect? */
66502bec490STim Blechmann 
66602bec490STim Blechmann 		/* retrieve Play/Rec features */
66702bec490STim Blechmann 		/* done here because we may have to handle alternate
66802bec490STim Blechmann 		 * DSP files. */
66902bec490STim Blechmann 		/* later */
67002bec490STim Blechmann 
67102bec490STim Blechmann 		/* init the EtherSound sample rate */
67202bec490STim Blechmann 		err = lx_dsp_get_clock_frequency(chip, &freq);
67302bec490STim Blechmann 		if (err == 0)
67402bec490STim Blechmann 			chip->board_sample_rate = freq;
675be4e6d3cSTakashi Iwai 		dev_dbg(chip->card->dev, "actual clock frequency %d\n", freq);
67602bec490STim Blechmann 	} else {
677be4e6d3cSTakashi Iwai 		dev_err(chip->card->dev, "DSP corrupted \n");
67802bec490STim Blechmann 		err = -EAGAIN;
67902bec490STim Blechmann 	}
68002bec490STim Blechmann 
68102bec490STim Blechmann 	return err;
68202bec490STim Blechmann }
68302bec490STim Blechmann 
lx_set_granularity(struct lx6464es * chip,u32 gran)68402bec490STim Blechmann static int lx_set_granularity(struct lx6464es *chip, u32 gran)
68502bec490STim Blechmann {
68602bec490STim Blechmann 	int err = 0;
68702bec490STim Blechmann 	u32 snapped_gran = MICROBLAZE_IBL_MIN;
68802bec490STim Blechmann 
689be4e6d3cSTakashi Iwai 	dev_dbg(chip->card->dev, "->lx_set_granularity\n");
69002bec490STim Blechmann 
69102bec490STim Blechmann 	/* blocksize is a power of 2 */
69202bec490STim Blechmann 	while ((snapped_gran < gran) &&
69302bec490STim Blechmann 	       (snapped_gran < MICROBLAZE_IBL_MAX)) {
69402bec490STim Blechmann 		snapped_gran *= 2;
69502bec490STim Blechmann 	}
69602bec490STim Blechmann 
69702bec490STim Blechmann 	if (snapped_gran == chip->pcm_granularity)
69802bec490STim Blechmann 		return 0;
69902bec490STim Blechmann 
70002bec490STim Blechmann 	err = lx_dsp_set_granularity(chip, snapped_gran);
70102bec490STim Blechmann 	if (err < 0) {
702be4e6d3cSTakashi Iwai 		dev_warn(chip->card->dev, "could not set granularity\n");
70302bec490STim Blechmann 		err = -EAGAIN;
70402bec490STim Blechmann 	}
70502bec490STim Blechmann 
70602bec490STim Blechmann 	if (snapped_gran != gran)
707be4e6d3cSTakashi Iwai 		dev_err(chip->card->dev, "snapped blocksize to %d\n", snapped_gran);
70802bec490STim Blechmann 
709be4e6d3cSTakashi Iwai 	dev_dbg(chip->card->dev, "set blocksize on board %d\n", snapped_gran);
71002bec490STim Blechmann 	chip->pcm_granularity = snapped_gran;
71102bec490STim Blechmann 
71202bec490STim Blechmann 	return err;
71302bec490STim Blechmann }
71402bec490STim Blechmann 
71502bec490STim Blechmann /* initialize and test the xilinx dsp chip */
lx_init_dsp(struct lx6464es * chip)716e23e7a14SBill Pemberton static int lx_init_dsp(struct lx6464es *chip)
71702bec490STim Blechmann {
71802bec490STim Blechmann 	int err;
71902bec490STim Blechmann 	int i;
72002bec490STim Blechmann 
721be4e6d3cSTakashi Iwai 	dev_dbg(chip->card->dev, "->lx_init_dsp\n");
72202bec490STim Blechmann 
723be4e6d3cSTakashi Iwai 	dev_dbg(chip->card->dev, "initialize board\n");
72402bec490STim Blechmann 	err = lx_init_xilinx_reset(chip);
72502bec490STim Blechmann 	if (err)
72602bec490STim Blechmann 		return err;
72702bec490STim Blechmann 
728be4e6d3cSTakashi Iwai 	dev_dbg(chip->card->dev, "testing board\n");
72902bec490STim Blechmann 	err = lx_init_xilinx_test(chip);
73002bec490STim Blechmann 	if (err)
73102bec490STim Blechmann 		return err;
73202bec490STim Blechmann 
733be4e6d3cSTakashi Iwai 	dev_dbg(chip->card->dev, "initialize ethersound configuration\n");
73402bec490STim Blechmann 	err = lx_init_ethersound_config(chip);
73502bec490STim Blechmann 	if (err)
73602bec490STim Blechmann 		return err;
73702bec490STim Blechmann 
73802bec490STim Blechmann 	lx_irq_enable(chip);
73902bec490STim Blechmann 
74002bec490STim Blechmann 	/** \todo the mac address should be ready by not, but it isn't,
74102bec490STim Blechmann 	 *  so we wait for it */
74202bec490STim Blechmann 	for (i = 0; i != 1000; ++i) {
74380b52490STim Blechmann 		err = lx_dsp_get_mac(chip);
74402bec490STim Blechmann 		if (err)
74502bec490STim Blechmann 			return err;
74680b52490STim Blechmann 		if (chip->mac_address[0] || chip->mac_address[1] || chip->mac_address[2] ||
74780b52490STim Blechmann 		    chip->mac_address[3] || chip->mac_address[4] || chip->mac_address[5])
74802bec490STim Blechmann 			goto mac_ready;
74902bec490STim Blechmann 		msleep(1);
75002bec490STim Blechmann 	}
75102bec490STim Blechmann 	return -ETIMEDOUT;
75202bec490STim Blechmann 
75302bec490STim Blechmann mac_ready:
754be4e6d3cSTakashi Iwai 	dev_dbg(chip->card->dev, "mac address ready read after: %dms\n", i);
755be4e6d3cSTakashi Iwai 	dev_info(chip->card->dev,
756be4e6d3cSTakashi Iwai 		 "mac address: %02X.%02X.%02X.%02X.%02X.%02X\n",
75780b52490STim Blechmann 		   chip->mac_address[0], chip->mac_address[1], chip->mac_address[2],
75880b52490STim Blechmann 		   chip->mac_address[3], chip->mac_address[4], chip->mac_address[5]);
75902bec490STim Blechmann 
76002bec490STim Blechmann 	err = lx_init_get_version_features(chip);
76102bec490STim Blechmann 	if (err)
76202bec490STim Blechmann 		return err;
76302bec490STim Blechmann 
76402bec490STim Blechmann 	lx_set_granularity(chip, MICROBLAZE_IBL_DEFAULT);
76502bec490STim Blechmann 
76602bec490STim Blechmann 	chip->playback_mute = 0;
76702bec490STim Blechmann 
76802bec490STim Blechmann 	return err;
76902bec490STim Blechmann }
77002bec490STim Blechmann 
7716769e988SJulia Lawall static const struct snd_pcm_ops lx_ops_playback = {
77202bec490STim Blechmann 	.open      = lx_pcm_open,
77302bec490STim Blechmann 	.close     = lx_pcm_close,
77402bec490STim Blechmann 	.prepare   = lx_pcm_prepare,
77502bec490STim Blechmann 	.hw_params = lx_pcm_hw_params_playback,
77602bec490STim Blechmann 	.hw_free   = lx_pcm_hw_free,
77702bec490STim Blechmann 	.trigger   = lx_pcm_trigger,
77802bec490STim Blechmann 	.pointer   = lx_pcm_stream_pointer,
77902bec490STim Blechmann };
78002bec490STim Blechmann 
7816769e988SJulia Lawall static const struct snd_pcm_ops lx_ops_capture = {
78202bec490STim Blechmann 	.open      = lx_pcm_open,
78302bec490STim Blechmann 	.close     = lx_pcm_close,
78402bec490STim Blechmann 	.prepare   = lx_pcm_prepare,
78502bec490STim Blechmann 	.hw_params = lx_pcm_hw_params_capture,
78602bec490STim Blechmann 	.hw_free   = lx_pcm_hw_free,
78702bec490STim Blechmann 	.trigger   = lx_pcm_trigger,
78802bec490STim Blechmann 	.pointer   = lx_pcm_stream_pointer,
78902bec490STim Blechmann };
79002bec490STim Blechmann 
lx_pcm_create(struct lx6464es * chip)791e23e7a14SBill Pemberton static int lx_pcm_create(struct lx6464es *chip)
79202bec490STim Blechmann {
79302bec490STim Blechmann 	int err;
79402bec490STim Blechmann 	struct snd_pcm *pcm;
79502bec490STim Blechmann 
79602bec490STim Blechmann 	u32 size = 64 *		     /* channels */
79702bec490STim Blechmann 		3 *		     /* 24 bit samples */
79802bec490STim Blechmann 		MAX_STREAM_BUFFER *  /* periods */
79902bec490STim Blechmann 		MICROBLAZE_IBL_MAX * /* frames per period */
80002bec490STim Blechmann 		2;		     /* duplex */
80102bec490STim Blechmann 
80202bec490STim Blechmann 	size = PAGE_ALIGN(size);
80302bec490STim Blechmann 
80402bec490STim Blechmann 	/* hardcoded device name & channel count */
80502bec490STim Blechmann 	err = snd_pcm_new(chip->card, (char *)card_name, 0,
80602bec490STim Blechmann 			  1, 1, &pcm);
8073bdcff70STakashi Iwai 	if (err < 0)
8083bdcff70STakashi Iwai 		return err;
80902bec490STim Blechmann 
81002bec490STim Blechmann 	pcm->private_data = chip;
81102bec490STim Blechmann 
81202bec490STim Blechmann 	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &lx_ops_playback);
81302bec490STim Blechmann 	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &lx_ops_capture);
81402bec490STim Blechmann 
81502bec490STim Blechmann 	pcm->info_flags = 0;
8166336c20cSTakashi Iwai 	pcm->nonatomic = true;
81702bec490STim Blechmann 	strcpy(pcm->name, card_name);
81802bec490STim Blechmann 
819a747db23STakashi Iwai 	snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
820a747db23STakashi Iwai 				       &chip->pci->dev, size, size);
82102bec490STim Blechmann 
82202bec490STim Blechmann 	chip->pcm = pcm;
82302bec490STim Blechmann 	chip->capture_stream.is_capture = 1;
82402bec490STim Blechmann 
82502bec490STim Blechmann 	return 0;
82602bec490STim Blechmann }
82702bec490STim Blechmann 
lx_control_playback_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)82802bec490STim Blechmann static int lx_control_playback_info(struct snd_kcontrol *kcontrol,
82902bec490STim Blechmann 				    struct snd_ctl_elem_info *uinfo)
83002bec490STim Blechmann {
83102bec490STim Blechmann 	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
83202bec490STim Blechmann 	uinfo->count = 1;
83302bec490STim Blechmann 	uinfo->value.integer.min = 0;
83402bec490STim Blechmann 	uinfo->value.integer.max = 1;
83502bec490STim Blechmann 	return 0;
83602bec490STim Blechmann }
83702bec490STim Blechmann 
lx_control_playback_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)83802bec490STim Blechmann static int lx_control_playback_get(struct snd_kcontrol *kcontrol,
83902bec490STim Blechmann 				   struct snd_ctl_elem_value *ucontrol)
84002bec490STim Blechmann {
84102bec490STim Blechmann 	struct lx6464es *chip = snd_kcontrol_chip(kcontrol);
84202bec490STim Blechmann 	ucontrol->value.integer.value[0] = chip->playback_mute;
84302bec490STim Blechmann 	return 0;
84402bec490STim Blechmann }
84502bec490STim Blechmann 
lx_control_playback_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)84602bec490STim Blechmann static int lx_control_playback_put(struct snd_kcontrol *kcontrol,
84702bec490STim Blechmann 				   struct snd_ctl_elem_value *ucontrol)
84802bec490STim Blechmann {
84902bec490STim Blechmann 	struct lx6464es *chip = snd_kcontrol_chip(kcontrol);
85002bec490STim Blechmann 	int changed = 0;
85102bec490STim Blechmann 	int current_value = chip->playback_mute;
85202bec490STim Blechmann 
85302bec490STim Blechmann 	if (current_value != ucontrol->value.integer.value[0]) {
85402bec490STim Blechmann 		lx_level_unmute(chip, 0, !current_value);
85502bec490STim Blechmann 		chip->playback_mute = !current_value;
85602bec490STim Blechmann 		changed = 1;
85702bec490STim Blechmann 	}
85802bec490STim Blechmann 	return changed;
85902bec490STim Blechmann }
86002bec490STim Blechmann 
861f3b827e0SBhumika Goyal static const struct snd_kcontrol_new lx_control_playback_switch = {
86202bec490STim Blechmann 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
86302bec490STim Blechmann 	.name = "PCM Playback Switch",
86402bec490STim Blechmann 	.index = 0,
86502bec490STim Blechmann 	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
86602bec490STim Blechmann 	.private_value = 0,
86702bec490STim Blechmann 	.info = lx_control_playback_info,
86802bec490STim Blechmann 	.get = lx_control_playback_get,
86902bec490STim Blechmann 	.put = lx_control_playback_put
87002bec490STim Blechmann };
87102bec490STim Blechmann 
87202bec490STim Blechmann 
87302bec490STim Blechmann 
lx_proc_levels_read(struct snd_info_entry * entry,struct snd_info_buffer * buffer)87402bec490STim Blechmann static void lx_proc_levels_read(struct snd_info_entry *entry,
87502bec490STim Blechmann 				struct snd_info_buffer *buffer)
87602bec490STim Blechmann {
87702bec490STim Blechmann 	u32 levels[64];
87802bec490STim Blechmann 	int err;
87902bec490STim Blechmann 	int i, j;
88002bec490STim Blechmann 	struct lx6464es *chip = entry->private_data;
88102bec490STim Blechmann 
88202bec490STim Blechmann 	snd_iprintf(buffer, "capture levels:\n");
88302bec490STim Blechmann 	err = lx_level_peaks(chip, 1, 64, levels);
88402bec490STim Blechmann 	if (err < 0)
88502bec490STim Blechmann 		return;
88602bec490STim Blechmann 
88702bec490STim Blechmann 	for (i = 0; i != 8; ++i) {
88802bec490STim Blechmann 		for (j = 0; j != 8; ++j)
88902bec490STim Blechmann 			snd_iprintf(buffer, "%08x ", levels[i*8+j]);
89002bec490STim Blechmann 		snd_iprintf(buffer, "\n");
89102bec490STim Blechmann 	}
89202bec490STim Blechmann 
89302bec490STim Blechmann 	snd_iprintf(buffer, "\nplayback levels:\n");
89402bec490STim Blechmann 
89502bec490STim Blechmann 	err = lx_level_peaks(chip, 0, 64, levels);
89602bec490STim Blechmann 	if (err < 0)
89702bec490STim Blechmann 		return;
89802bec490STim Blechmann 
89902bec490STim Blechmann 	for (i = 0; i != 8; ++i) {
90002bec490STim Blechmann 		for (j = 0; j != 8; ++j)
90102bec490STim Blechmann 			snd_iprintf(buffer, "%08x ", levels[i*8+j]);
90202bec490STim Blechmann 		snd_iprintf(buffer, "\n");
90302bec490STim Blechmann 	}
90402bec490STim Blechmann 
90502bec490STim Blechmann 	snd_iprintf(buffer, "\n");
90602bec490STim Blechmann }
90702bec490STim Blechmann 
lx_proc_create(struct snd_card * card,struct lx6464es * chip)908e23e7a14SBill Pemberton static int lx_proc_create(struct snd_card *card, struct lx6464es *chip)
90902bec490STim Blechmann {
91047f2769bSTakashi Iwai 	return snd_card_ro_proc_new(card, "levels", chip, lx_proc_levels_read);
91102bec490STim Blechmann }
91202bec490STim Blechmann 
91302bec490STim Blechmann 
snd_lx6464es_create(struct snd_card * card,struct pci_dev * pci)914e23e7a14SBill Pemberton static int snd_lx6464es_create(struct snd_card *card,
9156f16c19bSTakashi Iwai 			       struct pci_dev *pci)
91602bec490STim Blechmann {
9176f16c19bSTakashi Iwai 	struct lx6464es *chip = card->private_data;
91802bec490STim Blechmann 	int err;
91902bec490STim Blechmann 
920be4e6d3cSTakashi Iwai 	dev_dbg(card->dev, "->snd_lx6464es_create\n");
92102bec490STim Blechmann 
92202bec490STim Blechmann 	/* enable PCI device */
9236f16c19bSTakashi Iwai 	err = pcim_enable_device(pci);
92402bec490STim Blechmann 	if (err < 0)
92502bec490STim Blechmann 		return err;
92602bec490STim Blechmann 
92702bec490STim Blechmann 	pci_set_master(pci);
92802bec490STim Blechmann 
92902bec490STim Blechmann 	/* check if we can restrict PCI DMA transfers to 32 bits */
930412b979cSQuentin Lambert 	err = dma_set_mask(&pci->dev, DMA_BIT_MASK(32));
93102bec490STim Blechmann 	if (err < 0) {
932be4e6d3cSTakashi Iwai 		dev_err(card->dev,
933be4e6d3cSTakashi Iwai 			"architecture does not support 32bit PCI busmaster DMA\n");
93402bec490STim Blechmann 		return -ENXIO;
93502bec490STim Blechmann 	}
93602bec490STim Blechmann 
93702bec490STim Blechmann 	chip->card = card;
93802bec490STim Blechmann 	chip->pci = pci;
93902bec490STim Blechmann 	chip->irq = -1;
94002bec490STim Blechmann 
94102bec490STim Blechmann 	/* initialize synchronization structs */
9426336c20cSTakashi Iwai 	mutex_init(&chip->lock);
9436336c20cSTakashi Iwai 	mutex_init(&chip->msg_lock);
94402bec490STim Blechmann 	mutex_init(&chip->setup_mutex);
94502bec490STim Blechmann 
94602bec490STim Blechmann 	/* request resources */
94702bec490STim Blechmann 	err = pci_request_regions(pci, card_name);
94802bec490STim Blechmann 	if (err < 0)
9496f16c19bSTakashi Iwai 		return err;
95002bec490STim Blechmann 
95102bec490STim Blechmann 	/* plx port */
95202bec490STim Blechmann 	chip->port_plx = pci_resource_start(pci, 1);
9536f16c19bSTakashi Iwai 	chip->port_plx_remapped = devm_ioport_map(&pci->dev, chip->port_plx,
95402bec490STim Blechmann 						  pci_resource_len(pci, 1));
9556f16c19bSTakashi Iwai 	if (!chip->port_plx_remapped)
9566f16c19bSTakashi Iwai 		return -ENOMEM;
95702bec490STim Blechmann 
95802bec490STim Blechmann 	/* dsp port */
9596f16c19bSTakashi Iwai 	chip->port_dsp_bar = pcim_iomap(pci, 2, 0);
9606f16c19bSTakashi Iwai 	if (!chip->port_dsp_bar)
9616f16c19bSTakashi Iwai 		return -ENOMEM;
96202bec490STim Blechmann 
9636f16c19bSTakashi Iwai 	err = devm_request_threaded_irq(&pci->dev, pci->irq, lx_interrupt,
9646f16c19bSTakashi Iwai 					lx_threaded_irq, IRQF_SHARED,
9656f16c19bSTakashi Iwai 					KBUILD_MODNAME, chip);
96602bec490STim Blechmann 	if (err) {
967be4e6d3cSTakashi Iwai 		dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq);
9686f16c19bSTakashi Iwai 		return err;
96902bec490STim Blechmann 	}
97002bec490STim Blechmann 	chip->irq = pci->irq;
97141094b24STakashi Iwai 	card->sync_irq = chip->irq;
9726f16c19bSTakashi Iwai 	card->private_free = snd_lx6464es_free;
97302bec490STim Blechmann 
97402bec490STim Blechmann 	err = lx_init_dsp(chip);
97502bec490STim Blechmann 	if (err < 0) {
976be4e6d3cSTakashi Iwai 		dev_err(card->dev, "error during DSP initialization\n");
97702bec490STim Blechmann 		return err;
97802bec490STim Blechmann 	}
97902bec490STim Blechmann 
98002bec490STim Blechmann 	err = lx_pcm_create(chip);
98102bec490STim Blechmann 	if (err < 0)
98202bec490STim Blechmann 		return err;
98302bec490STim Blechmann 
98402bec490STim Blechmann 	err = lx_proc_create(card, chip);
98502bec490STim Blechmann 	if (err < 0)
98602bec490STim Blechmann 		return err;
98702bec490STim Blechmann 
98802bec490STim Blechmann 	err = snd_ctl_add(card, snd_ctl_new1(&lx_control_playback_switch,
98902bec490STim Blechmann 					     chip));
99002bec490STim Blechmann 	if (err < 0)
99102bec490STim Blechmann 		return err;
99202bec490STim Blechmann 
99302bec490STim Blechmann 	return 0;
99402bec490STim Blechmann }
99502bec490STim Blechmann 
snd_lx6464es_probe(struct pci_dev * pci,const struct pci_device_id * pci_id)996e23e7a14SBill Pemberton static int snd_lx6464es_probe(struct pci_dev *pci,
99702bec490STim Blechmann 			      const struct pci_device_id *pci_id)
99802bec490STim Blechmann {
99902bec490STim Blechmann 	static int dev;
100002bec490STim Blechmann 	struct snd_card *card;
100102bec490STim Blechmann 	struct lx6464es *chip;
100202bec490STim Blechmann 	int err;
100302bec490STim Blechmann 
1004be4e6d3cSTakashi Iwai 	dev_dbg(&pci->dev, "->snd_lx6464es_probe\n");
100502bec490STim Blechmann 
100602bec490STim Blechmann 	if (dev >= SNDRV_CARDS)
100702bec490STim Blechmann 		return -ENODEV;
100802bec490STim Blechmann 	if (!enable[dev]) {
100902bec490STim Blechmann 		dev++;
101002bec490STim Blechmann 		return -ENOENT;
101102bec490STim Blechmann 	}
101202bec490STim Blechmann 
10136f16c19bSTakashi Iwai 	err = snd_devm_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
10146f16c19bSTakashi Iwai 				sizeof(*chip), &card);
10157852fd08STakashi Iwai 	if (err < 0)
10167852fd08STakashi Iwai 		return err;
10176f16c19bSTakashi Iwai 	chip = card->private_data;
101802bec490STim Blechmann 
10196f16c19bSTakashi Iwai 	err = snd_lx6464es_create(card, pci);
102002bec490STim Blechmann 	if (err < 0) {
1021be4e6d3cSTakashi Iwai 		dev_err(card->dev, "error during snd_lx6464es_create\n");
1022*5fbb73cbSTakashi Iwai 		goto error;
102302bec490STim Blechmann 	}
102402bec490STim Blechmann 
102580b52490STim Blechmann 	strcpy(card->driver, "LX6464ES");
102680b52490STim Blechmann 	sprintf(card->id, "LX6464ES_%02X%02X%02X",
102780b52490STim Blechmann 		chip->mac_address[3], chip->mac_address[4], chip->mac_address[5]);
102880b52490STim Blechmann 
102980b52490STim Blechmann 	sprintf(card->shortname, "LX6464ES %02X.%02X.%02X.%02X.%02X.%02X",
103080b52490STim Blechmann 		chip->mac_address[0], chip->mac_address[1], chip->mac_address[2],
103180b52490STim Blechmann 		chip->mac_address[3], chip->mac_address[4], chip->mac_address[5]);
103280b52490STim Blechmann 
103302bec490STim Blechmann 	sprintf(card->longname, "%s at 0x%lx, 0x%p, irq %i",
103402bec490STim Blechmann 		card->shortname, chip->port_plx,
103502bec490STim Blechmann 		chip->port_dsp_bar, chip->irq);
103602bec490STim Blechmann 
103702bec490STim Blechmann 	err = snd_card_register(card);
103802bec490STim Blechmann 	if (err < 0)
1039*5fbb73cbSTakashi Iwai 		goto error;
104002bec490STim Blechmann 
1041be4e6d3cSTakashi Iwai 	dev_dbg(chip->card->dev, "initialization successful\n");
104202bec490STim Blechmann 	pci_set_drvdata(pci, card);
104302bec490STim Blechmann 	dev++;
104402bec490STim Blechmann 	return 0;
1045*5fbb73cbSTakashi Iwai 
1046*5fbb73cbSTakashi Iwai  error:
1047*5fbb73cbSTakashi Iwai 	snd_card_free(card);
1048*5fbb73cbSTakashi Iwai 	return err;
104902bec490STim Blechmann }
105002bec490STim Blechmann 
1051e9f66d9bSTakashi Iwai static struct pci_driver lx6464es_driver = {
10523733e424STakashi Iwai 	.name =     KBUILD_MODNAME,
105302bec490STim Blechmann 	.id_table = snd_lx6464es_ids,
105402bec490STim Blechmann 	.probe =    snd_lx6464es_probe,
105502bec490STim Blechmann };
105602bec490STim Blechmann 
1057e9f66d9bSTakashi Iwai module_pci_driver(lx6464es_driver);
1058