162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Driver for AT73C213 16-bit stereo DAC connected to Atmel SSC
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2006-2007 Atmel Norway
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci/*#define DEBUG*/
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/clk.h>
1162306a36Sopenharmony_ci#include <linux/err.h>
1262306a36Sopenharmony_ci#include <linux/delay.h>
1362306a36Sopenharmony_ci#include <linux/device.h>
1462306a36Sopenharmony_ci#include <linux/dma-mapping.h>
1562306a36Sopenharmony_ci#include <linux/init.h>
1662306a36Sopenharmony_ci#include <linux/interrupt.h>
1762306a36Sopenharmony_ci#include <linux/module.h>
1862306a36Sopenharmony_ci#include <linux/mutex.h>
1962306a36Sopenharmony_ci#include <linux/platform_device.h>
2062306a36Sopenharmony_ci#include <linux/io.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#include <sound/initval.h>
2362306a36Sopenharmony_ci#include <sound/control.h>
2462306a36Sopenharmony_ci#include <sound/core.h>
2562306a36Sopenharmony_ci#include <sound/pcm.h>
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#include <linux/atmel-ssc.h>
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#include <linux/spi/spi.h>
3062306a36Sopenharmony_ci#include <linux/spi/at73c213.h>
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci#include "at73c213.h"
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci#define BITRATE_MIN	 8000 /* Hardware limit? */
3562306a36Sopenharmony_ci#define BITRATE_TARGET	CONFIG_SND_AT73C213_TARGET_BITRATE
3662306a36Sopenharmony_ci#define BITRATE_MAX	50000 /* Hardware limit. */
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci/* Initial (hardware reset) AT73C213 register values. */
3962306a36Sopenharmony_cistatic const u8 snd_at73c213_original_image[18] =
4062306a36Sopenharmony_ci{
4162306a36Sopenharmony_ci	0x00,	/* 00 - CTRL    */
4262306a36Sopenharmony_ci	0x05,	/* 01 - LLIG    */
4362306a36Sopenharmony_ci	0x05,	/* 02 - RLIG    */
4462306a36Sopenharmony_ci	0x08,	/* 03 - LPMG    */
4562306a36Sopenharmony_ci	0x08,	/* 04 - RPMG    */
4662306a36Sopenharmony_ci	0x00,	/* 05 - LLOG    */
4762306a36Sopenharmony_ci	0x00,	/* 06 - RLOG    */
4862306a36Sopenharmony_ci	0x22,	/* 07 - OLC     */
4962306a36Sopenharmony_ci	0x09,	/* 08 - MC      */
5062306a36Sopenharmony_ci	0x00,	/* 09 - CSFC    */
5162306a36Sopenharmony_ci	0x00,	/* 0A - MISC    */
5262306a36Sopenharmony_ci	0x00,	/* 0B -         */
5362306a36Sopenharmony_ci	0x00,	/* 0C - PRECH   */
5462306a36Sopenharmony_ci	0x05,	/* 0D - AUXG    */
5562306a36Sopenharmony_ci	0x00,	/* 0E -         */
5662306a36Sopenharmony_ci	0x00,	/* 0F -         */
5762306a36Sopenharmony_ci	0x00,	/* 10 - RST     */
5862306a36Sopenharmony_ci	0x00,	/* 11 - PA_CTRL */
5962306a36Sopenharmony_ci};
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_cistruct snd_at73c213 {
6262306a36Sopenharmony_ci	struct snd_card			*card;
6362306a36Sopenharmony_ci	struct snd_pcm			*pcm;
6462306a36Sopenharmony_ci	struct snd_pcm_substream	*substream;
6562306a36Sopenharmony_ci	struct at73c213_board_info	*board;
6662306a36Sopenharmony_ci	int				irq;
6762306a36Sopenharmony_ci	int				period;
6862306a36Sopenharmony_ci	unsigned long			bitrate;
6962306a36Sopenharmony_ci	struct ssc_device		*ssc;
7062306a36Sopenharmony_ci	struct spi_device		*spi;
7162306a36Sopenharmony_ci	u8				spi_wbuffer[2];
7262306a36Sopenharmony_ci	u8				spi_rbuffer[2];
7362306a36Sopenharmony_ci	/* Image of the SPI registers in AT73C213. */
7462306a36Sopenharmony_ci	u8				reg_image[18];
7562306a36Sopenharmony_ci	/* Protect SSC registers against concurrent access. */
7662306a36Sopenharmony_ci	spinlock_t			lock;
7762306a36Sopenharmony_ci	/* Protect mixer registers against concurrent access. */
7862306a36Sopenharmony_ci	struct mutex			mixer_lock;
7962306a36Sopenharmony_ci};
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci#define get_chip(card) ((struct snd_at73c213 *)card->private_data)
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cistatic int
8462306a36Sopenharmony_cisnd_at73c213_write_reg(struct snd_at73c213 *chip, u8 reg, u8 val)
8562306a36Sopenharmony_ci{
8662306a36Sopenharmony_ci	struct spi_message msg;
8762306a36Sopenharmony_ci	struct spi_transfer msg_xfer = {
8862306a36Sopenharmony_ci		.len		= 2,
8962306a36Sopenharmony_ci		.cs_change	= 0,
9062306a36Sopenharmony_ci	};
9162306a36Sopenharmony_ci	int retval;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	spi_message_init(&msg);
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	chip->spi_wbuffer[0] = reg;
9662306a36Sopenharmony_ci	chip->spi_wbuffer[1] = val;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	msg_xfer.tx_buf = chip->spi_wbuffer;
9962306a36Sopenharmony_ci	msg_xfer.rx_buf = chip->spi_rbuffer;
10062306a36Sopenharmony_ci	spi_message_add_tail(&msg_xfer, &msg);
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	retval = spi_sync(chip->spi, &msg);
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	if (!retval)
10562306a36Sopenharmony_ci		chip->reg_image[reg] = val;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	return retval;
10862306a36Sopenharmony_ci}
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_cistatic struct snd_pcm_hardware snd_at73c213_playback_hw = {
11162306a36Sopenharmony_ci	.info		= SNDRV_PCM_INFO_INTERLEAVED |
11262306a36Sopenharmony_ci			  SNDRV_PCM_INFO_BLOCK_TRANSFER,
11362306a36Sopenharmony_ci	.formats	= SNDRV_PCM_FMTBIT_S16_BE,
11462306a36Sopenharmony_ci	.rates		= SNDRV_PCM_RATE_CONTINUOUS,
11562306a36Sopenharmony_ci	.rate_min	= 8000,  /* Replaced by chip->bitrate later. */
11662306a36Sopenharmony_ci	.rate_max	= 50000, /* Replaced by chip->bitrate later. */
11762306a36Sopenharmony_ci	.channels_min	= 1,
11862306a36Sopenharmony_ci	.channels_max	= 2,
11962306a36Sopenharmony_ci	.buffer_bytes_max = 64 * 1024 - 1,
12062306a36Sopenharmony_ci	.period_bytes_min = 512,
12162306a36Sopenharmony_ci	.period_bytes_max = 64 * 1024 - 1,
12262306a36Sopenharmony_ci	.periods_min	= 4,
12362306a36Sopenharmony_ci	.periods_max	= 1024,
12462306a36Sopenharmony_ci};
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci/*
12762306a36Sopenharmony_ci * Calculate and set bitrate and divisions.
12862306a36Sopenharmony_ci */
12962306a36Sopenharmony_cistatic int snd_at73c213_set_bitrate(struct snd_at73c213 *chip)
13062306a36Sopenharmony_ci{
13162306a36Sopenharmony_ci	unsigned long ssc_rate = clk_get_rate(chip->ssc->clk);
13262306a36Sopenharmony_ci	unsigned long dac_rate_new, ssc_div;
13362306a36Sopenharmony_ci	int status;
13462306a36Sopenharmony_ci	unsigned long ssc_div_max, ssc_div_min;
13562306a36Sopenharmony_ci	int max_tries;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	/*
13862306a36Sopenharmony_ci	 * We connect two clocks here, picking divisors so the I2S clocks
13962306a36Sopenharmony_ci	 * out data at the same rate the DAC clocks it in ... and as close
14062306a36Sopenharmony_ci	 * as practical to the desired target rate.
14162306a36Sopenharmony_ci	 *
14262306a36Sopenharmony_ci	 * The DAC master clock (MCLK) is programmable, and is either 256
14362306a36Sopenharmony_ci	 * or (not here) 384 times the I2S output clock (BCLK).
14462306a36Sopenharmony_ci	 */
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	/* SSC clock / (bitrate * stereo * 16-bit). */
14762306a36Sopenharmony_ci	ssc_div = ssc_rate / (BITRATE_TARGET * 2 * 16);
14862306a36Sopenharmony_ci	ssc_div_min = ssc_rate / (BITRATE_MAX * 2 * 16);
14962306a36Sopenharmony_ci	ssc_div_max = ssc_rate / (BITRATE_MIN * 2 * 16);
15062306a36Sopenharmony_ci	max_tries = (ssc_div_max - ssc_div_min) / 2;
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	if (max_tries < 1)
15362306a36Sopenharmony_ci		max_tries = 1;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	/* ssc_div must be even. */
15662306a36Sopenharmony_ci	ssc_div = (ssc_div + 1) & ~1UL;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	if ((ssc_rate / (ssc_div * 2 * 16)) < BITRATE_MIN) {
15962306a36Sopenharmony_ci		ssc_div -= 2;
16062306a36Sopenharmony_ci		if ((ssc_rate / (ssc_div * 2 * 16)) > BITRATE_MAX)
16162306a36Sopenharmony_ci			return -ENXIO;
16262306a36Sopenharmony_ci	}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	/* Search for a possible bitrate. */
16562306a36Sopenharmony_ci	do {
16662306a36Sopenharmony_ci		/* SSC clock / (ssc divider * 16-bit * stereo). */
16762306a36Sopenharmony_ci		if ((ssc_rate / (ssc_div * 2 * 16)) < BITRATE_MIN)
16862306a36Sopenharmony_ci			return -ENXIO;
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci		/* 256 / (2 * 16) = 8 */
17162306a36Sopenharmony_ci		dac_rate_new = 8 * (ssc_rate / ssc_div);
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci		status = clk_round_rate(chip->board->dac_clk, dac_rate_new);
17462306a36Sopenharmony_ci		if (status <= 0)
17562306a36Sopenharmony_ci			return status;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci		/* Ignore difference smaller than 256 Hz. */
17862306a36Sopenharmony_ci		if ((status/256) == (dac_rate_new/256))
17962306a36Sopenharmony_ci			goto set_rate;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci		ssc_div += 2;
18262306a36Sopenharmony_ci	} while (--max_tries);
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	/* Not able to find a valid bitrate. */
18562306a36Sopenharmony_ci	return -ENXIO;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ciset_rate:
18862306a36Sopenharmony_ci	status = clk_set_rate(chip->board->dac_clk, status);
18962306a36Sopenharmony_ci	if (status < 0)
19062306a36Sopenharmony_ci		return status;
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	/* Set divider in SSC device. */
19362306a36Sopenharmony_ci	ssc_writel(chip->ssc->regs, CMR, ssc_div/2);
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	/* SSC clock / (ssc divider * 16-bit * stereo). */
19662306a36Sopenharmony_ci	chip->bitrate = ssc_rate / (ssc_div * 16 * 2);
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	dev_info(&chip->spi->dev,
19962306a36Sopenharmony_ci			"at73c213: supported bitrate is %lu (%lu divider)\n",
20062306a36Sopenharmony_ci			chip->bitrate, ssc_div);
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	return 0;
20362306a36Sopenharmony_ci}
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_cistatic int snd_at73c213_pcm_open(struct snd_pcm_substream *substream)
20662306a36Sopenharmony_ci{
20762306a36Sopenharmony_ci	struct snd_at73c213 *chip = snd_pcm_substream_chip(substream);
20862306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
20962306a36Sopenharmony_ci	int err;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	/* ensure buffer_size is a multiple of period_size */
21262306a36Sopenharmony_ci	err = snd_pcm_hw_constraint_integer(runtime,
21362306a36Sopenharmony_ci					SNDRV_PCM_HW_PARAM_PERIODS);
21462306a36Sopenharmony_ci	if (err < 0)
21562306a36Sopenharmony_ci		return err;
21662306a36Sopenharmony_ci	snd_at73c213_playback_hw.rate_min = chip->bitrate;
21762306a36Sopenharmony_ci	snd_at73c213_playback_hw.rate_max = chip->bitrate;
21862306a36Sopenharmony_ci	runtime->hw = snd_at73c213_playback_hw;
21962306a36Sopenharmony_ci	chip->substream = substream;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	err = clk_enable(chip->ssc->clk);
22262306a36Sopenharmony_ci	if (err)
22362306a36Sopenharmony_ci		return err;
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	return 0;
22662306a36Sopenharmony_ci}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_cistatic int snd_at73c213_pcm_close(struct snd_pcm_substream *substream)
22962306a36Sopenharmony_ci{
23062306a36Sopenharmony_ci	struct snd_at73c213 *chip = snd_pcm_substream_chip(substream);
23162306a36Sopenharmony_ci	chip->substream = NULL;
23262306a36Sopenharmony_ci	clk_disable(chip->ssc->clk);
23362306a36Sopenharmony_ci	return 0;
23462306a36Sopenharmony_ci}
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_cistatic int snd_at73c213_pcm_hw_params(struct snd_pcm_substream *substream,
23762306a36Sopenharmony_ci				 struct snd_pcm_hw_params *hw_params)
23862306a36Sopenharmony_ci{
23962306a36Sopenharmony_ci	struct snd_at73c213 *chip = snd_pcm_substream_chip(substream);
24062306a36Sopenharmony_ci	int channels = params_channels(hw_params);
24162306a36Sopenharmony_ci	int val;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	val = ssc_readl(chip->ssc->regs, TFMR);
24462306a36Sopenharmony_ci	val = SSC_BFINS(TFMR_DATNB, channels - 1, val);
24562306a36Sopenharmony_ci	ssc_writel(chip->ssc->regs, TFMR, val);
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	return 0;
24862306a36Sopenharmony_ci}
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_cistatic int snd_at73c213_pcm_prepare(struct snd_pcm_substream *substream)
25162306a36Sopenharmony_ci{
25262306a36Sopenharmony_ci	struct snd_at73c213 *chip = snd_pcm_substream_chip(substream);
25362306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
25462306a36Sopenharmony_ci	int block_size;
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	block_size = frames_to_bytes(runtime, runtime->period_size);
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	chip->period = 0;
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	ssc_writel(chip->ssc->regs, PDC_TPR,
26162306a36Sopenharmony_ci			(long)runtime->dma_addr);
26262306a36Sopenharmony_ci	ssc_writel(chip->ssc->regs, PDC_TCR,
26362306a36Sopenharmony_ci			runtime->period_size * runtime->channels);
26462306a36Sopenharmony_ci	ssc_writel(chip->ssc->regs, PDC_TNPR,
26562306a36Sopenharmony_ci			(long)runtime->dma_addr + block_size);
26662306a36Sopenharmony_ci	ssc_writel(chip->ssc->regs, PDC_TNCR,
26762306a36Sopenharmony_ci			runtime->period_size * runtime->channels);
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	return 0;
27062306a36Sopenharmony_ci}
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_cistatic int snd_at73c213_pcm_trigger(struct snd_pcm_substream *substream,
27362306a36Sopenharmony_ci				   int cmd)
27462306a36Sopenharmony_ci{
27562306a36Sopenharmony_ci	struct snd_at73c213 *chip = snd_pcm_substream_chip(substream);
27662306a36Sopenharmony_ci	int retval = 0;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	spin_lock(&chip->lock);
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	switch (cmd) {
28162306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
28262306a36Sopenharmony_ci		ssc_writel(chip->ssc->regs, IER, SSC_BIT(IER_ENDTX));
28362306a36Sopenharmony_ci		ssc_writel(chip->ssc->regs, PDC_PTCR, SSC_BIT(PDC_PTCR_TXTEN));
28462306a36Sopenharmony_ci		break;
28562306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
28662306a36Sopenharmony_ci		ssc_writel(chip->ssc->regs, PDC_PTCR, SSC_BIT(PDC_PTCR_TXTDIS));
28762306a36Sopenharmony_ci		ssc_writel(chip->ssc->regs, IDR, SSC_BIT(IDR_ENDTX));
28862306a36Sopenharmony_ci		break;
28962306a36Sopenharmony_ci	default:
29062306a36Sopenharmony_ci		dev_dbg(&chip->spi->dev, "spurious command %x\n", cmd);
29162306a36Sopenharmony_ci		retval = -EINVAL;
29262306a36Sopenharmony_ci		break;
29362306a36Sopenharmony_ci	}
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	spin_unlock(&chip->lock);
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	return retval;
29862306a36Sopenharmony_ci}
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_cistatic snd_pcm_uframes_t
30162306a36Sopenharmony_cisnd_at73c213_pcm_pointer(struct snd_pcm_substream *substream)
30262306a36Sopenharmony_ci{
30362306a36Sopenharmony_ci	struct snd_at73c213 *chip = snd_pcm_substream_chip(substream);
30462306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
30562306a36Sopenharmony_ci	snd_pcm_uframes_t pos;
30662306a36Sopenharmony_ci	unsigned long bytes;
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	bytes = ssc_readl(chip->ssc->regs, PDC_TPR)
30962306a36Sopenharmony_ci		- (unsigned long)runtime->dma_addr;
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	pos = bytes_to_frames(runtime, bytes);
31262306a36Sopenharmony_ci	if (pos >= runtime->buffer_size)
31362306a36Sopenharmony_ci		pos -= runtime->buffer_size;
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	return pos;
31662306a36Sopenharmony_ci}
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_cistatic const struct snd_pcm_ops at73c213_playback_ops = {
31962306a36Sopenharmony_ci	.open		= snd_at73c213_pcm_open,
32062306a36Sopenharmony_ci	.close		= snd_at73c213_pcm_close,
32162306a36Sopenharmony_ci	.hw_params	= snd_at73c213_pcm_hw_params,
32262306a36Sopenharmony_ci	.prepare	= snd_at73c213_pcm_prepare,
32362306a36Sopenharmony_ci	.trigger	= snd_at73c213_pcm_trigger,
32462306a36Sopenharmony_ci	.pointer	= snd_at73c213_pcm_pointer,
32562306a36Sopenharmony_ci};
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_cistatic int snd_at73c213_pcm_new(struct snd_at73c213 *chip, int device)
32862306a36Sopenharmony_ci{
32962306a36Sopenharmony_ci	struct snd_pcm *pcm;
33062306a36Sopenharmony_ci	int retval;
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	retval = snd_pcm_new(chip->card, chip->card->shortname,
33362306a36Sopenharmony_ci			device, 1, 0, &pcm);
33462306a36Sopenharmony_ci	if (retval < 0)
33562306a36Sopenharmony_ci		goto out;
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	pcm->private_data = chip;
33862306a36Sopenharmony_ci	pcm->info_flags = SNDRV_PCM_INFO_BLOCK_TRANSFER;
33962306a36Sopenharmony_ci	strcpy(pcm->name, "at73c213");
34062306a36Sopenharmony_ci	chip->pcm = pcm;
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &at73c213_playback_ops);
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	snd_pcm_set_managed_buffer_all(chip->pcm,
34562306a36Sopenharmony_ci			SNDRV_DMA_TYPE_DEV, &chip->ssc->pdev->dev,
34662306a36Sopenharmony_ci			64 * 1024, 64 * 1024);
34762306a36Sopenharmony_ciout:
34862306a36Sopenharmony_ci	return retval;
34962306a36Sopenharmony_ci}
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_cistatic irqreturn_t snd_at73c213_interrupt(int irq, void *dev_id)
35262306a36Sopenharmony_ci{
35362306a36Sopenharmony_ci	struct snd_at73c213 *chip = dev_id;
35462306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = chip->substream->runtime;
35562306a36Sopenharmony_ci	u32 status;
35662306a36Sopenharmony_ci	int offset;
35762306a36Sopenharmony_ci	int block_size;
35862306a36Sopenharmony_ci	int next_period;
35962306a36Sopenharmony_ci	int retval = IRQ_NONE;
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	spin_lock(&chip->lock);
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	block_size = frames_to_bytes(runtime, runtime->period_size);
36462306a36Sopenharmony_ci	status = ssc_readl(chip->ssc->regs, IMR);
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	if (status & SSC_BIT(IMR_ENDTX)) {
36762306a36Sopenharmony_ci		chip->period++;
36862306a36Sopenharmony_ci		if (chip->period == runtime->periods)
36962306a36Sopenharmony_ci			chip->period = 0;
37062306a36Sopenharmony_ci		next_period = chip->period + 1;
37162306a36Sopenharmony_ci		if (next_period == runtime->periods)
37262306a36Sopenharmony_ci			next_period = 0;
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci		offset = block_size * next_period;
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci		ssc_writel(chip->ssc->regs, PDC_TNPR,
37762306a36Sopenharmony_ci				(long)runtime->dma_addr + offset);
37862306a36Sopenharmony_ci		ssc_writel(chip->ssc->regs, PDC_TNCR,
37962306a36Sopenharmony_ci				runtime->period_size * runtime->channels);
38062306a36Sopenharmony_ci		retval = IRQ_HANDLED;
38162306a36Sopenharmony_ci	}
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	ssc_readl(chip->ssc->regs, IMR);
38462306a36Sopenharmony_ci	spin_unlock(&chip->lock);
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	if (status & SSC_BIT(IMR_ENDTX))
38762306a36Sopenharmony_ci		snd_pcm_period_elapsed(chip->substream);
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	return retval;
39062306a36Sopenharmony_ci}
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci/*
39362306a36Sopenharmony_ci * Mixer functions.
39462306a36Sopenharmony_ci */
39562306a36Sopenharmony_cistatic int snd_at73c213_mono_get(struct snd_kcontrol *kcontrol,
39662306a36Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol)
39762306a36Sopenharmony_ci{
39862306a36Sopenharmony_ci	struct snd_at73c213 *chip = snd_kcontrol_chip(kcontrol);
39962306a36Sopenharmony_ci	int reg = kcontrol->private_value & 0xff;
40062306a36Sopenharmony_ci	int shift = (kcontrol->private_value >> 8) & 0xff;
40162306a36Sopenharmony_ci	int mask = (kcontrol->private_value >> 16) & 0xff;
40262306a36Sopenharmony_ci	int invert = (kcontrol->private_value >> 24) & 0xff;
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	mutex_lock(&chip->mixer_lock);
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	ucontrol->value.integer.value[0] =
40762306a36Sopenharmony_ci		(chip->reg_image[reg] >> shift) & mask;
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	if (invert)
41062306a36Sopenharmony_ci		ucontrol->value.integer.value[0] =
41162306a36Sopenharmony_ci			mask - ucontrol->value.integer.value[0];
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	mutex_unlock(&chip->mixer_lock);
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	return 0;
41662306a36Sopenharmony_ci}
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_cistatic int snd_at73c213_mono_put(struct snd_kcontrol *kcontrol,
41962306a36Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol)
42062306a36Sopenharmony_ci{
42162306a36Sopenharmony_ci	struct snd_at73c213 *chip = snd_kcontrol_chip(kcontrol);
42262306a36Sopenharmony_ci	int reg = kcontrol->private_value & 0xff;
42362306a36Sopenharmony_ci	int shift = (kcontrol->private_value >> 8) & 0xff;
42462306a36Sopenharmony_ci	int mask = (kcontrol->private_value >> 16) & 0xff;
42562306a36Sopenharmony_ci	int invert = (kcontrol->private_value >> 24) & 0xff;
42662306a36Sopenharmony_ci	int change, retval;
42762306a36Sopenharmony_ci	unsigned short val;
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	val = (ucontrol->value.integer.value[0] & mask);
43062306a36Sopenharmony_ci	if (invert)
43162306a36Sopenharmony_ci		val = mask - val;
43262306a36Sopenharmony_ci	val <<= shift;
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	mutex_lock(&chip->mixer_lock);
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	val = (chip->reg_image[reg] & ~(mask << shift)) | val;
43762306a36Sopenharmony_ci	change = val != chip->reg_image[reg];
43862306a36Sopenharmony_ci	retval = snd_at73c213_write_reg(chip, reg, val);
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	mutex_unlock(&chip->mixer_lock);
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	if (retval)
44362306a36Sopenharmony_ci		return retval;
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	return change;
44662306a36Sopenharmony_ci}
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_cistatic int snd_at73c213_stereo_info(struct snd_kcontrol *kcontrol,
44962306a36Sopenharmony_ci				  struct snd_ctl_elem_info *uinfo)
45062306a36Sopenharmony_ci{
45162306a36Sopenharmony_ci	int mask = (kcontrol->private_value >> 24) & 0xff;
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	if (mask == 1)
45462306a36Sopenharmony_ci		uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
45562306a36Sopenharmony_ci	else
45662306a36Sopenharmony_ci		uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	uinfo->count = 2;
45962306a36Sopenharmony_ci	uinfo->value.integer.min = 0;
46062306a36Sopenharmony_ci	uinfo->value.integer.max = mask;
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	return 0;
46362306a36Sopenharmony_ci}
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_cistatic int snd_at73c213_stereo_get(struct snd_kcontrol *kcontrol,
46662306a36Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol)
46762306a36Sopenharmony_ci{
46862306a36Sopenharmony_ci	struct snd_at73c213 *chip = snd_kcontrol_chip(kcontrol);
46962306a36Sopenharmony_ci	int left_reg = kcontrol->private_value & 0xff;
47062306a36Sopenharmony_ci	int right_reg = (kcontrol->private_value >> 8) & 0xff;
47162306a36Sopenharmony_ci	int shift_left = (kcontrol->private_value >> 16) & 0x07;
47262306a36Sopenharmony_ci	int shift_right = (kcontrol->private_value >> 19) & 0x07;
47362306a36Sopenharmony_ci	int mask = (kcontrol->private_value >> 24) & 0xff;
47462306a36Sopenharmony_ci	int invert = (kcontrol->private_value >> 22) & 1;
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	mutex_lock(&chip->mixer_lock);
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	ucontrol->value.integer.value[0] =
47962306a36Sopenharmony_ci		(chip->reg_image[left_reg] >> shift_left) & mask;
48062306a36Sopenharmony_ci	ucontrol->value.integer.value[1] =
48162306a36Sopenharmony_ci		(chip->reg_image[right_reg] >> shift_right) & mask;
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	if (invert) {
48462306a36Sopenharmony_ci		ucontrol->value.integer.value[0] =
48562306a36Sopenharmony_ci			mask - ucontrol->value.integer.value[0];
48662306a36Sopenharmony_ci		ucontrol->value.integer.value[1] =
48762306a36Sopenharmony_ci			mask - ucontrol->value.integer.value[1];
48862306a36Sopenharmony_ci	}
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	mutex_unlock(&chip->mixer_lock);
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	return 0;
49362306a36Sopenharmony_ci}
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_cistatic int snd_at73c213_stereo_put(struct snd_kcontrol *kcontrol,
49662306a36Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol)
49762306a36Sopenharmony_ci{
49862306a36Sopenharmony_ci	struct snd_at73c213 *chip = snd_kcontrol_chip(kcontrol);
49962306a36Sopenharmony_ci	int left_reg = kcontrol->private_value & 0xff;
50062306a36Sopenharmony_ci	int right_reg = (kcontrol->private_value >> 8) & 0xff;
50162306a36Sopenharmony_ci	int shift_left = (kcontrol->private_value >> 16) & 0x07;
50262306a36Sopenharmony_ci	int shift_right = (kcontrol->private_value >> 19) & 0x07;
50362306a36Sopenharmony_ci	int mask = (kcontrol->private_value >> 24) & 0xff;
50462306a36Sopenharmony_ci	int invert = (kcontrol->private_value >> 22) & 1;
50562306a36Sopenharmony_ci	int change, retval;
50662306a36Sopenharmony_ci	unsigned short val1, val2;
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	val1 = ucontrol->value.integer.value[0] & mask;
50962306a36Sopenharmony_ci	val2 = ucontrol->value.integer.value[1] & mask;
51062306a36Sopenharmony_ci	if (invert) {
51162306a36Sopenharmony_ci		val1 = mask - val1;
51262306a36Sopenharmony_ci		val2 = mask - val2;
51362306a36Sopenharmony_ci	}
51462306a36Sopenharmony_ci	val1 <<= shift_left;
51562306a36Sopenharmony_ci	val2 <<= shift_right;
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	mutex_lock(&chip->mixer_lock);
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	val1 = (chip->reg_image[left_reg] & ~(mask << shift_left)) | val1;
52062306a36Sopenharmony_ci	val2 = (chip->reg_image[right_reg] & ~(mask << shift_right)) | val2;
52162306a36Sopenharmony_ci	change = val1 != chip->reg_image[left_reg]
52262306a36Sopenharmony_ci		|| val2 != chip->reg_image[right_reg];
52362306a36Sopenharmony_ci	retval = snd_at73c213_write_reg(chip, left_reg, val1);
52462306a36Sopenharmony_ci	if (retval) {
52562306a36Sopenharmony_ci		mutex_unlock(&chip->mixer_lock);
52662306a36Sopenharmony_ci		goto out;
52762306a36Sopenharmony_ci	}
52862306a36Sopenharmony_ci	retval = snd_at73c213_write_reg(chip, right_reg, val2);
52962306a36Sopenharmony_ci	if (retval) {
53062306a36Sopenharmony_ci		mutex_unlock(&chip->mixer_lock);
53162306a36Sopenharmony_ci		goto out;
53262306a36Sopenharmony_ci	}
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	mutex_unlock(&chip->mixer_lock);
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	return change;
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ciout:
53962306a36Sopenharmony_ci	return retval;
54062306a36Sopenharmony_ci}
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci#define snd_at73c213_mono_switch_info	snd_ctl_boolean_mono_info
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_cistatic int snd_at73c213_mono_switch_get(struct snd_kcontrol *kcontrol,
54562306a36Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol)
54662306a36Sopenharmony_ci{
54762306a36Sopenharmony_ci	struct snd_at73c213 *chip = snd_kcontrol_chip(kcontrol);
54862306a36Sopenharmony_ci	int reg = kcontrol->private_value & 0xff;
54962306a36Sopenharmony_ci	int shift = (kcontrol->private_value >> 8) & 0xff;
55062306a36Sopenharmony_ci	int invert = (kcontrol->private_value >> 24) & 0xff;
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	mutex_lock(&chip->mixer_lock);
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	ucontrol->value.integer.value[0] =
55562306a36Sopenharmony_ci		(chip->reg_image[reg] >> shift) & 0x01;
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	if (invert)
55862306a36Sopenharmony_ci		ucontrol->value.integer.value[0] =
55962306a36Sopenharmony_ci			0x01 - ucontrol->value.integer.value[0];
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci	mutex_unlock(&chip->mixer_lock);
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci	return 0;
56462306a36Sopenharmony_ci}
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_cistatic int snd_at73c213_mono_switch_put(struct snd_kcontrol *kcontrol,
56762306a36Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol)
56862306a36Sopenharmony_ci{
56962306a36Sopenharmony_ci	struct snd_at73c213 *chip = snd_kcontrol_chip(kcontrol);
57062306a36Sopenharmony_ci	int reg = kcontrol->private_value & 0xff;
57162306a36Sopenharmony_ci	int shift = (kcontrol->private_value >> 8) & 0xff;
57262306a36Sopenharmony_ci	int mask = (kcontrol->private_value >> 16) & 0xff;
57362306a36Sopenharmony_ci	int invert = (kcontrol->private_value >> 24) & 0xff;
57462306a36Sopenharmony_ci	int change, retval;
57562306a36Sopenharmony_ci	unsigned short val;
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci	if (ucontrol->value.integer.value[0])
57862306a36Sopenharmony_ci		val = mask;
57962306a36Sopenharmony_ci	else
58062306a36Sopenharmony_ci		val = 0;
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	if (invert)
58362306a36Sopenharmony_ci		val = mask - val;
58462306a36Sopenharmony_ci	val <<= shift;
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci	mutex_lock(&chip->mixer_lock);
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci	val |= (chip->reg_image[reg] & ~(mask << shift));
58962306a36Sopenharmony_ci	change = val != chip->reg_image[reg];
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	retval = snd_at73c213_write_reg(chip, reg, val);
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci	mutex_unlock(&chip->mixer_lock);
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	if (retval)
59662306a36Sopenharmony_ci		return retval;
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci	return change;
59962306a36Sopenharmony_ci}
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_cistatic int snd_at73c213_pa_volume_info(struct snd_kcontrol *kcontrol,
60262306a36Sopenharmony_ci				  struct snd_ctl_elem_info *uinfo)
60362306a36Sopenharmony_ci{
60462306a36Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
60562306a36Sopenharmony_ci	uinfo->count = 1;
60662306a36Sopenharmony_ci	uinfo->value.integer.min = 0;
60762306a36Sopenharmony_ci	uinfo->value.integer.max = ((kcontrol->private_value >> 16) & 0xff) - 1;
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci	return 0;
61062306a36Sopenharmony_ci}
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_cistatic int snd_at73c213_line_capture_volume_info(
61362306a36Sopenharmony_ci		struct snd_kcontrol *kcontrol,
61462306a36Sopenharmony_ci		struct snd_ctl_elem_info *uinfo)
61562306a36Sopenharmony_ci{
61662306a36Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
61762306a36Sopenharmony_ci	uinfo->count = 2;
61862306a36Sopenharmony_ci	/* When inverted will give values 0x10001 => 0. */
61962306a36Sopenharmony_ci	uinfo->value.integer.min = 14;
62062306a36Sopenharmony_ci	uinfo->value.integer.max = 31;
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	return 0;
62362306a36Sopenharmony_ci}
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_cistatic int snd_at73c213_aux_capture_volume_info(
62662306a36Sopenharmony_ci		struct snd_kcontrol *kcontrol,
62762306a36Sopenharmony_ci		struct snd_ctl_elem_info *uinfo)
62862306a36Sopenharmony_ci{
62962306a36Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
63062306a36Sopenharmony_ci	uinfo->count = 1;
63162306a36Sopenharmony_ci	/* When inverted will give values 0x10001 => 0. */
63262306a36Sopenharmony_ci	uinfo->value.integer.min = 14;
63362306a36Sopenharmony_ci	uinfo->value.integer.max = 31;
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci	return 0;
63662306a36Sopenharmony_ci}
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci#define AT73C213_MONO_SWITCH(xname, xindex, reg, shift, mask, invert)	\
63962306a36Sopenharmony_ci{									\
64062306a36Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,				\
64162306a36Sopenharmony_ci	.name = xname,							\
64262306a36Sopenharmony_ci	.index = xindex,						\
64362306a36Sopenharmony_ci	.info = snd_at73c213_mono_switch_info,				\
64462306a36Sopenharmony_ci	.get = snd_at73c213_mono_switch_get,				\
64562306a36Sopenharmony_ci	.put = snd_at73c213_mono_switch_put,				\
64662306a36Sopenharmony_ci	.private_value = (reg | (shift << 8) | (mask << 16) | (invert << 24)) \
64762306a36Sopenharmony_ci}
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci#define AT73C213_STEREO(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \
65062306a36Sopenharmony_ci{									\
65162306a36Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,				\
65262306a36Sopenharmony_ci	.name = xname,							\
65362306a36Sopenharmony_ci	.index = xindex,						\
65462306a36Sopenharmony_ci	.info = snd_at73c213_stereo_info,				\
65562306a36Sopenharmony_ci	.get = snd_at73c213_stereo_get,					\
65662306a36Sopenharmony_ci	.put = snd_at73c213_stereo_put,					\
65762306a36Sopenharmony_ci	.private_value = (left_reg | (right_reg << 8)			\
65862306a36Sopenharmony_ci			| (shift_left << 16) | (shift_right << 19)	\
65962306a36Sopenharmony_ci			| (mask << 24) | (invert << 22))		\
66062306a36Sopenharmony_ci}
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_at73c213_controls[] = {
66362306a36Sopenharmony_ciAT73C213_STEREO("Master Playback Volume", 0, DAC_LMPG, DAC_RMPG, 0, 0, 0x1f, 1),
66462306a36Sopenharmony_ciAT73C213_STEREO("Master Playback Switch", 0, DAC_LMPG, DAC_RMPG, 5, 5, 1, 1),
66562306a36Sopenharmony_ciAT73C213_STEREO("PCM Playback Volume", 0, DAC_LLOG, DAC_RLOG, 0, 0, 0x1f, 1),
66662306a36Sopenharmony_ciAT73C213_STEREO("PCM Playback Switch", 0, DAC_LLOG, DAC_RLOG, 5, 5, 1, 1),
66762306a36Sopenharmony_ciAT73C213_MONO_SWITCH("Mono PA Playback Switch", 0, DAC_CTRL, DAC_CTRL_ONPADRV,
66862306a36Sopenharmony_ci		     0x01, 0),
66962306a36Sopenharmony_ci{
67062306a36Sopenharmony_ci	.iface	= SNDRV_CTL_ELEM_IFACE_MIXER,
67162306a36Sopenharmony_ci	.name	= "PA Playback Volume",
67262306a36Sopenharmony_ci	.index	= 0,
67362306a36Sopenharmony_ci	.info	= snd_at73c213_pa_volume_info,
67462306a36Sopenharmony_ci	.get	= snd_at73c213_mono_get,
67562306a36Sopenharmony_ci	.put	= snd_at73c213_mono_put,
67662306a36Sopenharmony_ci	.private_value	= PA_CTRL | (PA_CTRL_APAGAIN << 8) | \
67762306a36Sopenharmony_ci		(0x0f << 16) | (1 << 24),
67862306a36Sopenharmony_ci},
67962306a36Sopenharmony_ciAT73C213_MONO_SWITCH("PA High Gain Playback Switch", 0, PA_CTRL, PA_CTRL_APALP,
68062306a36Sopenharmony_ci		     0x01, 1),
68162306a36Sopenharmony_ciAT73C213_MONO_SWITCH("PA Playback Switch", 0, PA_CTRL, PA_CTRL_APAON, 0x01, 0),
68262306a36Sopenharmony_ci{
68362306a36Sopenharmony_ci	.iface	= SNDRV_CTL_ELEM_IFACE_MIXER,
68462306a36Sopenharmony_ci	.name	= "Aux Capture Volume",
68562306a36Sopenharmony_ci	.index	= 0,
68662306a36Sopenharmony_ci	.info	= snd_at73c213_aux_capture_volume_info,
68762306a36Sopenharmony_ci	.get	= snd_at73c213_mono_get,
68862306a36Sopenharmony_ci	.put	= snd_at73c213_mono_put,
68962306a36Sopenharmony_ci	.private_value	= DAC_AUXG | (0 << 8) | (0x1f << 16) | (1 << 24),
69062306a36Sopenharmony_ci},
69162306a36Sopenharmony_ciAT73C213_MONO_SWITCH("Aux Capture Switch", 0, DAC_CTRL, DAC_CTRL_ONAUXIN,
69262306a36Sopenharmony_ci		     0x01, 0),
69362306a36Sopenharmony_ci{
69462306a36Sopenharmony_ci	.iface	= SNDRV_CTL_ELEM_IFACE_MIXER,
69562306a36Sopenharmony_ci	.name	= "Line Capture Volume",
69662306a36Sopenharmony_ci	.index	= 0,
69762306a36Sopenharmony_ci	.info	= snd_at73c213_line_capture_volume_info,
69862306a36Sopenharmony_ci	.get	= snd_at73c213_stereo_get,
69962306a36Sopenharmony_ci	.put	= snd_at73c213_stereo_put,
70062306a36Sopenharmony_ci	.private_value	= DAC_LLIG | (DAC_RLIG << 8) | (0 << 16) | (0 << 19)
70162306a36Sopenharmony_ci		| (0x1f << 24) | (1 << 22),
70262306a36Sopenharmony_ci},
70362306a36Sopenharmony_ciAT73C213_MONO_SWITCH("Line Capture Switch", 0, DAC_CTRL, 0, 0x03, 0),
70462306a36Sopenharmony_ci};
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_cistatic int snd_at73c213_mixer(struct snd_at73c213 *chip)
70762306a36Sopenharmony_ci{
70862306a36Sopenharmony_ci	struct snd_card *card;
70962306a36Sopenharmony_ci	int errval, idx;
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	if (chip == NULL || chip->pcm == NULL)
71262306a36Sopenharmony_ci		return -EINVAL;
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci	card = chip->card;
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci	strcpy(card->mixername, chip->pcm->name);
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	for (idx = 0; idx < ARRAY_SIZE(snd_at73c213_controls); idx++) {
71962306a36Sopenharmony_ci		errval = snd_ctl_add(card,
72062306a36Sopenharmony_ci				snd_ctl_new1(&snd_at73c213_controls[idx],
72162306a36Sopenharmony_ci					chip));
72262306a36Sopenharmony_ci		if (errval < 0)
72362306a36Sopenharmony_ci			goto cleanup;
72462306a36Sopenharmony_ci	}
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	return 0;
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_cicleanup:
72962306a36Sopenharmony_ci	for (idx = 1; idx < ARRAY_SIZE(snd_at73c213_controls) + 1; idx++) {
73062306a36Sopenharmony_ci		struct snd_kcontrol *kctl;
73162306a36Sopenharmony_ci		kctl = snd_ctl_find_numid(card, idx);
73262306a36Sopenharmony_ci		if (kctl)
73362306a36Sopenharmony_ci			snd_ctl_remove(card, kctl);
73462306a36Sopenharmony_ci	}
73562306a36Sopenharmony_ci	return errval;
73662306a36Sopenharmony_ci}
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci/*
73962306a36Sopenharmony_ci * Device functions
74062306a36Sopenharmony_ci */
74162306a36Sopenharmony_cistatic int snd_at73c213_ssc_init(struct snd_at73c213 *chip)
74262306a36Sopenharmony_ci{
74362306a36Sopenharmony_ci	/*
74462306a36Sopenharmony_ci	 * Continuous clock output.
74562306a36Sopenharmony_ci	 * Starts on falling TF.
74662306a36Sopenharmony_ci	 * Delay 1 cycle (1 bit).
74762306a36Sopenharmony_ci	 * Periode is 16 bit (16 - 1).
74862306a36Sopenharmony_ci	 */
74962306a36Sopenharmony_ci	ssc_writel(chip->ssc->regs, TCMR,
75062306a36Sopenharmony_ci			SSC_BF(TCMR_CKO, 1)
75162306a36Sopenharmony_ci			| SSC_BF(TCMR_START, 4)
75262306a36Sopenharmony_ci			| SSC_BF(TCMR_STTDLY, 1)
75362306a36Sopenharmony_ci			| SSC_BF(TCMR_PERIOD, 16 - 1));
75462306a36Sopenharmony_ci	/*
75562306a36Sopenharmony_ci	 * Data length is 16 bit (16 - 1).
75662306a36Sopenharmony_ci	 * Transmit MSB first.
75762306a36Sopenharmony_ci	 * Transmit 2 words each transfer.
75862306a36Sopenharmony_ci	 * Frame sync length is 16 bit (16 - 1).
75962306a36Sopenharmony_ci	 * Frame starts on negative pulse.
76062306a36Sopenharmony_ci	 */
76162306a36Sopenharmony_ci	ssc_writel(chip->ssc->regs, TFMR,
76262306a36Sopenharmony_ci			SSC_BF(TFMR_DATLEN, 16 - 1)
76362306a36Sopenharmony_ci			| SSC_BIT(TFMR_MSBF)
76462306a36Sopenharmony_ci			| SSC_BF(TFMR_DATNB, 1)
76562306a36Sopenharmony_ci			| SSC_BF(TFMR_FSLEN, 16 - 1)
76662306a36Sopenharmony_ci			| SSC_BF(TFMR_FSOS, 1));
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci	return 0;
76962306a36Sopenharmony_ci}
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_cistatic int snd_at73c213_chip_init(struct snd_at73c213 *chip)
77262306a36Sopenharmony_ci{
77362306a36Sopenharmony_ci	int retval;
77462306a36Sopenharmony_ci	unsigned char dac_ctrl = 0;
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci	retval = snd_at73c213_set_bitrate(chip);
77762306a36Sopenharmony_ci	if (retval)
77862306a36Sopenharmony_ci		goto out;
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_ci	/* Enable DAC master clock. */
78162306a36Sopenharmony_ci	retval = clk_enable(chip->board->dac_clk);
78262306a36Sopenharmony_ci	if (retval)
78362306a36Sopenharmony_ci		goto out;
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci	/* Initialize at73c213 on SPI bus. */
78662306a36Sopenharmony_ci	retval = snd_at73c213_write_reg(chip, DAC_RST, 0x04);
78762306a36Sopenharmony_ci	if (retval)
78862306a36Sopenharmony_ci		goto out_clk;
78962306a36Sopenharmony_ci	msleep(1);
79062306a36Sopenharmony_ci	retval = snd_at73c213_write_reg(chip, DAC_RST, 0x03);
79162306a36Sopenharmony_ci	if (retval)
79262306a36Sopenharmony_ci		goto out_clk;
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci	/* Precharge everything. */
79562306a36Sopenharmony_ci	retval = snd_at73c213_write_reg(chip, DAC_PRECH, 0xff);
79662306a36Sopenharmony_ci	if (retval)
79762306a36Sopenharmony_ci		goto out_clk;
79862306a36Sopenharmony_ci	retval = snd_at73c213_write_reg(chip, PA_CTRL, (1<<PA_CTRL_APAPRECH));
79962306a36Sopenharmony_ci	if (retval)
80062306a36Sopenharmony_ci		goto out_clk;
80162306a36Sopenharmony_ci	retval = snd_at73c213_write_reg(chip, DAC_CTRL,
80262306a36Sopenharmony_ci			(1<<DAC_CTRL_ONLNOL) | (1<<DAC_CTRL_ONLNOR));
80362306a36Sopenharmony_ci	if (retval)
80462306a36Sopenharmony_ci		goto out_clk;
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci	msleep(50);
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci	/* Stop precharging PA. */
80962306a36Sopenharmony_ci	retval = snd_at73c213_write_reg(chip, PA_CTRL,
81062306a36Sopenharmony_ci			(1<<PA_CTRL_APALP) | 0x0f);
81162306a36Sopenharmony_ci	if (retval)
81262306a36Sopenharmony_ci		goto out_clk;
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci	msleep(450);
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci	/* Stop precharging DAC, turn on master power. */
81762306a36Sopenharmony_ci	retval = snd_at73c213_write_reg(chip, DAC_PRECH, (1<<DAC_PRECH_ONMSTR));
81862306a36Sopenharmony_ci	if (retval)
81962306a36Sopenharmony_ci		goto out_clk;
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci	msleep(1);
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci	/* Turn on DAC. */
82462306a36Sopenharmony_ci	dac_ctrl = (1<<DAC_CTRL_ONDACL) | (1<<DAC_CTRL_ONDACR)
82562306a36Sopenharmony_ci		| (1<<DAC_CTRL_ONLNOL) | (1<<DAC_CTRL_ONLNOR);
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci	retval = snd_at73c213_write_reg(chip, DAC_CTRL, dac_ctrl);
82862306a36Sopenharmony_ci	if (retval)
82962306a36Sopenharmony_ci		goto out_clk;
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci	/* Mute sound. */
83262306a36Sopenharmony_ci	retval = snd_at73c213_write_reg(chip, DAC_LMPG, 0x3f);
83362306a36Sopenharmony_ci	if (retval)
83462306a36Sopenharmony_ci		goto out_clk;
83562306a36Sopenharmony_ci	retval = snd_at73c213_write_reg(chip, DAC_RMPG, 0x3f);
83662306a36Sopenharmony_ci	if (retval)
83762306a36Sopenharmony_ci		goto out_clk;
83862306a36Sopenharmony_ci	retval = snd_at73c213_write_reg(chip, DAC_LLOG, 0x3f);
83962306a36Sopenharmony_ci	if (retval)
84062306a36Sopenharmony_ci		goto out_clk;
84162306a36Sopenharmony_ci	retval = snd_at73c213_write_reg(chip, DAC_RLOG, 0x3f);
84262306a36Sopenharmony_ci	if (retval)
84362306a36Sopenharmony_ci		goto out_clk;
84462306a36Sopenharmony_ci	retval = snd_at73c213_write_reg(chip, DAC_LLIG, 0x11);
84562306a36Sopenharmony_ci	if (retval)
84662306a36Sopenharmony_ci		goto out_clk;
84762306a36Sopenharmony_ci	retval = snd_at73c213_write_reg(chip, DAC_RLIG, 0x11);
84862306a36Sopenharmony_ci	if (retval)
84962306a36Sopenharmony_ci		goto out_clk;
85062306a36Sopenharmony_ci	retval = snd_at73c213_write_reg(chip, DAC_AUXG, 0x11);
85162306a36Sopenharmony_ci	if (retval)
85262306a36Sopenharmony_ci		goto out_clk;
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci	/* Enable I2S device, i.e. clock output. */
85562306a36Sopenharmony_ci	ssc_writel(chip->ssc->regs, CR, SSC_BIT(CR_TXEN));
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci	goto out;
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ciout_clk:
86062306a36Sopenharmony_ci	clk_disable(chip->board->dac_clk);
86162306a36Sopenharmony_ciout:
86262306a36Sopenharmony_ci	return retval;
86362306a36Sopenharmony_ci}
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_cistatic int snd_at73c213_dev_free(struct snd_device *device)
86662306a36Sopenharmony_ci{
86762306a36Sopenharmony_ci	struct snd_at73c213 *chip = device->device_data;
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_ci	ssc_writel(chip->ssc->regs, CR, SSC_BIT(CR_TXDIS));
87062306a36Sopenharmony_ci	if (chip->irq >= 0) {
87162306a36Sopenharmony_ci		free_irq(chip->irq, chip);
87262306a36Sopenharmony_ci		chip->irq = -1;
87362306a36Sopenharmony_ci	}
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci	return 0;
87662306a36Sopenharmony_ci}
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_cistatic int snd_at73c213_dev_init(struct snd_card *card,
87962306a36Sopenharmony_ci				 struct spi_device *spi)
88062306a36Sopenharmony_ci{
88162306a36Sopenharmony_ci	static const struct snd_device_ops ops = {
88262306a36Sopenharmony_ci		.dev_free	= snd_at73c213_dev_free,
88362306a36Sopenharmony_ci	};
88462306a36Sopenharmony_ci	struct snd_at73c213 *chip = get_chip(card);
88562306a36Sopenharmony_ci	int irq, retval;
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_ci	irq = chip->ssc->irq;
88862306a36Sopenharmony_ci	if (irq < 0)
88962306a36Sopenharmony_ci		return irq;
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci	spin_lock_init(&chip->lock);
89262306a36Sopenharmony_ci	mutex_init(&chip->mixer_lock);
89362306a36Sopenharmony_ci	chip->card = card;
89462306a36Sopenharmony_ci	chip->irq = -1;
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_ci	retval = clk_enable(chip->ssc->clk);
89762306a36Sopenharmony_ci	if (retval)
89862306a36Sopenharmony_ci		return retval;
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci	retval = request_irq(irq, snd_at73c213_interrupt, 0, "at73c213", chip);
90162306a36Sopenharmony_ci	if (retval) {
90262306a36Sopenharmony_ci		dev_dbg(&chip->spi->dev, "unable to request irq %d\n", irq);
90362306a36Sopenharmony_ci		goto out;
90462306a36Sopenharmony_ci	}
90562306a36Sopenharmony_ci	chip->irq = irq;
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci	memcpy(&chip->reg_image, &snd_at73c213_original_image,
90862306a36Sopenharmony_ci			sizeof(snd_at73c213_original_image));
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci	retval = snd_at73c213_ssc_init(chip);
91162306a36Sopenharmony_ci	if (retval)
91262306a36Sopenharmony_ci		goto out_irq;
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ci	retval = snd_at73c213_chip_init(chip);
91562306a36Sopenharmony_ci	if (retval)
91662306a36Sopenharmony_ci		goto out_irq;
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci	retval = snd_at73c213_pcm_new(chip, 0);
91962306a36Sopenharmony_ci	if (retval)
92062306a36Sopenharmony_ci		goto out_irq;
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_ci	retval = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
92362306a36Sopenharmony_ci	if (retval)
92462306a36Sopenharmony_ci		goto out_irq;
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci	retval = snd_at73c213_mixer(chip);
92762306a36Sopenharmony_ci	if (retval)
92862306a36Sopenharmony_ci		goto out_snd_dev;
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci	goto out;
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ciout_snd_dev:
93362306a36Sopenharmony_ci	snd_device_free(card, chip);
93462306a36Sopenharmony_ciout_irq:
93562306a36Sopenharmony_ci	free_irq(chip->irq, chip);
93662306a36Sopenharmony_ci	chip->irq = -1;
93762306a36Sopenharmony_ciout:
93862306a36Sopenharmony_ci	clk_disable(chip->ssc->clk);
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci	return retval;
94162306a36Sopenharmony_ci}
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_cistatic int snd_at73c213_probe(struct spi_device *spi)
94462306a36Sopenharmony_ci{
94562306a36Sopenharmony_ci	struct snd_card			*card;
94662306a36Sopenharmony_ci	struct snd_at73c213		*chip;
94762306a36Sopenharmony_ci	struct at73c213_board_info	*board;
94862306a36Sopenharmony_ci	int				retval;
94962306a36Sopenharmony_ci	char				id[16];
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_ci	board = spi->dev.platform_data;
95262306a36Sopenharmony_ci	if (!board) {
95362306a36Sopenharmony_ci		dev_dbg(&spi->dev, "no platform_data\n");
95462306a36Sopenharmony_ci		return -ENXIO;
95562306a36Sopenharmony_ci	}
95662306a36Sopenharmony_ci
95762306a36Sopenharmony_ci	if (!board->dac_clk) {
95862306a36Sopenharmony_ci		dev_dbg(&spi->dev, "no DAC clk\n");
95962306a36Sopenharmony_ci		return -ENXIO;
96062306a36Sopenharmony_ci	}
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_ci	if (IS_ERR(board->dac_clk)) {
96362306a36Sopenharmony_ci		dev_dbg(&spi->dev, "no DAC clk\n");
96462306a36Sopenharmony_ci		return PTR_ERR(board->dac_clk);
96562306a36Sopenharmony_ci	}
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci	/* Allocate "card" using some unused identifiers. */
96862306a36Sopenharmony_ci	snprintf(id, sizeof id, "at73c213_%d", board->ssc_id);
96962306a36Sopenharmony_ci	retval = snd_card_new(&spi->dev, -1, id, THIS_MODULE,
97062306a36Sopenharmony_ci			      sizeof(struct snd_at73c213), &card);
97162306a36Sopenharmony_ci	if (retval < 0)
97262306a36Sopenharmony_ci		goto out;
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_ci	chip = card->private_data;
97562306a36Sopenharmony_ci	chip->spi = spi;
97662306a36Sopenharmony_ci	chip->board = board;
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_ci	chip->ssc = ssc_request(board->ssc_id);
97962306a36Sopenharmony_ci	if (IS_ERR(chip->ssc)) {
98062306a36Sopenharmony_ci		dev_dbg(&spi->dev, "could not get ssc%d device\n",
98162306a36Sopenharmony_ci				board->ssc_id);
98262306a36Sopenharmony_ci		retval = PTR_ERR(chip->ssc);
98362306a36Sopenharmony_ci		goto out_card;
98462306a36Sopenharmony_ci	}
98562306a36Sopenharmony_ci
98662306a36Sopenharmony_ci	retval = snd_at73c213_dev_init(card, spi);
98762306a36Sopenharmony_ci	if (retval)
98862306a36Sopenharmony_ci		goto out_ssc;
98962306a36Sopenharmony_ci
99062306a36Sopenharmony_ci	strcpy(card->driver, "at73c213");
99162306a36Sopenharmony_ci	strcpy(card->shortname, board->shortname);
99262306a36Sopenharmony_ci	sprintf(card->longname, "%s on irq %d", card->shortname, chip->irq);
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_ci	retval = snd_card_register(card);
99562306a36Sopenharmony_ci	if (retval)
99662306a36Sopenharmony_ci		goto out_ssc;
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ci	dev_set_drvdata(&spi->dev, card);
99962306a36Sopenharmony_ci
100062306a36Sopenharmony_ci	goto out;
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_ciout_ssc:
100362306a36Sopenharmony_ci	ssc_free(chip->ssc);
100462306a36Sopenharmony_ciout_card:
100562306a36Sopenharmony_ci	snd_card_free(card);
100662306a36Sopenharmony_ciout:
100762306a36Sopenharmony_ci	return retval;
100862306a36Sopenharmony_ci}
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_cistatic void snd_at73c213_remove(struct spi_device *spi)
101162306a36Sopenharmony_ci{
101262306a36Sopenharmony_ci	struct snd_card *card = dev_get_drvdata(&spi->dev);
101362306a36Sopenharmony_ci	struct snd_at73c213 *chip = card->private_data;
101462306a36Sopenharmony_ci	int retval;
101562306a36Sopenharmony_ci
101662306a36Sopenharmony_ci	/* Stop playback. */
101762306a36Sopenharmony_ci	retval = clk_enable(chip->ssc->clk);
101862306a36Sopenharmony_ci	if (retval)
101962306a36Sopenharmony_ci		goto out;
102062306a36Sopenharmony_ci	ssc_writel(chip->ssc->regs, CR, SSC_BIT(CR_TXDIS));
102162306a36Sopenharmony_ci	clk_disable(chip->ssc->clk);
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci	/* Mute sound. */
102462306a36Sopenharmony_ci	retval = snd_at73c213_write_reg(chip, DAC_LMPG, 0x3f);
102562306a36Sopenharmony_ci	if (retval)
102662306a36Sopenharmony_ci		goto out;
102762306a36Sopenharmony_ci	retval = snd_at73c213_write_reg(chip, DAC_RMPG, 0x3f);
102862306a36Sopenharmony_ci	if (retval)
102962306a36Sopenharmony_ci		goto out;
103062306a36Sopenharmony_ci	retval = snd_at73c213_write_reg(chip, DAC_LLOG, 0x3f);
103162306a36Sopenharmony_ci	if (retval)
103262306a36Sopenharmony_ci		goto out;
103362306a36Sopenharmony_ci	retval = snd_at73c213_write_reg(chip, DAC_RLOG, 0x3f);
103462306a36Sopenharmony_ci	if (retval)
103562306a36Sopenharmony_ci		goto out;
103662306a36Sopenharmony_ci	retval = snd_at73c213_write_reg(chip, DAC_LLIG, 0x11);
103762306a36Sopenharmony_ci	if (retval)
103862306a36Sopenharmony_ci		goto out;
103962306a36Sopenharmony_ci	retval = snd_at73c213_write_reg(chip, DAC_RLIG, 0x11);
104062306a36Sopenharmony_ci	if (retval)
104162306a36Sopenharmony_ci		goto out;
104262306a36Sopenharmony_ci	retval = snd_at73c213_write_reg(chip, DAC_AUXG, 0x11);
104362306a36Sopenharmony_ci	if (retval)
104462306a36Sopenharmony_ci		goto out;
104562306a36Sopenharmony_ci
104662306a36Sopenharmony_ci	/* Turn off PA. */
104762306a36Sopenharmony_ci	retval = snd_at73c213_write_reg(chip, PA_CTRL,
104862306a36Sopenharmony_ci					chip->reg_image[PA_CTRL] | 0x0f);
104962306a36Sopenharmony_ci	if (retval)
105062306a36Sopenharmony_ci		goto out;
105162306a36Sopenharmony_ci	msleep(10);
105262306a36Sopenharmony_ci	retval = snd_at73c213_write_reg(chip, PA_CTRL,
105362306a36Sopenharmony_ci					(1 << PA_CTRL_APALP) | 0x0f);
105462306a36Sopenharmony_ci	if (retval)
105562306a36Sopenharmony_ci		goto out;
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_ci	/* Turn off external DAC. */
105862306a36Sopenharmony_ci	retval = snd_at73c213_write_reg(chip, DAC_CTRL, 0x0c);
105962306a36Sopenharmony_ci	if (retval)
106062306a36Sopenharmony_ci		goto out;
106162306a36Sopenharmony_ci	msleep(2);
106262306a36Sopenharmony_ci	retval = snd_at73c213_write_reg(chip, DAC_CTRL, 0x00);
106362306a36Sopenharmony_ci	if (retval)
106462306a36Sopenharmony_ci		goto out;
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_ci	/* Turn off master power. */
106762306a36Sopenharmony_ci	retval = snd_at73c213_write_reg(chip, DAC_PRECH, 0x00);
106862306a36Sopenharmony_ci	if (retval)
106962306a36Sopenharmony_ci		goto out;
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ciout:
107262306a36Sopenharmony_ci	/* Stop DAC master clock. */
107362306a36Sopenharmony_ci	clk_disable(chip->board->dac_clk);
107462306a36Sopenharmony_ci
107562306a36Sopenharmony_ci	ssc_free(chip->ssc);
107662306a36Sopenharmony_ci	snd_card_free(card);
107762306a36Sopenharmony_ci}
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_cistatic int snd_at73c213_suspend(struct device *dev)
108262306a36Sopenharmony_ci{
108362306a36Sopenharmony_ci	struct snd_card *card = dev_get_drvdata(dev);
108462306a36Sopenharmony_ci	struct snd_at73c213 *chip = card->private_data;
108562306a36Sopenharmony_ci
108662306a36Sopenharmony_ci	ssc_writel(chip->ssc->regs, CR, SSC_BIT(CR_TXDIS));
108762306a36Sopenharmony_ci	clk_disable(chip->ssc->clk);
108862306a36Sopenharmony_ci	clk_disable(chip->board->dac_clk);
108962306a36Sopenharmony_ci
109062306a36Sopenharmony_ci	return 0;
109162306a36Sopenharmony_ci}
109262306a36Sopenharmony_ci
109362306a36Sopenharmony_cistatic int snd_at73c213_resume(struct device *dev)
109462306a36Sopenharmony_ci{
109562306a36Sopenharmony_ci	struct snd_card *card = dev_get_drvdata(dev);
109662306a36Sopenharmony_ci	struct snd_at73c213 *chip = card->private_data;
109762306a36Sopenharmony_ci	int retval;
109862306a36Sopenharmony_ci
109962306a36Sopenharmony_ci	retval = clk_enable(chip->board->dac_clk);
110062306a36Sopenharmony_ci	if (retval)
110162306a36Sopenharmony_ci		return retval;
110262306a36Sopenharmony_ci	retval = clk_enable(chip->ssc->clk);
110362306a36Sopenharmony_ci	if (retval) {
110462306a36Sopenharmony_ci		clk_disable(chip->board->dac_clk);
110562306a36Sopenharmony_ci		return retval;
110662306a36Sopenharmony_ci	}
110762306a36Sopenharmony_ci	ssc_writel(chip->ssc->regs, CR, SSC_BIT(CR_TXEN));
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_ci	return 0;
111062306a36Sopenharmony_ci}
111162306a36Sopenharmony_ci
111262306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(at73c213_pm_ops, snd_at73c213_suspend,
111362306a36Sopenharmony_ci		snd_at73c213_resume);
111462306a36Sopenharmony_ci#define AT73C213_PM_OPS (&at73c213_pm_ops)
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_ci#else
111762306a36Sopenharmony_ci#define AT73C213_PM_OPS NULL
111862306a36Sopenharmony_ci#endif
111962306a36Sopenharmony_ci
112062306a36Sopenharmony_cistatic struct spi_driver at73c213_driver = {
112162306a36Sopenharmony_ci	.driver		= {
112262306a36Sopenharmony_ci		.name	= "at73c213",
112362306a36Sopenharmony_ci		.pm	= AT73C213_PM_OPS,
112462306a36Sopenharmony_ci	},
112562306a36Sopenharmony_ci	.probe		= snd_at73c213_probe,
112662306a36Sopenharmony_ci	.remove		= snd_at73c213_remove,
112762306a36Sopenharmony_ci};
112862306a36Sopenharmony_ci
112962306a36Sopenharmony_cimodule_spi_driver(at73c213_driver);
113062306a36Sopenharmony_ci
113162306a36Sopenharmony_ciMODULE_AUTHOR("Hans-Christian Egtvedt <egtvedt@samfundet.no>");
113262306a36Sopenharmony_ciMODULE_DESCRIPTION("Sound driver for AT73C213 with Atmel SSC");
113362306a36Sopenharmony_ciMODULE_LICENSE("GPL");
1134