// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver for AT73C213 16-bit stereo DAC connected to Atmel SSC
*
* Copyright (C) 2006-2007 Atmel Norway
*/
/*#define DEBUG*/
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <sound/initval.h>
#include <sound/control.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <linux/atmel-ssc.h>
#include <linux/spi/spi.h>
#include <linux/spi/at73c213.h>
#include "at73c213.h"
#define BITRATE_MIN 8000 /* Hardware limit? */
#define BITRATE_TARGET CONFIG_SND_AT73C213_TARGET_BITRATE
#define BITRATE_MAX 50000 /* Hardware limit. */
/* Initial (hardware reset) AT73C213 register values. */
static u8 snd_at73c213_original_image[18] =
{
0x00, /* 00 - CTRL */
0x05, /* 01 - LLIG */
0x05, /* 02 - RLIG */
0x08, /* 03 - LPMG */
0x08, /* 04 - RPMG */
0x00, /* 05 - LLOG */
0x00, /* 06 - RLOG */
0x22, /* 07 - OLC */
0x09, /* 08 - MC */
0x00, /* 09 - CSFC */
0x00, /* 0A - MISC */
0x00, /* 0B - */
0x00, /* 0C - PRECH */
0x05, /* 0D - AUXG */
0x00, /* 0E - */
0x00, /* 0F - */
0x00, /* 10 - RST */
0x00, /* 11 - PA_CTRL */
};
struct snd_at73c213 {
struct snd_card *card;
struct snd_pcm *pcm;
struct snd_pcm_substream *substream;
struct at73c213_board_info *board;
int irq;
int period;
unsigned long bitrate;
struct ssc_device *ssc;
struct spi_device *spi;
u8 spi_wbuffer[2];
u8 spi_rbuffer[2];
/* Image of the SPI registers in AT73C213. */
u8 reg_image[18];
/* Protect SSC registers against concurrent access. */
spinlock_t lock;
/* Protect mixer registers against concurrent access. */
struct mutex mixer_lock;
};
#define get_chip(card) ((struct snd_at73c213 *)card->private_data)
static int
snd_at73c213_write_reg(struct snd_at73c213 *chip, u8 reg, u8 val)
{
struct spi_message msg;
struct spi_transfer msg_xfer = {
.len = 2,
.cs_change = 0,
};
int retval;
spi_message_init(&msg);
chip->spi_wbuffer[0] = reg;
chip->spi_wbuffer[1] = val;
msg_xfer.tx_buf = chip->spi_wbuffer;
msg_xfer.rx_buf = chip->spi_rbuffer;
spi_message_add_tail(&msg_xfer, &msg);
retval = spi_sync(chip->spi, &msg);
if (!retval)
chip->reg_image[reg] = val;
return retval;
}
static struct snd_pcm_hardware snd_at73c213_playback_hw = {
.info = SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER,
.formats = SNDRV_PCM_FMTBIT_S16_BE,
.rates = SNDRV_PCM_RATE_CONTINUOUS,
.rate_min = 8000, /* Replaced by chip->bitrate later. */
.rate_max = 50000, /* Replaced by chip->bitrate later. */
.channels_min = 1,
.channels_max = 2,
.buffer_bytes_max = 64 * 1024 - 1,
.period_bytes_min = 512,
.period_bytes_max = 64 * 1024 - 1,
.periods_min = 4,
.periods_max = 1024,
};
/*
* Calculate and set bitrate and divisions.
*/
static int snd_at73c213_set_bitrate(struct snd_at73c213 *chip)
{
unsigned long ssc_rate = clk_get_rate(chip->ssc->clk);
unsigned long dac_rate_new, ssc_div;
int status;
unsigned long ssc_div_max, ssc_div_min;
int