| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * ak4619.c -- Asahi Kasei ALSA SoC Audio driver |
| * |
| * Copyright (C) 2023 Renesas Electronics Corporation |
| * Khanh Le <khanh.le.xr@renesas.com> |
| * |
| * Based on ak4613.c by Kuninori Morimoto |
| * Based on da7213.c by Adam Thomson |
| * Based on ak4641.c by Harald Welte |
| */ |
| |
| #include <linux/clk.h> |
| #include <linux/gpio/consumer.h> |
| #include <linux/i2c.h> |
| #include <linux/slab.h> |
| #include <linux/of_device.h> |
| #include <linux/module.h> |
| #include <linux/regmap.h> |
| #include <sound/soc.h> |
| #include <sound/pcm.h> |
| #include <sound/pcm_params.h> |
| #include <sound/tlv.h> |
| |
| /* |
| * Registers |
| */ |
| |
| #define PWR_MGMT 0x00 /* Power Management */ |
| #define AU_IFF1 0x01 /* Audio I/F Format */ |
| #define AU_IFF2 0x02 /* Audio I/F Format (Extended) */ |
| #define SYS_CLK 0x03 /* System Clock Setting */ |
| #define MIC_AMP1 0x04 /* MIC AMP Gain 1 */ |
| #define MIC_AMP2 0x05 /* MIC AMP Gain 2 */ |
| #define LADC1 0x06 /* ADC1 Lch Digital Volume */ |
| #define RADC1 0x07 /* ADC1 Rch Digital Volume */ |
| #define LADC2 0x08 /* ADC2 Lch Digital Volume */ |
| #define RADC2 0x09 /* ADC2 Rch Digital Volume */ |
| #define ADC_DF 0x0a /* ADC Digital Filter Setting */ |
| #define ADC_AI 0x0b /* ADC Analog Input Setting */ |
| #define ADC_MHPF 0x0D /* ADC Mute & HPF Control */ |
| #define LDAC1 0x0E /* DAC1 Lch Digital Volume */ |
| #define RDAC1 0x0F /* DAC1 Rch Digital Volume */ |
| #define LDAC2 0x10 /* DAC2 Lch Digital Volume */ |
| #define RDAC2 0x11 /* DAC2 Rch Digital Volume */ |
| #define DAC_IS 0x12 /* DAC Input Select Setting */ |
| #define DAC_DEMP 0x13 /* DAC De-Emphasis Setting */ |
| #define DAC_MF 0x14 /* DAC Mute & Filter Setting */ |
| |
| /* |
| * Bit fields |
| */ |
| |
| /* Power Management */ |
| #define PMAD2 BIT(5) |
| #define PMAD1 BIT(4) |
| #define PMDA2 BIT(2) |
| #define PMDA1 BIT(1) |
| #define RSTN BIT(0) |
| |
| /* Audio_I/F Format */ |
| #define DCF_STEREO_I2S (0x0 << 4) |
| #define DCF_STEREO_MSB (0x5 << 4) |
| #define DCF_PCM_SF (0x6 << 4) |
| #define DCF_PCM_LF (0x7 << 4) |
| #define DSL_32 (0x3 << 2) |
| #define DCF_MASK (0x7 << 4) |
| #define DSL_MASK (0x3 << 2) |
| #define BCKP BIT(1) |
| |
| /* Audio_I/F Format (Extended) */ |
| #define DIDL_24 (0x0 << 2) |
| #define DIDL_20 (0x1 << 2) |
| #define DIDL_16 (0x2 << 2) |
| #define DIDL_32 (0x3 << 2) |
| #define DODL_24 (0x0 << 0) |
| #define DODL_20 (0x1 << 0) |
| #define DODL_16 (0x2 << 0) |
| #define DIDL_MASK (0x3 << 2) |
| #define DODL_MASK (0x3 << 0) |
| #define SLOT BIT(4) |
| |
| /* System Clock Setting */ |
| #define FS_MASK 0x7 |
| |
| /* MIC AMP Gain */ |
| #define MGNL_SHIFT 4 |
| #define MGNR_SHIFT 0 |
| #define MGN_MAX 0xB |
| |
| /* ADC Digital Volume */ |
| #define VOLAD_SHIFT 0 |
| #define VOLAD_MAX 0xFF |
| |
| /* ADC Digital Filter Setting */ |
| #define AD1SL_SHIFT 0 |
| #define AD2SL_SHIFT 4 |
| |
| /* Analog Input Select */ |
| #define AD1LSEL_SHIFT 6 |
| #define AD1RSEL_SHIFT 4 |
| #define AD2LSEL_SHIFT 2 |
| #define AD2RSEL_SHIFT 0 |
| |
| /* ADC Mute & HPF Control */ |
| #define ATSPAD_SHIFT 7 |
| #define AD1MUTE_SHIFT 5 |
| #define AD2MUTE_SHIFT 6 |
| #define AD1MUTE_MAX 1 |
| #define AD2MUTE_MAX 1 |
| #define AD1MUTE_EN BIT(5) |
| #define AD2MUTE_EN BIT(6) |
| #define AD1HPFN_SHIFT 1 |
| #define AD1HPFN_MAX 1 |
| #define AD2HPFN_SHIFT 2 |
| #define AD2HPFN_MAX 1 |
| |
| /* DAC Digital Volume */ |
| #define VOLDA_SHIFT 0 |
| #define VOLDA_MAX 0xFF |
| |
| /* DAC Input Select Setting */ |
| #define DAC1SEL_SHIFT 0 |
| #define DAC2SEL_SHIFT 2 |
| |
| /* DAC De-Emphasis Setting */ |
| #define DEM1_32000 (0x3 << 0) |
| #define DEM1_44100 (0x0 << 0) |
| #define DEM1_48000 (0x2 << 0) |
| #define DEM1_OFF (0x1 << 0) |
| #define DEM2_32000 (0x3 << 2) |
| #define DEM2_44100 (0x0 << 2) |
| #define DEM2_48000 (0x2 << 2) |
| #define DEM2_OFF (0x1 << 2) |
| #define DEM1_MASK (0x3 << 0) |
| #define DEM2_MASK (0x3 << 2) |
| #define DEM1_SHIFT 0 |
| #define DEM2_SHIFT 2 |
| |
| /* DAC Mute & Filter Setting */ |
| #define DA1MUTE_SHIFT 4 |
| #define DA1MUTE_MAX 1 |
| #define DA2MUTE_SHIFT 5 |
| #define DA2MUTE_MAX 1 |
| #define DA1MUTE_EN BIT(4) |
| #define DA2MUTE_EN BIT(5) |
| #define ATSPDA_SHIFT 7 |
| #define DA1SL_SHIFT 0 |
| #define DA2SL_SHIFT 2 |
| |
| /* Codec private data */ |
| struct ak4619_priv { |
| struct regmap *regmap; |
| struct snd_pcm_hw_constraint_list constraint; |
| int deemph_en; |
| unsigned int playback_rate; |
| unsigned int sysclk; |
| }; |
| |
| /* |
| * DAC Volume |
| * |
| * max : 0x00 : +12.0 dB |
| * ( 0.5 dB step ) |
| * min : 0xFE : -115.0 dB |
| * mute: 0xFF |
| */ |
| static const DECLARE_TLV_DB_SCALE(dac_tlv, -11550, 50, 1); |
| |
| /* |
| * MIC Volume |
| * |
| * max : 0x0B : +27.0 dB |
| * ( 3 dB step ) |
| * min: 0x00 : -6.0 dB |
| */ |
| static const DECLARE_TLV_DB_SCALE(mic_tlv, -600, 300, 0); |
| |
| /* |
| * ADC Volume |
| * |
| * max : 0x00 : +24.0 dB |
| * ( 0.5 dB step ) |
| * min : 0xFE : -103.0 dB |
| * mute: 0xFF |
| */ |
| static const DECLARE_TLV_DB_SCALE(adc_tlv, -10350, 50, 1); |
| |
| /* ADC & DAC Volume Level Transition Time select */ |
| static const char * const ak4619_vol_trans_txt[] = { |
| "4/fs", "16/fs" |
| }; |
| |
| static SOC_ENUM_SINGLE_DECL(ak4619_adc_vol_trans, ADC_MHPF, ATSPAD_SHIFT, ak4619_vol_trans_txt); |
| static SOC_ENUM_SINGLE_DECL(ak4619_dac_vol_trans, DAC_MF, ATSPDA_SHIFT, ak4619_vol_trans_txt); |
| |
| /* ADC Digital Filter select */ |
| static const char * const ak4619_adc_digi_fil_txt[] = { |
| "Sharp Roll-Off Filter", |
| "Slow Roll-Off Filter", |
| "Short Delay Sharp Roll-Off Filter", |
| "Short Delay Slow Roll-Off Filter", |
| "Voice Filter" |
| }; |
| |
| static SOC_ENUM_SINGLE_DECL(ak4619_adc_1_digi_fil, ADC_DF, AD1SL_SHIFT, ak4619_adc_digi_fil_txt); |
| static SOC_ENUM_SINGLE_DECL(ak4619_adc_2_digi_fil, ADC_DF, AD2SL_SHIFT, ak4619_adc_digi_fil_txt); |
| |
| /* DAC De-Emphasis Filter select */ |
| static const char * const ak4619_dac_de_emp_txt[] = { |
| "44.1kHz", "OFF", "48kHz", "32kHz" |
| }; |
| |
| static SOC_ENUM_SINGLE_DECL(ak4619_dac_1_de_emp, DAC_DEMP, DEM1_SHIFT, ak4619_dac_de_emp_txt); |
| static SOC_ENUM_SINGLE_DECL(ak4619_dac_2_de_emp, DAC_DEMP, DEM2_SHIFT, ak4619_dac_de_emp_txt); |
| |
| /* DAC Digital Filter select */ |
| static const char * const ak4619_dac_digi_fil_txt[] = { |
| "Sharp Roll-Off Filter", |
| "Slow Roll-Off Filter", |
| "Short Delay Sharp Roll-Off Filter", |
| "Short Delay Slow Roll-Off Filter" |
| }; |
| |
| static SOC_ENUM_SINGLE_DECL(ak4619_dac_1_digi_fil, DAC_MF, DA1SL_SHIFT, ak4619_dac_digi_fil_txt); |
| static SOC_ENUM_SINGLE_DECL(ak4619_dac_2_digi_fil, DAC_MF, DA2SL_SHIFT, ak4619_dac_digi_fil_txt); |
| |
| /* |
| * Control functions |
| */ |
| |
| static void ak4619_set_deemph(struct snd_soc_component *component) |
| { |
| struct ak4619_priv *ak4619 = snd_soc_component_get_drvdata(component); |
| u8 dem = 0; |
| |
| if (!ak4619->deemph_en) |
| return; |
| |
| switch (ak4619->playback_rate) { |
| case 32000: |
| dem |= DEM1_32000 | DEM2_32000; |
| break; |
| case 44100: |
| dem |= DEM1_44100 | DEM2_44100; |
| break; |
| case 48000: |
| dem |= DEM1_48000 | DEM2_48000; |
| break; |
| default: |
| dem |= DEM1_OFF | DEM2_OFF; |
| break; |
| } |
| snd_soc_component_update_bits(component, DAC_DEMP, DEM1_MASK | DEM2_MASK, dem); |
| } |
| |
| static int ak4619_put_deemph(struct snd_kcontrol *kcontrol, |
| struct snd_ctl_elem_value *ucontrol) |
| { |
| struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); |
| struct ak4619_priv *ak4619 = snd_soc_component_get_drvdata(component); |
| int deemph_en = ucontrol->value.integer.value[0]; |
| int ret = 0; |
| |
| switch (deemph_en) { |
| case 0: |
| case 1: |
| break; |
| default: |
| return -EINVAL; |
| } |
| |
| if (ak4619->deemph_en != deemph_en) |
| ret = 1; /* The value changed */ |
| |
| ak4619->deemph_en = deemph_en; |
| ak4619_set_deemph(component); |
| |
| return ret; |
| } |
| |
| static int ak4619_get_deemph(struct snd_kcontrol *kcontrol, |
| struct snd_ctl_elem_value *ucontrol) |
| { |
| struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); |
| struct ak4619_priv *ak4619 = snd_soc_component_get_drvdata(component); |
| |
| ucontrol->value.integer.value[0] = ak4619->deemph_en; |
| |
| return 0; |
| }; |
| |
| /* |
| * KControls |
| */ |
| static const struct snd_kcontrol_new ak4619_snd_controls[] = { |
| |
| /* Volume controls */ |
| SOC_DOUBLE_R_TLV("DAC 1 Volume", LDAC1, RDAC1, VOLDA_SHIFT, VOLDA_MAX, 1, dac_tlv), |
| SOC_DOUBLE_R_TLV("DAC 2 Volume", LDAC2, RDAC2, VOLDA_SHIFT, VOLDA_MAX, 1, dac_tlv), |
| SOC_DOUBLE_R_TLV("ADC 1 Volume", LADC1, RADC1, VOLAD_SHIFT, VOLAD_MAX, 1, adc_tlv), |
| SOC_DOUBLE_R_TLV("ADC 2 Volume", LADC2, RADC2, VOLAD_SHIFT, VOLAD_MAX, 1, adc_tlv), |
| |
| SOC_DOUBLE_TLV("Mic 1 Volume", MIC_AMP1, MGNL_SHIFT, MGNR_SHIFT, MGN_MAX, 0, mic_tlv), |
| SOC_DOUBLE_TLV("Mic 2 Volume", MIC_AMP2, MGNL_SHIFT, MGNR_SHIFT, MGN_MAX, 0, mic_tlv), |
| |
| /* Volume Level Transition Time controls */ |
| SOC_ENUM("ADC Volume Level Transition Time", ak4619_adc_vol_trans), |
| SOC_ENUM("DAC Volume Level Transition Time", ak4619_dac_vol_trans), |
| |
| /* Mute controls */ |
| SOC_SINGLE("DAC 1 Switch", DAC_MF, DA1MUTE_SHIFT, DA1MUTE_MAX, 1), |
| SOC_SINGLE("DAC 2 Switch", DAC_MF, DA2MUTE_SHIFT, DA2MUTE_MAX, 1), |
| |
| SOC_SINGLE("ADC 1 Switch", ADC_MHPF, AD1MUTE_SHIFT, AD1MUTE_MAX, 1), |
| SOC_SINGLE("ADC 2 Switch", ADC_MHPF, AD2MUTE_SHIFT, AD2MUTE_MAX, 1), |
| |
| /* Filter controls */ |
| SOC_ENUM("ADC 1 Digital Filter", ak4619_adc_1_digi_fil), |
| SOC_ENUM("ADC 2 Digital Filter", ak4619_adc_2_digi_fil), |
| |
| SOC_SINGLE("ADC 1 HPF", ADC_MHPF, AD1HPFN_SHIFT, AD1HPFN_MAX, 1), |
| SOC_SINGLE("ADC 2 HPF", ADC_MHPF, AD2HPFN_SHIFT, AD2HPFN_MAX, 1), |
| |
| SOC_ENUM("DAC 1 De-Emphasis Filter", ak4619_dac_1_de_emp), |
| SOC_ENUM("DAC 2 De-Emphasis Filter", ak4619_dac_2_de_emp), |
| |
| SOC_ENUM("DAC 1 Digital Filter", ak4619_dac_1_digi_fil), |
| SOC_ENUM("DAC 2 Digital Filter", ak4619_dac_2_digi_fil), |
| |
| SOC_SINGLE_BOOL_EXT("Playback De-Emphasis Switch", 0, ak4619_get_deemph, ak4619_put_deemph), |
| }; |
| |
| /* |
| * DAPM |
| */ |
| |
| /* Analog input mode */ |
| static const char * const ak4619_analog_in_txt[] = { |
| "Differential", "Single-Ended1", "Single-Ended2", "Pseudo Differential" |
| }; |
| |
| static SOC_ENUM_SINGLE_DECL(ak4619_ad_1_left_in, ADC_AI, AD1LSEL_SHIFT, ak4619_analog_in_txt); |
| static SOC_ENUM_SINGLE_DECL(ak4619_ad_1_right_in, ADC_AI, AD1RSEL_SHIFT, ak4619_analog_in_txt); |
| static SOC_ENUM_SINGLE_DECL(ak4619_ad_2_left_in, ADC_AI, AD2LSEL_SHIFT, ak4619_analog_in_txt); |
| static SOC_ENUM_SINGLE_DECL(ak4619_ad_2_right_in, ADC_AI, AD2RSEL_SHIFT, ak4619_analog_in_txt); |
| |
| static const struct snd_kcontrol_new ak4619_ad_1_left_in_mux = |
| SOC_DAPM_ENUM("Analog Input 1 Left MUX", ak4619_ad_1_left_in); |
| static const struct snd_kcontrol_new ak4619_ad_1_right_in_mux = |
| SOC_DAPM_ENUM("Analog Input 1 Right MUX", ak4619_ad_1_right_in); |
| static const struct snd_kcontrol_new ak4619_ad_2_left_in_mux = |
| SOC_DAPM_ENUM("Analog Input 2 Left MUX", ak4619_ad_2_left_in); |
| static const struct snd_kcontrol_new ak4619_ad_2_right_in_mux = |
| SOC_DAPM_ENUM("Analog Input 2 Right MUX", ak4619_ad_2_right_in); |
| |
| /* DAC source mux */ |
| static const char * const ak4619_dac_in_txt[] = { |
| "SDIN1", "SDIN2", "SDOUT1", "SDOUT2" |
| }; |
| |
| static SOC_ENUM_SINGLE_DECL(ak4619_dac_1_in, DAC_IS, DAC1SEL_SHIFT, ak4619_dac_in_txt); |
| static SOC_ENUM_SINGLE_DECL(ak4619_dac_2_in, DAC_IS, DAC2SEL_SHIFT, ak4619_dac_in_txt); |
| |
| static const struct snd_kcontrol_new ak4619_dac_1_in_mux = |
| SOC_DAPM_ENUM("DAC 1 Source MUX", ak4619_dac_1_in); |
| static const struct snd_kcontrol_new ak4619_dac_2_in_mux = |
| SOC_DAPM_ENUM("DAC 2 Source MUX", ak4619_dac_2_in); |
| |
| static const struct snd_soc_dapm_widget ak4619_dapm_widgets[] = { |
| |
| /* DACs */ |
| SND_SOC_DAPM_DAC("DAC1", NULL, PWR_MGMT, 1, 0), |
| SND_SOC_DAPM_DAC("DAC2", NULL, PWR_MGMT, 2, 0), |
| |
| /* ADCs */ |
| SND_SOC_DAPM_ADC("ADC1", NULL, PWR_MGMT, 4, 0), |
| SND_SOC_DAPM_ADC("ADC2", NULL, PWR_MGMT, 5, 0), |
| |
| /* Outputs */ |
| SND_SOC_DAPM_OUTPUT("AOUT1L"), |
| SND_SOC_DAPM_OUTPUT("AOUT2L"), |
| |
| SND_SOC_DAPM_OUTPUT("AOUT1R"), |
| SND_SOC_DAPM_OUTPUT("AOUT2R"), |
| |
| /* Inputs */ |
| SND_SOC_DAPM_INPUT("AIN1L"), |
| SND_SOC_DAPM_INPUT("AIN2L"), |
| SND_SOC_DAPM_INPUT("AIN4L"), |
| SND_SOC_DAPM_INPUT("AIN5L"), |
| |
| SND_SOC_DAPM_INPUT("AIN1R"), |
| SND_SOC_DAPM_INPUT("AIN2R"), |
| SND_SOC_DAPM_INPUT("AIN4R"), |
| SND_SOC_DAPM_INPUT("AIN5R"), |
| |
| SND_SOC_DAPM_INPUT("MIC1L"), |
| SND_SOC_DAPM_INPUT("MIC1R"), |
| SND_SOC_DAPM_INPUT("MIC2L"), |
| SND_SOC_DAPM_INPUT("MIC2R"), |
| |
| /* DAI */ |
| SND_SOC_DAPM_AIF_IN("SDIN1", "Playback", 0, SND_SOC_NOPM, 0, 0), |
| SND_SOC_DAPM_AIF_IN("SDIN2", "Playback", 0, SND_SOC_NOPM, 0, 0), |
| SND_SOC_DAPM_AIF_OUT("SDOUT1", "Capture", 0, SND_SOC_NOPM, 0, 0), |
| SND_SOC_DAPM_AIF_OUT("SDOUT2", "Capture", 0, SND_SOC_NOPM, 0, 0), |
| |
| /* MUXs for Mic PGA source selection */ |
| SND_SOC_DAPM_MUX("Analog Input 1 Left MUX", SND_SOC_NOPM, 0, 0, &ak4619_ad_1_left_in_mux), |
| SND_SOC_DAPM_MUX("Analog Input 1 Right MUX", SND_SOC_NOPM, 0, 0, &ak4619_ad_1_right_in_mux), |
| SND_SOC_DAPM_MUX("Analog Input 2 Left MUX", SND_SOC_NOPM, 0, 0, &ak4619_ad_2_left_in_mux), |
| SND_SOC_DAPM_MUX("Analog Input 2 Right MUX", SND_SOC_NOPM, 0, 0, &ak4619_ad_2_right_in_mux), |
| |
| /* MUXs for DAC source selection */ |
| SND_SOC_DAPM_MUX("DAC 1 Source MUX", SND_SOC_NOPM, 0, 0, &ak4619_dac_1_in_mux), |
| SND_SOC_DAPM_MUX("DAC 2 Source MUX", SND_SOC_NOPM, 0, 0, &ak4619_dac_2_in_mux), |
| }; |
| |
| static const struct snd_soc_dapm_route ak4619_intercon[] = { |
| /* Dest Connecting Widget Source */ |
| |
| /* Output path */ |
| {"AOUT1L", NULL, "DAC1"}, |
| {"AOUT2L", NULL, "DAC2"}, |
| |
| {"AOUT1R", NULL, "DAC1"}, |
| {"AOUT2R", NULL, "DAC2"}, |
| |
| {"DAC1", NULL, "DAC 1 Source MUX"}, |
| {"DAC2", NULL, "DAC 2 Source MUX"}, |
| |
| {"DAC 1 Source MUX", "SDIN1", "SDIN1"}, |
| {"DAC 1 Source MUX", "SDIN2", "SDIN2"}, |
| {"DAC 1 Source MUX", "SDOUT1", "SDOUT1"}, |
| {"DAC 1 Source MUX", "SDOUT2", "SDOUT2"}, |
| |
| {"DAC 2 Source MUX", "SDIN1", "SDIN1"}, |
| {"DAC 2 Source MUX", "SDIN2", "SDIN2"}, |
| {"DAC 2 Source MUX", "SDOUT1", "SDOUT1"}, |
| {"DAC 2 Source MUX", "SDOUT2", "SDOUT2"}, |
| |
| /* Input path */ |
| {"SDOUT1", NULL, "ADC1"}, |
| {"SDOUT2", NULL, "ADC2"}, |
| |
| {"ADC1", NULL, "Analog Input 1 Left MUX"}, |
| {"ADC1", NULL, "Analog Input 1 Right MUX"}, |
| |
| {"ADC2", NULL, "Analog Input 2 Left MUX"}, |
| {"ADC2", NULL, "Analog Input 2 Right MUX"}, |
| |
| {"Analog Input 1 Left MUX", "Differential", "MIC1L"}, |
| {"Analog Input 1 Left MUX", "Single-Ended1", "MIC1L"}, |
| {"Analog Input 1 Left MUX", "Single-Ended2", "MIC1L"}, |
| {"Analog Input 1 Left MUX", "Pseudo Differential", "MIC1L"}, |
| |
| {"Analog Input 1 Right MUX", "Differential", "MIC1R"}, |
| {"Analog Input 1 Right MUX", "Single-Ended1", "MIC1R"}, |
| {"Analog Input 1 Right MUX", "Single-Ended2", "MIC1R"}, |
| {"Analog Input 1 Right MUX", "Pseudo Differential", "MIC1R"}, |
| |
| {"Analog Input 2 Left MUX", "Differential", "MIC2L"}, |
| {"Analog Input 2 Left MUX", "Single-Ended1", "MIC2L"}, |
| {"Analog Input 2 Left MUX", "Single-Ended2", "MIC2L"}, |
| {"Analog Input 2 Left MUX", "Pseudo Differential", "MIC2L"}, |
| |
| {"Analog Input 2 Right MUX", "Differential", "MIC2R"}, |
| {"Analog Input 2 Right MUX", "Single-Ended1", "MIC2R"}, |
| {"Analog Input 2 Right MUX", "Single-Ended2", "MIC2R"}, |
| {"Analog Input 2 Right MUX", "Pseudo Differential", "MIC2R"}, |
| |
| {"MIC1L", NULL, "AIN1L"}, |
| {"MIC1L", NULL, "AIN2L"}, |
| |
| {"MIC1R", NULL, "AIN1R"}, |
| {"MIC1R", NULL, "AIN2R"}, |
| |
| {"MIC2L", NULL, "AIN4L"}, |
| {"MIC2L", NULL, "AIN5L"}, |
| |
| {"MIC2R", NULL, "AIN4R"}, |
| {"MIC2R", NULL, "AIN5R"}, |
| }; |
| |
| static const struct reg_default ak4619_reg_defaults[] = { |
| { PWR_MGMT, 0x00 }, |
| { AU_IFF1, 0x0C }, |
| { AU_IFF2, 0x0C }, |
| { SYS_CLK, 0x00 }, |
| { MIC_AMP1, 0x22 }, |
| { MIC_AMP2, 0x22 }, |
| { LADC1, 0x30 }, |
| { RADC1, 0x30 }, |
| { LADC2, 0x30 }, |
| { RADC2, 0x30 }, |
| { ADC_DF, 0x00 }, |
| { ADC_AI, 0x00 }, |
| { ADC_MHPF, 0x00 }, |
| { LDAC1, 0x18 }, |
| { RDAC1, 0x18 }, |
| { LDAC2, 0x18 }, |
| { RDAC2, 0x18 }, |
| { DAC_IS, 0x04 }, |
| { DAC_DEMP, 0x05 }, |
| { DAC_MF, 0x0A }, |
| }; |
| |
| static int ak4619_set_bias_level(struct snd_soc_component *component, |
| enum snd_soc_bias_level level) |
| { |
| u8 pwr_ctrl = 0; |
| |
| switch (level) { |
| case SND_SOC_BIAS_ON: |
| pwr_ctrl |= RSTN; |
| fallthrough; |
| case SND_SOC_BIAS_PREPARE: |
| pwr_ctrl |= PMAD1 | PMAD2 | PMDA1 | PMDA2; |
| fallthrough; |
| case SND_SOC_BIAS_STANDBY: |
| case SND_SOC_BIAS_OFF: |
| default: |
| break; |
| } |
| |
| snd_soc_component_write(component, PWR_MGMT, pwr_ctrl); |
| |
| return 0; |
| } |
| |
| static int ak4619_dai_hw_params(struct snd_pcm_substream *substream, |
| struct snd_pcm_hw_params *params, |
| struct snd_soc_dai *dai) |
| { |
| struct snd_soc_component *component = dai->component; |
| struct ak4619_priv *ak4619 = snd_soc_component_get_drvdata(component); |
| unsigned int width; |
| unsigned int rate; |
| unsigned int fs; |
| bool is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; |
| u8 dai_ctrl = 0; |
| u8 clk_mode = 0; |
| |
| width = params_width(params); |
| switch (width) { |
| case 16: |
| dai_ctrl |= is_play ? DIDL_16 : DODL_16; |
| break; |
| case 20: |
| dai_ctrl |= is_play ? DIDL_20 : DODL_20; |
| break; |
| case 24: |
| dai_ctrl |= is_play ? DIDL_24 : DODL_24; |
| break; |
| case 32: |
| if (is_play) |
| dai_ctrl |= DIDL_32; |
| else |
| return -EINVAL; |
| break; |
| default: |
| return -EINVAL; |
| } |
| |
| rate = params_rate(params); |
| if (rate) |
| fs = ak4619->sysclk / rate; |
| else |
| return -EINVAL; |
| |
| switch (rate) { |
| case 8000: |
| case 11025: |
| case 12000: |
| case 16000: |
| case 22050: |
| case 24000: |
| case 32000: |
| case 44100: |
| case 48000: |
| switch (fs) { |
| case 256: |
| clk_mode |= (0x0 << 0); |
| break; |
| case 384: |
| clk_mode |= (0x2 << 0); |
| break; |
| case 512: |
| clk_mode |= (0x3 << 0); |
| break; |
| default: |
| return -EINVAL; |
| } |
| break; |
| case 64000: |
| case 88200: |
| case 96000: |
| if (fs == 256) |
| clk_mode |= (0x1 << 0); |
| else |
| return -EINVAL; |
| break; |
| case 176400: |
| case 192000: |
| if (fs == 128) |
| clk_mode |= (0x4 << 0); |
| else |
| return -EINVAL; |
| break; |
| default: |
| return -EINVAL; |
| } |
| |
| snd_soc_component_update_bits(component, SYS_CLK, FS_MASK, clk_mode); |
| snd_soc_component_update_bits(component, AU_IFF2, |
| is_play ? DIDL_MASK : DODL_MASK, dai_ctrl); |
| |
| if (is_play) { |
| ak4619->playback_rate = rate; |
| ak4619_set_deemph(component); |
| } |
| |
| return 0; |
| } |
| |
| static int ak4619_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) |
| { |
| struct snd_soc_component *component = dai->component; |
| u8 dai_fmt1 = 0; |
| u8 dai_fmt2 = 0; |
| |
| /* Set clock normal/inverted */ |
| switch (fmt & SND_SOC_DAIFMT_INV_MASK) { |
| case SND_SOC_DAIFMT_NB_NF: |
| break; |
| case SND_SOC_DAIFMT_IB_NF: |
| dai_fmt1 |= BCKP; |
| break; |
| case SND_SOC_DAIFMT_NB_IF: |
| case SND_SOC_DAIFMT_IB_IF: |
| default: |
| return -EINVAL; |
| } |
| |
| /* Only Stereo modes are supported */ |
| switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { |
| case SND_SOC_DAIFMT_I2S: |
| dai_fmt1 |= DCF_STEREO_I2S; |
| break; |
| case SND_SOC_DAIFMT_LEFT_J: |
| dai_fmt1 |= DCF_STEREO_MSB; |
| break; |
| case SND_SOC_DAIFMT_DSP_A: /* L data MSB after FRM LRC */ |
| dai_fmt1 |= DCF_PCM_SF; |
| dai_fmt2 |= SLOT; |
| break; |
| case SND_SOC_DAIFMT_DSP_B: /* L data MSB during FRM LRC */ |
| dai_fmt1 |= DCF_PCM_LF; |
| dai_fmt2 |= SLOT; |
| break; |
| default: |
| return -EINVAL; |
| } |
| |
| /* Only slave mode is support */ |
| switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { |
| case SND_SOC_DAIFMT_CBC_CFC: |
| break; |
| default: |
| return -EINVAL; |
| } |
| |
| /* By default only 64 BICK per LRCLK is supported */ |
| dai_fmt1 |= DSL_32; |
| |
| snd_soc_component_update_bits(component, AU_IFF1, DCF_MASK | |
| DSL_MASK | BCKP, dai_fmt1); |
| snd_soc_component_update_bits(component, AU_IFF2, SLOT, dai_fmt2); |
| |
| return 0; |
| } |
| |
| static int ak4619_dai_set_sysclk(struct snd_soc_dai *codec_dai, |
| int clk_id, unsigned int freq, int dir) |
| { |
| struct snd_soc_component *component = codec_dai->component; |
| struct ak4619_priv *ak4619 = snd_soc_component_get_drvdata(component); |
| |
| ak4619->sysclk = freq; |
| |
| return 0; |
| } |
| |
| static int ak4619_dai_mute(struct snd_soc_dai *dai, int mute, int direction) |
| { |
| struct snd_soc_component *component = dai->component; |
| |
| snd_soc_component_update_bits(component, DAC_MF, DA1MUTE_EN, mute ? DA1MUTE_EN : 0); |
| snd_soc_component_update_bits(component, DAC_MF, DA2MUTE_EN, mute ? DA2MUTE_EN : 0); |
| |
| return 0; |
| } |
| |
| static void ak4619_hw_constraints(struct ak4619_priv *ak4619, |
| struct snd_pcm_runtime *runtime) |
| { |
| struct snd_pcm_hw_constraint_list *constraint = &ak4619->constraint; |
| int ak4619_rate_mask = 0; |
| unsigned int fs; |
| int i; |
| static const unsigned int ak4619_sr[] = { |
| 8000, |
| 11025, |
| 12000, |
| 16000, |
| 22050, |
| 24000, |
| 32000, |
| 44100, |
| 48000, |
| 64000, |
| 88200, |
| 96000, |
| 176400, |
| 192000, |
| }; |
| |
| /* |
| * [8kHz - 48kHz] : 256fs, 384fs or 512fs |
| * [64kHz - 96kHz] : 256fs |
| * [176.4kHz, 192kHz] : 128fs |
| */ |
| |
| for (i = 0; i < ARRAY_SIZE(ak4619_sr); i++) { |
| fs = ak4619->sysclk / ak4619_sr[i]; |
| |
| switch (fs) { |
| case 512: |
| case 384: |
| case 256: |
| ak4619_rate_mask |= (1 << i); |
| break; |
| case 128: |
| switch (i) { |
| case (ARRAY_SIZE(ak4619_sr) - 1): |
| case (ARRAY_SIZE(ak4619_sr) - 2): |
| ak4619_rate_mask |= (1 << i); |
| break; |
| default: |
| break; |
| } |
| break; |
| default: |
| break; |
| } |
| } |
| |
| constraint->list = ak4619_sr; |
| constraint->mask = ak4619_rate_mask; |
| constraint->count = ARRAY_SIZE(ak4619_sr); |
| |
| snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, constraint); |
| }; |
| |
| #define PLAYBACK_MODE 0 |
| #define CAPTURE_MODE 1 |
| |
| static int ak4619_dai_startup(struct snd_pcm_substream *substream, |
| struct snd_soc_dai *dai) |
| { |
| struct snd_soc_component *component = dai->component; |
| struct ak4619_priv *ak4619 = snd_soc_component_get_drvdata(component); |
| |
| ak4619_hw_constraints(ak4619, substream->runtime); |
| |
| return 0; |
| } |
| |
| static u64 ak4619_dai_formats[] = { |
| /* |
| * Select below from Sound Card, not here |
| * SND_SOC_DAIFMT_CBC_CFC |
| * SND_SOC_DAIFMT_CBP_CFP |
| */ |
| |
| /* First Priority */ |
| SND_SOC_POSSIBLE_DAIFMT_I2S | |
| SND_SOC_POSSIBLE_DAIFMT_LEFT_J, |
| |
| /* Second Priority */ |
| SND_SOC_POSSIBLE_DAIFMT_DSP_A | |
| SND_SOC_POSSIBLE_DAIFMT_DSP_B, |
| }; |
| |
| static const struct snd_soc_dai_ops ak4619_dai_ops = { |
| .startup = ak4619_dai_startup, |
| .set_sysclk = ak4619_dai_set_sysclk, |
| .set_fmt = ak4619_dai_set_fmt, |
| .hw_params = ak4619_dai_hw_params, |
| .mute_stream = ak4619_dai_mute, |
| .auto_selectable_formats = ak4619_dai_formats, |
| .num_auto_selectable_formats = ARRAY_SIZE(ak4619_dai_formats), |
| }; |
| |
| static const struct snd_soc_component_driver soc_component_dev_ak4619 = { |
| .set_bias_level = ak4619_set_bias_level, |
| .controls = ak4619_snd_controls, |
| .num_controls = ARRAY_SIZE(ak4619_snd_controls), |
| .dapm_widgets = ak4619_dapm_widgets, |
| .num_dapm_widgets = ARRAY_SIZE(ak4619_dapm_widgets), |
| .dapm_routes = ak4619_intercon, |
| .num_dapm_routes = ARRAY_SIZE(ak4619_intercon), |
| .idle_bias_on = 1, |
| .endianness = 1, |
| }; |
| |
| static const struct regmap_config ak4619_regmap_cfg = { |
| .reg_bits = 8, |
| .val_bits = 8, |
| .max_register = 0x14, |
| .reg_defaults = ak4619_reg_defaults, |
| .num_reg_defaults = ARRAY_SIZE(ak4619_reg_defaults), |
| .cache_type = REGCACHE_MAPLE, |
| }; |
| |
| static const struct of_device_id ak4619_of_match[] = { |
| { .compatible = "asahi-kasei,ak4619", .data = &ak4619_regmap_cfg }, |
| {}, |
| }; |
| MODULE_DEVICE_TABLE(of, ak4619_of_match); |
| |
| static const struct i2c_device_id ak4619_i2c_id[] = { |
| { "ak4619", (kernel_ulong_t)&ak4619_regmap_cfg }, |
| { } |
| }; |
| MODULE_DEVICE_TABLE(i2c, ak4619_i2c_id); |
| |
| #define AK4619_RATES SNDRV_PCM_RATE_8000_192000 |
| |
| #define AK4619_DAC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ |
| SNDRV_PCM_FMTBIT_S20_LE |\ |
| SNDRV_PCM_FMTBIT_S24_LE |\ |
| SNDRV_PCM_FMTBIT_S32_LE) |
| |
| #define AK4619_ADC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ |
| SNDRV_PCM_FMTBIT_S20_LE |\ |
| SNDRV_PCM_FMTBIT_S24_LE) |
| |
| static struct snd_soc_dai_driver ak4619_dai = { |
| .name = "ak4619-hifi", |
| .playback = { |
| .stream_name = "Playback", |
| .channels_min = 1, |
| .channels_max = 2, |
| .rates = AK4619_RATES, |
| .formats = AK4619_DAC_FORMATS, |
| }, |
| .capture = { |
| .stream_name = "Capture", |
| .channels_min = 1, |
| .channels_max = 2, |
| .rates = AK4619_RATES, |
| .formats = AK4619_ADC_FORMATS, |
| }, |
| .ops = &ak4619_dai_ops, |
| .symmetric_rate = 1, |
| }; |
| |
| static int ak4619_i2c_probe(struct i2c_client *i2c) |
| { |
| struct device *dev = &i2c->dev; |
| struct ak4619_priv *ak4619; |
| int ret; |
| |
| ak4619 = devm_kzalloc(dev, sizeof(*ak4619), GFP_KERNEL); |
| if (!ak4619) |
| return -ENOMEM; |
| |
| i2c_set_clientdata(i2c, ak4619); |
| |
| ak4619->regmap = devm_regmap_init_i2c(i2c, &ak4619_regmap_cfg); |
| if (IS_ERR(ak4619->regmap)) { |
| ret = PTR_ERR(ak4619->regmap); |
| dev_err(dev, "regmap_init() failed: %d\n", ret); |
| return ret; |
| } |
| |
| ret = devm_snd_soc_register_component(dev, &soc_component_dev_ak4619, |
| &ak4619_dai, 1); |
| if (ret < 0) { |
| dev_err(dev, "Failed to register ak4619 component: %d\n", |
| ret); |
| return ret; |
| } |
| |
| return 0; |
| } |
| |
| static struct i2c_driver ak4619_i2c_driver = { |
| .driver = { |
| .name = "ak4619-codec", |
| .of_match_table = ak4619_of_match, |
| }, |
| .probe = ak4619_i2c_probe, |
| .id_table = ak4619_i2c_id, |
| }; |
| module_i2c_driver(ak4619_i2c_driver); |
| |
| MODULE_DESCRIPTION("SoC AK4619 driver"); |
| MODULE_AUTHOR("Khanh Le <khanh.le.xr@renesas.com>"); |
| MODULE_LICENSE("GPL v2"); |