Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
Herve Codina | 5381 | 99.98% | 1 | 50.00% |
Krzysztof Kozlowski | 1 | 0.02% | 1 | 50.00% |
Total | 5382 | 2 |
// SPDX-License-Identifier: GPL-2.0 // // IDT821034 ALSA SoC driver // // Copyright 2022 CS GROUP France // // Author: Herve Codina <herve.codina@bootlin.com> #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; /* Cannot use stack area for SPI (dma-safe memory) */ u8 spi_rx_buf; /* Cannot use stack area for SPI (dma-safe memory) */ 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; } /* Available mode for the programming sequence */ #define IDT821034_MODE_CODEC(_ch) (0x80 | ((_ch) << 2)) #define IDT821034_MODE_SLIC(_ch) (0xD0 | ((_ch) << 2)) #define IDT821034_MODE_GAIN(_ch) (0xC0 | ((_ch) << 2)) /* Power values that can be used in 'power' (can be ORed) */ #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; } /* Codec configuration values that can be used in 'codec_conf' (can be ORed) */ #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); /* codec conf fields are common to all channel. * Arbitrary use of channel 0 for this configuration. */ /* Set Configuration Register */ conf = IDT821034_MODE_CODEC(0) | codec_conf; /* Update conf value and timeslot register value according * to cache values */ 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; } /* Write configuration register and time-slot register */ 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; } /* Channel direction values that can be used in 'ch_dir' (can be ORed) */ #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; } /* SLIC direction values that can be used in 'slic_dir' (can be ORed) */ #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); /* * On write, slic_raw is mapped as follow : * b4: O_4 * b3: O_3 * b2: O_2 * b1: I/O_1 * b0: I/O_0 */ 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; /* * On read, slic_raw is mapped as follow : * b7: I/O_0 * b6: I/O_1 * b5: O_2 * b4: O_3 * b3: O_4 * b2: I/O1_0, I/O_0 from channel 1 (no matter ch value) * b1: I/O2_0, I/O_0 from channel 2 (no matter ch value) * b2: I/O3_0, I/O_0 from channel 3 (no matter ch value) */ 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; } /* Gain type values that can be used in 'gain_type' (cannot be ORed) */ #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); /* * The gain programming coefficients should be calculated as: * Transmit : Coeff_X = round [ gain_X0dB × gain_X ] * Receive: Coeff_R = round [ gain_R0dB × gain_R ] * where: * gain_X0dB = 1820; * gain_X is the target gain; * Coeff_X should be in the range of 0 to 8192. * gain_R0dB = 2506; * gain_R is the target gain; * Coeff_R should be in the range of 0 to 8192. * * A gain programming coefficient is 14-bit wide and in binary format. * The 7 Most Significant Bits of the coefficient is called * GA_MSB_Transmit for transmit path, or is called GA_MSB_Receive for * receive path; The 7 Least Significant Bits of the coefficient is * called GA_LSB_ Transmit for transmit path, or is called * GA_LSB_Receive for receive path. * * An example is given below to clarify the calculation of the * coefficient. To program a +3 dB gain in transmit path and a -3.5 dB * gain in receive path: * * Linear Code of +3dB = 10^(3/20)= 1.412537545 * Coeff_X = round (1820 × 1.412537545) = 2571 * = 0b001010_00001011 * GA_MSB_Transmit = 0b0010100 * GA_LSB_Transmit = 0b0001011 * * Linear Code of -3.5dB = 10^(-3.5/20) = 0.668343917 * Coeff_R= round (2506 × 0.668343917) = 1675 * = 0b0001101_0001011 * GA_MSB_Receive = 0b0001101 * GA_LSB_Receive = 0b0001011 */ 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; } /* Id helpers used in controls and dapm */ #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; /* The value changed */ 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; /* The value changed */ 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[] = { /* DAC volume control */ 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), /* DAC mute control */ 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), /* ADC volume control */ 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), /* ADC mute control */ 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: /* Not set -> default 8 */ 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; /* * Disable stream support (min = 0, max = 0) if no timeslots were * configured otherwise, limit the number of channels to those * configured. */ 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 const 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; /* reset idt821034 audio part*/ 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; } /* * SLIC IOs are read in reverse order compared to write. * Reverse the read value here in order to have IO0 at lsb (ie same * order as write) */ 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; /* Only IO0 and IO1 can be set as input */ 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); /* IO0 and IO1 as input for all channels and output IO set to 0 */ 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; /* 5 GPIOs on 4 channels */ 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");
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1