#include <linux/bitrev.h>
#include <linux/gpio/driver.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/spi/spi.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/tlv.h>
#define IDT821034_NB_CHANNEL 4
struct idt821034_amp {
u16 gain;
bool is_muted;
};
struct idt821034 {
struct spi_device *spi;
struct mutex mutex;
u8 spi_tx_buf;
u8 spi_rx_buf;
struct {
u8 codec_conf;
struct {
u8 power;
u8 tx_slot;
u8 rx_slot;
u8 slic_conf;
u8 slic_control;
} ch[IDT821034_NB_CHANNEL];
} cache;
struct {
struct {
struct idt821034_amp amp_out;
struct idt821034_amp amp_in;
} ch[IDT821034_NB_CHANNEL];
} amps;
int max_ch_playback;
int max_ch_capture;
struct gpio_chip gpio_chip;
};
static int idt821034_8bit_write(struct idt821034 *idt821034, u8 val)
{
struct spi_transfer xfer[] = {
{
.tx_buf = &idt821034->spi_tx_buf,
.len = 1,
}, {
.cs_off = 1,
.tx_buf = &idt821034->spi_tx_buf,
.len = 1,
}
};
idt821034->spi_tx_buf = val;
dev_vdbg(&idt821034->spi->dev, "spi xfer wr 0x%x\n", val);
return spi_sync_transfer(idt821034->spi, xfer, 2);
}
static int idt821034_2x8bit_write(struct idt821034 *idt821034, u8 val1, u8 val2)
{
int ret;
ret = idt821034_8bit_write(idt821034, val1);
if (ret)
return ret;
return idt821034_8bit_write(idt821034, val2);
}
static int idt821034_8bit_read(struct idt821034 *idt821034, u8 valw, u8 *valr)
{
struct spi_transfer xfer[] = {
{
.tx_buf = &idt821034->spi_tx_buf,
.rx_buf = &idt821034->spi_rx_buf,
.len = 1,
}, {
.cs_off = 1,
.tx_buf = &idt821034->spi_tx_buf,
.len = 1,
}
};
int ret;
idt821034->spi_tx_buf = valw;
ret = spi_sync_transfer(idt821034->spi, xfer, 2);
if (ret)
return ret;
*valr = idt821034->spi_rx_buf;
dev_vdbg(&idt821034->spi->dev, "spi xfer wr 0x%x, rd 0x%x\n",
valw, *valr);
return 0;
}
#define IDT821034_MODE_CODEC(_ch) (0x80 | ((_ch) << 2))
#define IDT821034_MODE_SLIC(_ch) (0xD0 | ((_ch) << 2))
#define IDT821034_MODE_GAIN(_ch) (0xC0 | ((_ch) << 2))
#define IDT821034_CONF_PWRUP_TX BIT(1) /* from analog input to PCM */
#define IDT821034_CONF_PWRUP_RX BIT(0) /* from PCM to analog output */
static int idt821034_set_channel_power(struct idt821034 *idt821034, u8 ch, u8 power)
{
u8 conf;
int ret;
dev_dbg(&idt821034->spi->dev, "set_channel_power(%u, 0x%x)\n", ch, power);
conf = IDT821034_MODE_CODEC(ch) | idt821034->cache.codec_conf;
if (power & IDT821034_CONF_PWRUP_RX) {
ret = idt821034_2x8bit_write(idt821034,
conf | IDT821034_CONF_PWRUP_RX,
idt821034->cache.ch[ch].rx_slot);
if (ret)
return ret;
}
if (power & IDT821034_CONF_PWRUP_TX) {
ret = idt821034_2x8bit_write(idt821034,
conf | IDT821034_CONF_PWRUP_TX,
idt821034->cache.ch[ch].tx_slot);
if (ret)
return ret;
}
if (!(power & (IDT821034_CONF_PWRUP_TX | IDT821034_CONF_PWRUP_RX))) {
ret = idt821034_2x8bit_write(idt821034, conf, 0);
if (ret)
return ret;
}
idt821034->cache.ch[ch].power = power;
return 0;
}
static u8 idt821034_get_channel_power(struct idt821034 *idt821034, u8 ch)
{
return idt821034->cache.ch[ch].power;
}
#define IDT821034_CONF_ALAW_MODE BIT(5)
#define IDT821034_CONF_DELAY_MODE BIT(4)
static int idt821034_set_codec_conf(struct idt821034 *idt821034, u8 codec_conf)
{
u8 conf;
u8 ts;
int ret;
dev_dbg(&idt821034->spi->dev, "set_codec_conf(0x%x)\n", codec_conf);
conf = IDT821034_MODE_CODEC(0) | codec_conf;
if (idt821034->cache.ch[0].power & IDT821034_CONF_PWRUP_RX) {
conf |= IDT821034_CONF_PWRUP_RX;
ts = idt821034->cache.ch[0].rx_slot;
} else if (idt821034->cache.ch[0].power & IDT821034_CONF_PWRUP_TX) {
conf |= IDT821034_CONF_PWRUP_TX;
ts = idt821034->cache.ch[0].tx_slot;
} else {
ts = 0x00;
}
ret = idt821034_2x8bit_write(idt821034, conf, ts);
if (ret)
return ret;
idt821034->cache.codec_conf = codec_conf;
return 0;
}
static u8 idt821034_get_codec_conf(struct idt821034 *idt821034)
{
return idt821034->cache.codec_conf;
}
#define IDT821034_CH_RX BIT(0) /* from PCM to analog output */
#define IDT821034_CH_TX BIT(1) /* from analog input to PCM */
static int idt821034_set_channel_ts(struct idt821034 *idt821034, u8 ch, u8 ch_dir, u8 ts_num)
{
u8 conf;
int ret;
dev_dbg(&idt821034->spi->dev, "set_channel_ts(%u, 0x%x, %d)\n", ch, ch_dir, ts_num);
conf = IDT821034_MODE_CODEC(ch) | idt821034->cache.codec_conf;
if (ch_dir & IDT821034_CH_RX) {
if (idt821034->cache.ch[ch].power & IDT821034_CONF_PWRUP_RX) {
ret = idt821034_2x8bit_write(idt821034,
conf | IDT821034_CONF_PWRUP_RX,
ts_num);
if (ret)
return ret;
}
idt821034->cache.ch[ch].rx_slot = ts_num;
}
if (ch_dir & IDT821034_CH_TX) {
if (idt821034->cache.ch[ch].power & IDT821034_CONF_PWRUP_TX) {
ret = idt821034_2x8bit_write(idt821034,
conf | IDT821034_CONF_PWRUP_TX,
ts_num);
if (ret)
return ret;
}
idt821034->cache.ch[ch].tx_slot = ts_num;
}
return 0;
}
#define IDT821034_SLIC_IO1_IN BIT(1)
#define IDT821034_SLIC_IO0_IN BIT(0)
static int idt821034_set_slic_conf(struct idt821034 *idt821034, u8 ch, u8 slic_dir)
{
u8 conf;
int ret;
dev_dbg(&idt821034->spi->dev, "set_slic_conf(%u, 0x%x)\n", ch, slic_dir);
conf = IDT821034_MODE_SLIC(ch) | slic_dir;
ret = idt821034_2x8bit_write(idt821034, conf, idt821034->cache.ch[ch].slic_control);
if (ret)
return ret;
idt821034->cache.ch[ch].slic_conf = slic_dir;
return 0;
}
static u8 idt821034_get_slic_conf(struct idt821034 *idt821034, u8 ch)
{
return idt821034->cache.ch[ch].slic_conf;
}
static int idt821034_write_slic_raw(struct idt821034 *idt821034, u8 ch, u8 slic_raw)
{
u8 conf;
int ret;
dev_dbg(&idt821034->spi->dev, "write_slic_raw(%u, 0x%x)\n", ch, slic_raw);
conf = IDT821034_MODE_SLIC(ch) | idt821034->cache.ch[ch].slic_conf;
ret = idt821034_2x8bit_write(idt821034, conf, slic_raw);
if (ret)
return ret;
idt821034->cache.ch[ch].slic_control = slic_raw;
return 0;
}
static u8 idt821034_get_written_slic_raw(struct idt821034 *idt821034, u8 ch)
{
return idt821034->cache.ch[ch].slic_control;
}
static int idt821034_read_slic_raw(struct idt821034 *idt821034, u8 ch, u8 *slic_raw)
{
u8 val;
int ret;
val = IDT821034_MODE_SLIC(ch) | idt821034->cache.ch[ch].slic_conf;
ret = idt821034_8bit_write(idt821034, val);
if (ret)
return ret;
ret = idt821034_8bit_read(idt821034, idt821034->cache.ch[ch].slic_control, slic_raw);
if (ret)
return ret;
dev_dbg(&idt821034->spi->dev, "read_slic_raw(%i) 0x%x\n", ch, *slic_raw);
return 0;
}
#define IDT821034_GAIN_RX (0 << 1) /* from PCM to analog output */
#define IDT821034_GAIN_TX (1 << 1) /* from analog input to PCM */
static int idt821034_set_gain_channel(struct idt821034 *idt821034, u8 ch,
u8 gain_type, u16 gain_val)
{
u8 conf;
int ret;
dev_dbg(&idt821034->spi->dev, "set_gain_channel(%u, 0x%x, 0x%x-%d)\n",
ch, gain_type, gain_val, gain_val);
conf = IDT821034_MODE_GAIN(ch) | gain_type;
ret = idt821034_2x8bit_write(idt821034, conf | 0x00, gain_val & 0x007F);
if (ret)
return ret;
ret = idt821034_2x8bit_write(idt821034, conf | 0x01, (gain_val >> 7) & 0x7F);
if (ret)
return ret;
return 0;
}
#define IDT821034_DIR_OUT (1 << 3)
#define IDT821034_DIR_IN (0 << 3)
#define IDT821034_ID(_ch, _dir) (((_ch) & 0x03) | (_dir))
#define IDT821034_ID_OUT(_ch) IDT821034_ID(_ch, IDT821034_DIR_OUT)
#define IDT821034_ID_IN(_ch) IDT821034_ID(_ch, IDT821034_DIR_IN)
#define IDT821034_ID_GET_CHAN(_id) ((_id) & 0x03)
#define IDT821034_ID_GET_DIR(_id) ((_id) & (1 << 3))
#define IDT821034_ID_IS_OUT(_id) (IDT821034_ID_GET_DIR(_id) == IDT821034_DIR_OUT)
static int idt821034_kctrl_gain_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value;
struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
struct idt821034 *idt821034 = snd_soc_component_get_drvdata(component);
int min = mc->min;
int max = mc->max;
unsigned int mask = (1 << fls(max)) - 1;
unsigned int invert = mc->invert;
int val;
u8 ch;
ch = IDT821034_ID_GET_CHAN(mc->reg);
mutex_lock(&idt821034->mutex);
if (IDT821034_ID_IS_OUT(mc->reg))
val = idt821034->amps.ch[ch].amp_out.gain;
else
val = idt821034->amps.ch[ch].amp_in.gain;
mutex_unlock(&idt821034->mutex);
ucontrol->value.integer.value[0] = val & mask;
if (invert)
ucontrol->value.integer.value[0] = max - ucontrol->value.integer.value[0];
else
ucontrol->value.integer.value[0] = ucontrol->value.integer.value[0] - min;
return 0;
}
static int idt821034_kctrl_gain_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value;
struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
struct idt821034 *idt821034 = snd_soc_component_get_drvdata(component);
struct idt821034_amp *amp;
int min = mc->min;
int max = mc->max;
unsigned int mask = (1 << fls(max)) - 1;
unsigned int invert = mc->invert;
unsigned int val;
int ret;
u8 gain_type;
u8 ch;
val = ucontrol->value.integer.value[0];
if (val > max - min)
return -EINVAL;
if (invert)
val = (max - val) & mask;
else
val = (val + min) & mask;
ch = IDT821034_ID_GET_CHAN(mc->reg);
mutex_lock(&idt821034->mutex);
if (IDT821034_ID_IS_OUT(mc->reg)) {
amp = &idt821034->amps.ch[ch].amp_out;
gain_type = IDT821034_GAIN_RX;
} else {
amp = &idt821034->amps.ch[ch].amp_in;
gain_type = IDT821034_GAIN_TX;
}
if (amp->gain == val) {
ret = 0;
goto end;
}
if (!amp->is_muted) {
ret = idt821034_set_gain_channel(idt821034, ch, gain_type, val);
if (ret)
goto end;
}
amp->gain = val;
ret = 1;
end:
mutex_unlock(&idt821034->mutex);
return ret;
}
static int idt821034_kctrl_mute_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
struct idt821034 *idt821034 = snd_soc_component_get_drvdata(component);
int id = kcontrol->private_value;
bool is_muted;
u8 ch;
ch = IDT821034_ID_GET_CHAN(id);
mutex_lock(&idt821034->mutex);
is_muted = IDT821034_ID_IS_OUT(id) ?
idt821034->amps.ch[ch].amp_out.is_muted :
idt821034->amps.ch[ch].amp_in.is_muted;
mutex_unlock(&idt821034->mutex);
ucontrol->value.integer.value[0] = !is_muted;
return 0;
}
static int idt821034_kctrl_mute_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
struct idt821034 *idt821034 = snd_soc_component_get_drvdata(component);
int id = kcontrol->private_value;
struct idt821034_amp *amp;
bool is_mute;
u8 gain_type;
int ret;
u8 ch;
ch = IDT821034_ID_GET_CHAN(id);
is_mute = !ucontrol->value.integer.value[0];
mutex_lock(&idt821034->mutex);
if (IDT821034_ID_IS_OUT(id)) {
amp = &idt821034->amps.ch[ch].amp_out;
gain_type = IDT821034_GAIN_RX;
} else {
amp = &idt821034->amps.ch[ch].amp_in;
gain_type = IDT821034_GAIN_TX;
}
if (amp->is_muted == is_mute) {
ret = 0;
goto end;
}
ret = idt821034_set_gain_channel(idt821034, ch, gain_type,
is_mute ? 0 : amp->gain);
if (ret)
goto end;
amp->is_muted = is_mute;
ret = 1;
end:
mutex_unlock(&idt821034->mutex);
return ret;
}
static const DECLARE_TLV_DB_LINEAR(idt821034_gain_in, -6520, 1306);
#define IDT821034_GAIN_IN_MIN_RAW 1 /* -65.20 dB -> 10^(-65.2/20.0) * 1820 = 1 */
#define IDT821034_GAIN_IN_MAX_RAW 8191 /* 13.06 dB -> 10^(13.06/20.0) * 1820 = 8191 */
#define IDT821034_GAIN_IN_INIT_RAW 1820 /* 0dB -> 10^(0/20) * 1820 = 1820 */
static const DECLARE_TLV_DB_LINEAR(idt821034_gain_out, -6798, 1029);
#define IDT821034_GAIN_OUT_MIN_RAW 1 /* -67.98 dB -> 10^(-67.98/20.0) * 2506 = 1*/
#define IDT821034_GAIN_OUT_MAX_RAW 8191 /* 10.29 dB -> 10^(10.29/20.0) * 2506 = 8191 */
#define IDT821034_GAIN_OUT_INIT_RAW 2506 /* 0dB -> 10^(0/20) * 2506 = 2506 */
static const struct snd_kcontrol_new idt821034_controls[] = {
SOC_SINGLE_RANGE_EXT_TLV("DAC0 Playback Volume", IDT821034_ID_OUT(0), 0,
IDT821034_GAIN_OUT_MIN_RAW, IDT821034_GAIN_OUT_MAX_RAW,
0, idt821034_kctrl_gain_get, idt821034_kctrl_gain_put,
idt821034_gain_out),
SOC_SINGLE_RANGE_EXT_TLV("DAC1 Playback Volume", IDT821034_ID_OUT(1), 0,
IDT821034_GAIN_OUT_MIN_RAW, IDT821034_GAIN_OUT_MAX_RAW,
0, idt821034_kctrl_gain_get, idt821034_kctrl_gain_put,
idt821034_gain_out),
SOC_SINGLE_RANGE_EXT_TLV("DAC2 Playback Volume", IDT821034_ID_OUT(2), 0,
IDT821034_GAIN_OUT_MIN_RAW, IDT821034_GAIN_OUT_MAX_RAW,
0, idt821034_kctrl_gain_get, idt821034_kctrl_gain_put,
idt821034_gain_out),
SOC_SINGLE_RANGE_EXT_TLV("DAC3 Playback Volume", IDT821034_ID_OUT(3), 0,
IDT821034_GAIN_OUT_MIN_RAW, IDT821034_GAIN_OUT_MAX_RAW,
0, idt821034_kctrl_gain_get, idt821034_kctrl_gain_put,
idt821034_gain_out),
SOC_SINGLE_BOOL_EXT("DAC0 Playback Switch", IDT821034_ID_OUT(0),
idt821034_kctrl_mute_get, idt821034_kctrl_mute_put),
SOC_SINGLE_BOOL_EXT("DAC1 Playback Switch", IDT821034_ID_OUT(1),
idt821034_kctrl_mute_get, idt821034_kctrl_mute_put),
SOC_SINGLE_BOOL_EXT("DAC2 Playback Switch", IDT821034_ID_OUT(2),
idt821034_kctrl_mute_get, idt821034_kctrl_mute_put),
SOC_SINGLE_BOOL_EXT("DAC3 Playback Switch", IDT821034_ID_OUT(3),
idt821034_kctrl_mute_get, idt821034_kctrl_mute_put),
SOC_SINGLE_RANGE_EXT_TLV("ADC0 Capture Volume", IDT821034_ID_IN(0), 0,
IDT821034_GAIN_IN_MIN_RAW, IDT821034_GAIN_IN_MAX_RAW,
0, idt821034_kctrl_gain_get, idt821034_kctrl_gain_put,
idt821034_gain_in),
SOC_SINGLE_RANGE_EXT_TLV("ADC1 Capture Volume", IDT821034_ID_IN(1), 0,
IDT821034_GAIN_IN_MIN_RAW, IDT821034_GAIN_IN_MAX_RAW,
0, idt821034_kctrl_gain_get, idt821034_kctrl_gain_put,
idt821034_gain_in),
SOC_SINGLE_RANGE_EXT_TLV("ADC2 Capture Volume", IDT821034_ID_IN(2), 0,
IDT821034_GAIN_IN_MIN_RAW, IDT821034_GAIN_IN_MAX_RAW,
0, idt821034_kctrl_gain_get, idt821034_kctrl_gain_put,
idt821034_gain_in),
SOC_SINGLE_RANGE_EXT_TLV("ADC3 Capture Volume", IDT821034_ID_IN(3), 0,
IDT821034_GAIN_IN_MIN_RAW, IDT821034_GAIN_IN_MAX_RAW,
0, idt821034_kctrl_gain_get, idt821034_kctrl_gain_put,
idt821034_gain_in),
SOC_SINGLE_BOOL_EXT("ADC0 Capture Switch", IDT821034_ID_IN(0),
idt821034_kctrl_mute_get, idt821034_kctrl_mute_put),
SOC_SINGLE_BOOL_EXT("ADC1 Capture Switch", IDT821034_ID_IN(1),
idt821034_kctrl_mute_get, idt821034_kctrl_mute_put),
SOC_SINGLE_BOOL_EXT("ADC2 Capture Switch", IDT821034_ID_IN(2),
idt821034_kctrl_mute_get, idt821034_kctrl_mute_put),
SOC_SINGLE_BOOL_EXT("ADC3 Capture Switch", IDT821034_ID_IN(3),
idt821034_kctrl_mute_get, idt821034_kctrl_mute_put),
};
static int idt821034_power_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
struct idt821034 *idt821034 = snd_soc_component_get_drvdata(component);
unsigned int id = w->shift;
u8 power, mask;
int ret;
u8 ch;
ch = IDT821034_ID_GET_CHAN(id);
mask = IDT821034_ID_IS_OUT(id) ? IDT821034_CONF_PWRUP_RX : IDT821034_CONF_PWRUP_TX;
mutex_lock(&idt821034->mutex);
power = idt821034_get_channel_power(idt821034, ch);
if (SND_SOC_DAPM_EVENT_ON(event))
power |= mask;
else
power &= ~mask;
ret = idt821034_set_channel_power(idt821034, ch, power);
mutex_unlock(&idt821034->mutex);
return ret;
}
static const struct snd_soc_dapm_widget idt821034_dapm_widgets[] = {
SND_SOC_DAPM_DAC_E("DAC0", "Playback", SND_SOC_NOPM, IDT821034_ID_OUT(0), 0,
idt821034_power_event,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_DAC_E("DAC1", "Playback", SND_SOC_NOPM, IDT821034_ID_OUT(1), 0,
idt821034_power_event,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_DAC_E("DAC2", "Playback", SND_SOC_NOPM, IDT821034_ID_OUT(2), 0,
idt821034_power_event,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_DAC_E("DAC3", "Playback", SND_SOC_NOPM, IDT821034_ID_OUT(3), 0,
idt821034_power_event,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_OUTPUT("OUT0"),
SND_SOC_DAPM_OUTPUT("OUT1"),
SND_SOC_DAPM_OUTPUT("OUT2"),
SND_SOC_DAPM_OUTPUT("OUT3"),
SND_SOC_DAPM_DAC_E("ADC0", "Capture", SND_SOC_NOPM, IDT821034_ID_IN(0), 0,
idt821034_power_event,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_DAC_E("ADC1", "Capture", SND_SOC_NOPM, IDT821034_ID_IN(1), 0,
idt821034_power_event,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_DAC_E("ADC2", "Capture", SND_SOC_NOPM, IDT821034_ID_IN(2), 0,
idt821034_power_event,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_DAC_E("ADC3", "Capture", SND_SOC_NOPM, IDT821034_ID_IN(3), 0,
idt821034_power_event,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_INPUT("IN0"),
SND_SOC_DAPM_INPUT("IN1"),
SND_SOC_DAPM_INPUT("IN2"),
SND_SOC_DAPM_INPUT("IN3"),
};
static const struct snd_soc_dapm_route idt821034_dapm_routes[] = {
{ "OUT0", NULL, "DAC0" },
{ "OUT1", NULL, "DAC1" },
{ "OUT2", NULL, "DAC2" },
{ "OUT3", NULL, "DAC3" },
{ "ADC0", NULL, "IN0" },
{ "ADC1", NULL, "IN1" },
{ "ADC2", NULL, "IN2" },
{ "ADC3", NULL, "IN3" },
};
static int idt821034_dai_set_tdm_slot(struct snd_soc_dai *dai,
unsigned int tx_mask, unsigned int rx_mask,
int slots, int width)
{
struct idt821034 *idt821034 = snd_soc_component_get_drvdata(dai->component);
unsigned int mask;
u8 slot;
int ret;
u8 ch;
switch (width) {
case 0:
case 8:
break;
default:
dev_err(dai->dev, "tdm slot width %d not supported\n", width);
return -EINVAL;
}
mask = tx_mask;
slot = 0;
ch = 0;
while (mask && ch < IDT821034_NB_CHANNEL) {
if (mask & 0x1) {
mutex_lock(&idt821034->mutex);
ret = idt821034_set_channel_ts(idt821034, ch, IDT821034_CH_RX, slot);
mutex_unlock(&idt821034->mutex);
if (ret) {
dev_err(dai->dev, "ch%u set tx tdm slot failed (%d)\n",
ch, ret);
return ret;
}
ch++;
}
mask >>= 1;
slot++;
}
if (mask) {
dev_err(dai->dev, "too much tx slots defined (mask = 0x%x) support max %d\n",
tx_mask, IDT821034_NB_CHANNEL);
return -EINVAL;
}
idt821034->max_ch_playback = ch;
mask = rx_mask;
slot = 0;
ch = 0;
while (mask && ch < IDT821034_NB_CHANNEL) {
if (mask & 0x1) {
mutex_lock(&idt821034->mutex);
ret = idt821034_set_channel_ts(idt821034, ch, IDT821034_CH_TX, slot);
mutex_unlock(&idt821034->mutex);
if (ret) {
dev_err(dai->dev, "ch%u set rx tdm slot failed (%d)\n",
ch, ret);
return ret;
}
ch++;
}
mask >>= 1;
slot++;
}
if (mask) {
dev_err(dai->dev, "too much rx slots defined (mask = 0x%x) support max %d\n",
rx_mask, IDT821034_NB_CHANNEL);
return -EINVAL;
}
idt821034->max_ch_capture = ch;
return 0;
}
static int idt821034_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
struct idt821034 *idt821034 = snd_soc_component_get_drvdata(dai->component);
u8 conf;
int ret;
mutex_lock(&idt821034->mutex);
conf = idt821034_get_codec_conf(idt821034);
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_DSP_A:
conf |= IDT821034_CONF_DELAY_MODE;
break;
case SND_SOC_DAIFMT_DSP_B:
conf &= ~IDT821034_CONF_DELAY_MODE;
break;
default:
dev_err(dai->dev, "Unsupported DAI format 0x%x\n",
fmt & SND_SOC_DAIFMT_FORMAT_MASK);
ret = -EINVAL;
goto end;
}
ret = idt821034_set_codec_conf(idt821034, conf);
end:
mutex_unlock(&idt821034->mutex);
return ret;
}
static int idt821034_dai_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct idt821034 *idt821034 = snd_soc_component_get_drvdata(dai->component);
u8 conf;
int ret;
mutex_lock(&idt821034->mutex);
conf = idt821034_get_codec_conf(idt821034);
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_A_LAW:
conf |= IDT821034_CONF_ALAW_MODE;
break;
case SNDRV_PCM_FORMAT_MU_LAW:
conf &= ~IDT821034_CONF_ALAW_MODE;
break;
default:
dev_err(dai->dev, "Unsupported PCM format 0x%x\n",
params_format(params));
ret = -EINVAL;
goto end;
}
ret = idt821034_set_codec_conf(idt821034, conf);
end:
mutex_unlock(&idt821034->mutex);
return ret;
}
static const unsigned int idt821034_sample_bits[] = {8};
static struct snd_pcm_hw_constraint_list idt821034_sample_bits_constr = {
.list = idt821034_sample_bits,
.count = ARRAY_SIZE(idt821034_sample_bits),
};
static int idt821034_dai_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct idt821034 *idt821034 = snd_soc_component_get_drvdata(dai->component);
unsigned int max_ch = 0;
int ret;
max_ch = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
idt821034->max_ch_playback : idt821034->max_ch_capture;
ret = snd_pcm_hw_constraint_minmax(substream->runtime, SNDRV_PCM_HW_PARAM_CHANNELS,
max_ch ? 1 : 0, max_ch);
if (ret < 0)
return ret;
ret = snd_pcm_hw_constraint_list(substream->runtime, 0, SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
&idt821034_sample_bits_constr);
if (ret)
return ret;
return 0;
}
static u64 idt821034_dai_formats[] = {
SND_SOC_POSSIBLE_DAIFMT_DSP_A |
SND_SOC_POSSIBLE_DAIFMT_DSP_B,
};
static const struct snd_soc_dai_ops idt821034_dai_ops = {
.startup = idt821034_dai_startup,
.hw_params = idt821034_dai_hw_params,
.set_tdm_slot = idt821034_dai_set_tdm_slot,
.set_fmt = idt821034_dai_set_fmt,
.auto_selectable_formats = idt821034_dai_formats,
.num_auto_selectable_formats = ARRAY_SIZE(idt821034_dai_formats),
};
static struct snd_soc_dai_driver idt821034_dai_driver = {
.name = "idt821034",
.playback = {
.stream_name = "Playback",
.channels_min = 1,
.channels_max = IDT821034_NB_CHANNEL,
.rates = SNDRV_PCM_RATE_8000,
.formats = SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW,
},
.capture = {
.stream_name = "Capture",
.channels_min = 1,
.channels_max = IDT821034_NB_CHANNEL,
.rates = SNDRV_PCM_RATE_8000,
.formats = SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW,
},
.ops = &idt821034_dai_ops,
};
static int idt821034_reset_audio(struct idt821034 *idt821034)
{
int ret;
u8 i;
mutex_lock(&idt821034->mutex);
ret = idt821034_set_codec_conf(idt821034, 0);
if (ret)
goto end;
for (i = 0; i < IDT821034_NB_CHANNEL; i++) {
idt821034->amps.ch[i].amp_out.gain = IDT821034_GAIN_OUT_INIT_RAW;
idt821034->amps.ch[i].amp_out.is_muted = false;
ret = idt821034_set_gain_channel(idt821034, i, IDT821034_GAIN_RX,
idt821034->amps.ch[i].amp_out.gain);
if (ret)
goto end;
idt821034->amps.ch[i].amp_in.gain = IDT821034_GAIN_IN_INIT_RAW;
idt821034->amps.ch[i].amp_in.is_muted = false;
ret = idt821034_set_gain_channel(idt821034, i, IDT821034_GAIN_TX,
idt821034->amps.ch[i].amp_in.gain);
if (ret)
goto end;
ret = idt821034_set_channel_power(idt821034, i, 0);
if (ret)
goto end;
}
ret = 0;
end:
mutex_unlock(&idt821034->mutex);
return ret;
}
static int idt821034_component_probe(struct snd_soc_component *component)
{
struct idt821034 *idt821034 = snd_soc_component_get_drvdata(component);
int ret;
ret = idt821034_reset_audio(idt821034);
if (ret)
return ret;
return 0;
}
static const struct snd_soc_component_driver idt821034_component_driver = {
.probe = idt821034_component_probe,
.controls = idt821034_controls,
.num_controls = ARRAY_SIZE(idt821034_controls),
.dapm_widgets = idt821034_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(idt821034_dapm_widgets),
.dapm_routes = idt821034_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(idt821034_dapm_routes),
.endianness = 1,
};
#define IDT821034_GPIO_OFFSET_TO_SLIC_CHANNEL(_offset) (((_offset) / 5) % 4)
#define IDT821034_GPIO_OFFSET_TO_SLIC_MASK(_offset) BIT((_offset) % 5)
static void idt821034_chip_gpio_set(struct gpio_chip *c, unsigned int offset, int val)
{
u8 ch = IDT821034_GPIO_OFFSET_TO_SLIC_CHANNEL(offset);
u8 mask = IDT821034_GPIO_OFFSET_TO_SLIC_MASK(offset);
struct idt821034 *idt821034 = gpiochip_get_data(c);
u8 slic_raw;
int ret;
mutex_lock(&idt821034->mutex);
slic_raw = idt821034_get_written_slic_raw(idt821034, ch);
if (val)
slic_raw |= mask;
else
slic_raw &= ~mask;
ret = idt821034_write_slic_raw(idt821034, ch, slic_raw);
if (ret) {
dev_err(&idt821034->spi->dev, "set gpio %d (%u, 0x%x) failed (%d)\n",
offset, ch, mask, ret);
}
mutex_unlock(&idt821034->mutex);
}
static int idt821034_chip_gpio_get(struct gpio_chip *c, unsigned int offset)
{
u8 ch = IDT821034_GPIO_OFFSET_TO_SLIC_CHANNEL(offset);
u8 mask = IDT821034_GPIO_OFFSET_TO_SLIC_MASK(offset);
struct idt821034 *idt821034 = gpiochip_get_data(c);
u8 slic_raw;
int ret;
mutex_lock(&idt821034->mutex);
ret = idt821034_read_slic_raw(idt821034, ch, &slic_raw);
mutex_unlock(&idt821034->mutex);
if (ret) {
dev_err(&idt821034->spi->dev, "get gpio %d (%u, 0x%x) failed (%d)\n",
offset, ch, mask, ret);
return ret;
}
return !!(bitrev8(slic_raw) & mask);
}
static int idt821034_chip_get_direction(struct gpio_chip *c, unsigned int offset)
{
u8 ch = IDT821034_GPIO_OFFSET_TO_SLIC_CHANNEL(offset);
u8 mask = IDT821034_GPIO_OFFSET_TO_SLIC_MASK(offset);
struct idt821034 *idt821034 = gpiochip_get_data(c);
u8 slic_dir;
mutex_lock(&idt821034->mutex);
slic_dir = idt821034_get_slic_conf(idt821034, ch);
mutex_unlock(&idt821034->mutex);
return slic_dir & mask ? GPIO_LINE_DIRECTION_IN : GPIO_LINE_DIRECTION_OUT;
}
static int idt821034_chip_direction_input(struct gpio_chip *c, unsigned int offset)
{
u8 ch = IDT821034_GPIO_OFFSET_TO_SLIC_CHANNEL(offset);
u8 mask = IDT821034_GPIO_OFFSET_TO_SLIC_MASK(offset);
struct idt821034 *idt821034 = gpiochip_get_data(c);
u8 slic_conf;
int ret;
if (mask & ~(IDT821034_SLIC_IO1_IN | IDT821034_SLIC_IO0_IN))
return -EPERM;
mutex_lock(&idt821034->mutex);
slic_conf = idt821034_get_slic_conf(idt821034, ch) | mask;
ret = idt821034_set_slic_conf(idt821034, ch, slic_conf);
if (ret) {
dev_err(&idt821034->spi->dev, "dir in gpio %d (%u, 0x%x) failed (%d)\n",
offset, ch, mask, ret);
}
mutex_unlock(&idt821034->mutex);
return ret;
}
static int idt821034_chip_direction_output(struct gpio_chip *c, unsigned int offset, int val)
{
u8 ch = IDT821034_GPIO_OFFSET_TO_SLIC_CHANNEL(offset);
u8 mask = IDT821034_GPIO_OFFSET_TO_SLIC_MASK(offset);
struct idt821034 *idt821034 = gpiochip_get_data(c);
u8 slic_conf;
int ret;
idt821034_chip_gpio_set(c, offset, val);
mutex_lock(&idt821034->mutex);
slic_conf = idt821034_get_slic_conf(idt821034, ch) & ~mask;
ret = idt821034_set_slic_conf(idt821034, ch, slic_conf);
if (ret) {
dev_err(&idt821034->spi->dev, "dir in gpio %d (%u, 0x%x) failed (%d)\n",
offset, ch, mask, ret);
}
mutex_unlock(&idt821034->mutex);
return ret;
}
static int idt821034_reset_gpio(struct idt821034 *idt821034)
{
int ret;
u8 i;
mutex_lock(&idt821034->mutex);
for (i = 0; i < IDT821034_NB_CHANNEL; i++) {
ret = idt821034_set_slic_conf(idt821034, i,
IDT821034_SLIC_IO1_IN | IDT821034_SLIC_IO0_IN);
if (ret)
goto end;
ret = idt821034_write_slic_raw(idt821034, i, 0);
if (ret)
goto end;
}
ret = 0;
end:
mutex_unlock(&idt821034->mutex);
return ret;
}
static int idt821034_gpio_init(struct idt821034 *idt821034)
{
int ret;
ret = idt821034_reset_gpio(idt821034);
if (ret)
return ret;
idt821034->gpio_chip.owner = THIS_MODULE;
idt821034->gpio_chip.label = dev_name(&idt821034->spi->dev);
idt821034->gpio_chip.parent = &idt821034->spi->dev;
idt821034->gpio_chip.base = -1;
idt821034->gpio_chip.ngpio = 5 * 4;
idt821034->gpio_chip.get_direction = idt821034_chip_get_direction;
idt821034->gpio_chip.direction_input = idt821034_chip_direction_input;
idt821034->gpio_chip.direction_output = idt821034_chip_direction_output;
idt821034->gpio_chip.get = idt821034_chip_gpio_get;
idt821034->gpio_chip.set = idt821034_chip_gpio_set;
idt821034->gpio_chip.can_sleep = true;
return devm_gpiochip_add_data(&idt821034->spi->dev, &idt821034->gpio_chip,
idt821034);
}
static int idt821034_spi_probe(struct spi_device *spi)
{
struct idt821034 *idt821034;
int ret;
spi->bits_per_word = 8;
ret = spi_setup(spi);
if (ret < 0)
return ret;
idt821034 = devm_kzalloc(&spi->dev, sizeof(*idt821034), GFP_KERNEL);
if (!idt821034)
return -ENOMEM;
idt821034->spi = spi;
mutex_init(&idt821034->mutex);
spi_set_drvdata(spi, idt821034);
ret = devm_snd_soc_register_component(&spi->dev, &idt821034_component_driver,
&idt821034_dai_driver, 1);
if (ret)
return ret;
if (IS_ENABLED(CONFIG_GPIOLIB))
return idt821034_gpio_init(idt821034);
return 0;
}
static const struct of_device_id idt821034_of_match[] = {
{ .compatible = "renesas,idt821034", },
{ }
};
MODULE_DEVICE_TABLE(of, idt821034_of_match);
static const struct spi_device_id idt821034_id_table[] = {
{ "idt821034", 0 },
{ }
};
MODULE_DEVICE_TABLE(spi, idt821034_id_table);
static struct spi_driver idt821034_spi_driver = {
.driver = {
.name = "idt821034",
.of_match_table = idt821034_of_match,
},
.id_table = idt821034_id_table,
.probe = idt821034_spi_probe,
};
module_spi_driver(idt821034_spi_driver);
MODULE_AUTHOR("Herve Codina <herve.codina@bootlin.com>");
MODULE_DESCRIPTION("IDT821034 ALSA SoC driver");
MODULE_LICENSE("GPL"