1e9d97b05SPaul Cercueil // SPDX-License-Identifier: GPL-2.0 2a50e3269SPaul Cercueil // 3a50e3269SPaul Cercueil // JZ4725B CODEC driver 4a50e3269SPaul Cercueil // 5a50e3269SPaul Cercueil // Copyright (C) 2019, Paul Cercueil <paul@crapouillou.net> 6e9d97b05SPaul Cercueil 7e9d97b05SPaul Cercueil #include <linux/kernel.h> 8e9d97b05SPaul Cercueil #include <linux/module.h> 9e9d97b05SPaul Cercueil #include <linux/platform_device.h> 10e9d97b05SPaul Cercueil #include <linux/slab.h> 11e9d97b05SPaul Cercueil #include <linux/io.h> 12e9d97b05SPaul Cercueil #include <linux/iopoll.h> 13e9d97b05SPaul Cercueil #include <linux/regmap.h> 14e9d97b05SPaul Cercueil #include <linux/clk.h> 15e9d97b05SPaul Cercueil 16e9d97b05SPaul Cercueil #include <linux/delay.h> 17e9d97b05SPaul Cercueil 18e9d97b05SPaul Cercueil #include <sound/core.h> 19e9d97b05SPaul Cercueil #include <sound/pcm.h> 20e9d97b05SPaul Cercueil #include <sound/pcm_params.h> 21e9d97b05SPaul Cercueil #include <sound/initval.h> 22e9d97b05SPaul Cercueil #include <sound/soc.h> 23e9d97b05SPaul Cercueil #include <sound/tlv.h> 24e9d97b05SPaul Cercueil 25e9d97b05SPaul Cercueil #define ICDC_RGADW_OFFSET 0x00 26e9d97b05SPaul Cercueil #define ICDC_RGDATA_OFFSET 0x04 27e9d97b05SPaul Cercueil 28e9d97b05SPaul Cercueil /* ICDC internal register access control register(RGADW) */ 29e9d97b05SPaul Cercueil #define ICDC_RGADW_RGWR BIT(16) 30e9d97b05SPaul Cercueil 31e9d97b05SPaul Cercueil #define ICDC_RGADW_RGADDR_OFFSET 8 32e9d97b05SPaul Cercueil #define ICDC_RGADW_RGADDR_MASK GENMASK(14, ICDC_RGADW_RGADDR_OFFSET) 33e9d97b05SPaul Cercueil 34e9d97b05SPaul Cercueil #define ICDC_RGADW_RGDIN_OFFSET 0 35e9d97b05SPaul Cercueil #define ICDC_RGADW_RGDIN_MASK GENMASK(7, ICDC_RGADW_RGDIN_OFFSET) 36e9d97b05SPaul Cercueil 37e9d97b05SPaul Cercueil /* ICDC internal register data output register (RGDATA)*/ 38e9d97b05SPaul Cercueil #define ICDC_RGDATA_IRQ BIT(8) 39e9d97b05SPaul Cercueil 40e9d97b05SPaul Cercueil #define ICDC_RGDATA_RGDOUT_OFFSET 0 41e9d97b05SPaul Cercueil #define ICDC_RGDATA_RGDOUT_MASK GENMASK(7, ICDC_RGDATA_RGDOUT_OFFSET) 42e9d97b05SPaul Cercueil 43e9d97b05SPaul Cercueil /* JZ internal register space */ 44e9d97b05SPaul Cercueil enum { 45e9d97b05SPaul Cercueil JZ4725B_CODEC_REG_AICR, 46e9d97b05SPaul Cercueil JZ4725B_CODEC_REG_CR1, 47e9d97b05SPaul Cercueil JZ4725B_CODEC_REG_CR2, 48e9d97b05SPaul Cercueil JZ4725B_CODEC_REG_CCR1, 49e9d97b05SPaul Cercueil JZ4725B_CODEC_REG_CCR2, 50e9d97b05SPaul Cercueil JZ4725B_CODEC_REG_PMR1, 51e9d97b05SPaul Cercueil JZ4725B_CODEC_REG_PMR2, 52e9d97b05SPaul Cercueil JZ4725B_CODEC_REG_CRR, 53e9d97b05SPaul Cercueil JZ4725B_CODEC_REG_ICR, 54e9d97b05SPaul Cercueil JZ4725B_CODEC_REG_IFR, 55e9d97b05SPaul Cercueil JZ4725B_CODEC_REG_CGR1, 56e9d97b05SPaul Cercueil JZ4725B_CODEC_REG_CGR2, 57e9d97b05SPaul Cercueil JZ4725B_CODEC_REG_CGR3, 58e9d97b05SPaul Cercueil JZ4725B_CODEC_REG_CGR4, 59e9d97b05SPaul Cercueil JZ4725B_CODEC_REG_CGR5, 60e9d97b05SPaul Cercueil JZ4725B_CODEC_REG_CGR6, 61e9d97b05SPaul Cercueil JZ4725B_CODEC_REG_CGR7, 62e9d97b05SPaul Cercueil JZ4725B_CODEC_REG_CGR8, 63e9d97b05SPaul Cercueil JZ4725B_CODEC_REG_CGR9, 64e9d97b05SPaul Cercueil JZ4725B_CODEC_REG_CGR10, 65e9d97b05SPaul Cercueil JZ4725B_CODEC_REG_TR1, 66e9d97b05SPaul Cercueil JZ4725B_CODEC_REG_TR2, 67e9d97b05SPaul Cercueil JZ4725B_CODEC_REG_CR3, 68e9d97b05SPaul Cercueil JZ4725B_CODEC_REG_AGC1, 69e9d97b05SPaul Cercueil JZ4725B_CODEC_REG_AGC2, 70e9d97b05SPaul Cercueil JZ4725B_CODEC_REG_AGC3, 71e9d97b05SPaul Cercueil JZ4725B_CODEC_REG_AGC4, 72e9d97b05SPaul Cercueil JZ4725B_CODEC_REG_AGC5, 73e9d97b05SPaul Cercueil }; 74e9d97b05SPaul Cercueil 75e9d97b05SPaul Cercueil #define REG_AICR_CONFIG1_OFFSET 0 76e9d97b05SPaul Cercueil #define REG_AICR_CONFIG1_MASK (0xf << REG_AICR_CONFIG1_OFFSET) 77e9d97b05SPaul Cercueil 78e9d97b05SPaul Cercueil #define REG_CR1_SB_MICBIAS_OFFSET 7 79e9d97b05SPaul Cercueil #define REG_CR1_MONO_OFFSET 6 80e9d97b05SPaul Cercueil #define REG_CR1_DAC_MUTE_OFFSET 5 81e9d97b05SPaul Cercueil #define REG_CR1_HP_DIS_OFFSET 4 82e9d97b05SPaul Cercueil #define REG_CR1_DACSEL_OFFSET 3 83e9d97b05SPaul Cercueil #define REG_CR1_BYPASS_OFFSET 2 84e9d97b05SPaul Cercueil 85e9d97b05SPaul Cercueil #define REG_CR2_DAC_DEEMP_OFFSET 7 86e9d97b05SPaul Cercueil #define REG_CR2_DAC_ADWL_OFFSET 5 87e9d97b05SPaul Cercueil #define REG_CR2_DAC_ADWL_MASK (0x3 << REG_CR2_DAC_ADWL_OFFSET) 88e9d97b05SPaul Cercueil #define REG_CR2_ADC_ADWL_OFFSET 3 89e9d97b05SPaul Cercueil #define REG_CR2_ADC_ADWL_MASK (0x3 << REG_CR2_ADC_ADWL_OFFSET) 90e9d97b05SPaul Cercueil #define REG_CR2_ADC_HPF_OFFSET 2 91e9d97b05SPaul Cercueil 92e9d97b05SPaul Cercueil #define REG_CR3_SB_MIC1_OFFSET 7 93e9d97b05SPaul Cercueil #define REG_CR3_SB_MIC2_OFFSET 6 94e9d97b05SPaul Cercueil #define REG_CR3_SIDETONE1_OFFSET 5 95e9d97b05SPaul Cercueil #define REG_CR3_SIDETONE2_OFFSET 4 96e9d97b05SPaul Cercueil #define REG_CR3_MICDIFF_OFFSET 3 97e9d97b05SPaul Cercueil #define REG_CR3_MICSTEREO_OFFSET 2 98e9d97b05SPaul Cercueil #define REG_CR3_INSEL_OFFSET 0 99e9d97b05SPaul Cercueil #define REG_CR3_INSEL_MASK (0x3 << REG_CR3_INSEL_OFFSET) 100e9d97b05SPaul Cercueil 101e9d97b05SPaul Cercueil #define REG_CCR1_CONFIG4_OFFSET 0 102e9d97b05SPaul Cercueil #define REG_CCR1_CONFIG4_MASK (0xf << REG_CCR1_CONFIG4_OFFSET) 103e9d97b05SPaul Cercueil 104e9d97b05SPaul Cercueil #define REG_CCR2_DFREQ_OFFSET 4 105e9d97b05SPaul Cercueil #define REG_CCR2_DFREQ_MASK (0xf << REG_CCR2_DFREQ_OFFSET) 106e9d97b05SPaul Cercueil #define REG_CCR2_AFREQ_OFFSET 0 107e9d97b05SPaul Cercueil #define REG_CCR2_AFREQ_MASK (0xf << REG_CCR2_AFREQ_OFFSET) 108e9d97b05SPaul Cercueil 109e9d97b05SPaul Cercueil #define REG_PMR1_SB_DAC_OFFSET 7 110e9d97b05SPaul Cercueil #define REG_PMR1_SB_OUT_OFFSET 6 111e9d97b05SPaul Cercueil #define REG_PMR1_SB_MIX_OFFSET 5 112e9d97b05SPaul Cercueil #define REG_PMR1_SB_ADC_OFFSET 4 113e9d97b05SPaul Cercueil #define REG_PMR1_SB_LIN_OFFSET 3 114e9d97b05SPaul Cercueil #define REG_PMR1_SB_IND_OFFSET 0 115e9d97b05SPaul Cercueil 116e9d97b05SPaul Cercueil #define REG_PMR2_LRGI_OFFSET 7 117e9d97b05SPaul Cercueil #define REG_PMR2_RLGI_OFFSET 6 118e9d97b05SPaul Cercueil #define REG_PMR2_LRGOD_OFFSET 5 119e9d97b05SPaul Cercueil #define REG_PMR2_RLGOD_OFFSET 4 120e9d97b05SPaul Cercueil #define REG_PMR2_GIM_OFFSET 3 121e9d97b05SPaul Cercueil #define REG_PMR2_SB_MC_OFFSET 2 122e9d97b05SPaul Cercueil #define REG_PMR2_SB_OFFSET 1 123e9d97b05SPaul Cercueil #define REG_PMR2_SB_SLEEP_OFFSET 0 124e9d97b05SPaul Cercueil 125e9d97b05SPaul Cercueil #define REG_IFR_RAMP_UP_DONE_OFFSET 3 126e9d97b05SPaul Cercueil #define REG_IFR_RAMP_DOWN_DONE_OFFSET 2 127e9d97b05SPaul Cercueil 128e9d97b05SPaul Cercueil #define REG_CGR1_GODL_OFFSET 4 129e9d97b05SPaul Cercueil #define REG_CGR1_GODL_MASK (0xf << REG_CGR1_GODL_OFFSET) 130e9d97b05SPaul Cercueil #define REG_CGR1_GODR_OFFSET 0 131e9d97b05SPaul Cercueil #define REG_CGR1_GODR_MASK (0xf << REG_CGR1_GODR_OFFSET) 132e9d97b05SPaul Cercueil 133e9d97b05SPaul Cercueil #define REG_CGR2_GO1R_OFFSET 0 134e9d97b05SPaul Cercueil #define REG_CGR2_GO1R_MASK (0x1f << REG_CGR2_GO1R_OFFSET) 135e9d97b05SPaul Cercueil 136e9d97b05SPaul Cercueil #define REG_CGR3_GO1L_OFFSET 0 137e9d97b05SPaul Cercueil #define REG_CGR3_GO1L_MASK (0x1f << REG_CGR3_GO1L_OFFSET) 138e9d97b05SPaul Cercueil 139e9d97b05SPaul Cercueil struct jz_icdc { 140e9d97b05SPaul Cercueil struct regmap *regmap; 141e9d97b05SPaul Cercueil void __iomem *base; 142e9d97b05SPaul Cercueil struct clk *clk; 143e9d97b05SPaul Cercueil }; 144e9d97b05SPaul Cercueil 145e9d97b05SPaul Cercueil static const SNDRV_CTL_TLVD_DECLARE_DB_LINEAR(jz4725b_dac_tlv, -2250, 0); 146e9d97b05SPaul Cercueil static const SNDRV_CTL_TLVD_DECLARE_DB_LINEAR(jz4725b_line_tlv, -1500, 600); 147e9d97b05SPaul Cercueil 148e9d97b05SPaul Cercueil static const struct snd_kcontrol_new jz4725b_codec_controls[] = { 149e9d97b05SPaul Cercueil SOC_DOUBLE_TLV("Master Playback Volume", 150e9d97b05SPaul Cercueil JZ4725B_CODEC_REG_CGR1, 151e9d97b05SPaul Cercueil REG_CGR1_GODL_OFFSET, 152e9d97b05SPaul Cercueil REG_CGR1_GODR_OFFSET, 153e9d97b05SPaul Cercueil 0xf, 1, jz4725b_dac_tlv), 154e9d97b05SPaul Cercueil SOC_DOUBLE_R_TLV("Master Capture Volume", 155e9d97b05SPaul Cercueil JZ4725B_CODEC_REG_CGR3, 156e9d97b05SPaul Cercueil JZ4725B_CODEC_REG_CGR2, 157e9d97b05SPaul Cercueil REG_CGR2_GO1R_OFFSET, 158e9d97b05SPaul Cercueil 0x1f, 1, jz4725b_line_tlv), 159e9d97b05SPaul Cercueil 160e9d97b05SPaul Cercueil SOC_SINGLE("Master Playback Switch", JZ4725B_CODEC_REG_CR1, 161e9d97b05SPaul Cercueil REG_CR1_DAC_MUTE_OFFSET, 1, 1), 162e9d97b05SPaul Cercueil 1636c3e6302SColin Ian King SOC_SINGLE("Deemphasize Filter Playback Switch", 164e9d97b05SPaul Cercueil JZ4725B_CODEC_REG_CR2, 165e9d97b05SPaul Cercueil REG_CR2_DAC_DEEMP_OFFSET, 1, 0), 166e9d97b05SPaul Cercueil 167e9d97b05SPaul Cercueil SOC_SINGLE("High-Pass Filter Capture Switch", 168e9d97b05SPaul Cercueil JZ4725B_CODEC_REG_CR2, 169e9d97b05SPaul Cercueil REG_CR2_ADC_HPF_OFFSET, 1, 0), 170e9d97b05SPaul Cercueil }; 171e9d97b05SPaul Cercueil 172e9d97b05SPaul Cercueil static const char * const jz4725b_codec_adc_src_texts[] = { 173e9d97b05SPaul Cercueil "Mic 1", "Mic 2", "Line In", "Mixer", 174e9d97b05SPaul Cercueil }; 175e9d97b05SPaul Cercueil static const unsigned int jz4725b_codec_adc_src_values[] = { 0, 1, 2, 3, }; 176e9d97b05SPaul Cercueil static const SOC_VALUE_ENUM_SINGLE_DECL(jz4725b_codec_adc_src_enum, 177e9d97b05SPaul Cercueil JZ4725B_CODEC_REG_CR3, 178e9d97b05SPaul Cercueil REG_CR3_INSEL_OFFSET, 179e9d97b05SPaul Cercueil REG_CR3_INSEL_MASK, 180e9d97b05SPaul Cercueil jz4725b_codec_adc_src_texts, 181e9d97b05SPaul Cercueil jz4725b_codec_adc_src_values); 182e9d97b05SPaul Cercueil static const struct snd_kcontrol_new jz4725b_codec_adc_src_ctrl = 183e9d97b05SPaul Cercueil SOC_DAPM_ENUM("Route", jz4725b_codec_adc_src_enum); 184e9d97b05SPaul Cercueil 185e9d97b05SPaul Cercueil static const struct snd_kcontrol_new jz4725b_codec_mixer_controls[] = { 186e9d97b05SPaul Cercueil SOC_DAPM_SINGLE("Line In Bypass", JZ4725B_CODEC_REG_CR1, 187e9d97b05SPaul Cercueil REG_CR1_BYPASS_OFFSET, 1, 0), 188e9d97b05SPaul Cercueil }; 189e9d97b05SPaul Cercueil 190e9d97b05SPaul Cercueil static int jz4725b_out_stage_enable(struct snd_soc_dapm_widget *w, 191e9d97b05SPaul Cercueil struct snd_kcontrol *kcontrol, 192e9d97b05SPaul Cercueil int event) 193e9d97b05SPaul Cercueil { 194e9d97b05SPaul Cercueil struct snd_soc_component *codec = snd_soc_dapm_to_component(w->dapm); 195e9d97b05SPaul Cercueil struct jz_icdc *icdc = snd_soc_component_get_drvdata(codec); 196e9d97b05SPaul Cercueil struct regmap *map = icdc->regmap; 197e9d97b05SPaul Cercueil unsigned int val; 198e9d97b05SPaul Cercueil 199e9d97b05SPaul Cercueil switch (event) { 200e9d97b05SPaul Cercueil case SND_SOC_DAPM_PRE_PMU: 201e9d97b05SPaul Cercueil return regmap_update_bits(map, JZ4725B_CODEC_REG_IFR, 202e9d97b05SPaul Cercueil BIT(REG_IFR_RAMP_UP_DONE_OFFSET), 0); 203e9d97b05SPaul Cercueil case SND_SOC_DAPM_POST_PMU: 204e9d97b05SPaul Cercueil return regmap_read_poll_timeout(map, JZ4725B_CODEC_REG_IFR, 205e9d97b05SPaul Cercueil val, val & BIT(REG_IFR_RAMP_UP_DONE_OFFSET), 206e9d97b05SPaul Cercueil 100000, 500000); 207e9d97b05SPaul Cercueil case SND_SOC_DAPM_PRE_PMD: 208e9d97b05SPaul Cercueil return regmap_update_bits(map, JZ4725B_CODEC_REG_IFR, 209e9d97b05SPaul Cercueil BIT(REG_IFR_RAMP_DOWN_DONE_OFFSET), 0); 210e9d97b05SPaul Cercueil case SND_SOC_DAPM_POST_PMD: 211e9d97b05SPaul Cercueil return regmap_read_poll_timeout(map, JZ4725B_CODEC_REG_IFR, 212e9d97b05SPaul Cercueil val, val & BIT(REG_IFR_RAMP_DOWN_DONE_OFFSET), 213e9d97b05SPaul Cercueil 100000, 500000); 214e9d97b05SPaul Cercueil default: 215e9d97b05SPaul Cercueil return -EINVAL; 216e9d97b05SPaul Cercueil } 217e9d97b05SPaul Cercueil } 218e9d97b05SPaul Cercueil 219e9d97b05SPaul Cercueil static const struct snd_soc_dapm_widget jz4725b_codec_dapm_widgets[] = { 220e9d97b05SPaul Cercueil /* DAC */ 221e9d97b05SPaul Cercueil SND_SOC_DAPM_DAC("DAC", "Playback", 222e9d97b05SPaul Cercueil JZ4725B_CODEC_REG_PMR1, REG_PMR1_SB_DAC_OFFSET, 1), 223e9d97b05SPaul Cercueil 224e9d97b05SPaul Cercueil /* ADC */ 225e9d97b05SPaul Cercueil SND_SOC_DAPM_ADC("ADC", "Capture", 226e9d97b05SPaul Cercueil JZ4725B_CODEC_REG_PMR1, REG_PMR1_SB_ADC_OFFSET, 1), 227e9d97b05SPaul Cercueil 228e9d97b05SPaul Cercueil SND_SOC_DAPM_MUX("ADC Source", SND_SOC_NOPM, 0, 0, 229e9d97b05SPaul Cercueil &jz4725b_codec_adc_src_ctrl), 230e9d97b05SPaul Cercueil 231e9d97b05SPaul Cercueil /* Mixer */ 232e9d97b05SPaul Cercueil SND_SOC_DAPM_MIXER("Mixer", JZ4725B_CODEC_REG_PMR1, 233e9d97b05SPaul Cercueil REG_PMR1_SB_MIX_OFFSET, 1, 234e9d97b05SPaul Cercueil jz4725b_codec_mixer_controls, 235e9d97b05SPaul Cercueil ARRAY_SIZE(jz4725b_codec_mixer_controls)), 236e9d97b05SPaul Cercueil SND_SOC_DAPM_MIXER("DAC to Mixer", JZ4725B_CODEC_REG_CR1, 237e9d97b05SPaul Cercueil REG_CR1_DACSEL_OFFSET, 0, NULL, 0), 238e9d97b05SPaul Cercueil 239e9d97b05SPaul Cercueil SND_SOC_DAPM_MIXER("Line In", SND_SOC_NOPM, 0, 0, NULL, 0), 240e9d97b05SPaul Cercueil SND_SOC_DAPM_MIXER("HP Out", JZ4725B_CODEC_REG_CR1, 241e9d97b05SPaul Cercueil REG_CR1_HP_DIS_OFFSET, 1, NULL, 0), 242e9d97b05SPaul Cercueil 243e9d97b05SPaul Cercueil SND_SOC_DAPM_MIXER("Mic 1", JZ4725B_CODEC_REG_CR3, 244e9d97b05SPaul Cercueil REG_CR3_SB_MIC1_OFFSET, 1, NULL, 0), 245e9d97b05SPaul Cercueil SND_SOC_DAPM_MIXER("Mic 2", JZ4725B_CODEC_REG_CR3, 246e9d97b05SPaul Cercueil REG_CR3_SB_MIC2_OFFSET, 1, NULL, 0), 247e9d97b05SPaul Cercueil 248e9d97b05SPaul Cercueil SND_SOC_DAPM_MIXER_E("Out Stage", JZ4725B_CODEC_REG_PMR1, 249e9d97b05SPaul Cercueil REG_PMR1_SB_OUT_OFFSET, 1, NULL, 0, 250e9d97b05SPaul Cercueil jz4725b_out_stage_enable, 251e9d97b05SPaul Cercueil SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | 252e9d97b05SPaul Cercueil SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), 253e9d97b05SPaul Cercueil SND_SOC_DAPM_MIXER("Mixer to ADC", JZ4725B_CODEC_REG_PMR1, 254e9d97b05SPaul Cercueil REG_PMR1_SB_IND_OFFSET, 1, NULL, 0), 255e9d97b05SPaul Cercueil 256e9d97b05SPaul Cercueil SND_SOC_DAPM_SUPPLY("Mic Bias", JZ4725B_CODEC_REG_CR1, 257e9d97b05SPaul Cercueil REG_CR1_SB_MICBIAS_OFFSET, 1, NULL, 0), 258e9d97b05SPaul Cercueil 259e9d97b05SPaul Cercueil /* Pins */ 260e9d97b05SPaul Cercueil SND_SOC_DAPM_INPUT("MIC1P"), 261e9d97b05SPaul Cercueil SND_SOC_DAPM_INPUT("MIC1N"), 262e9d97b05SPaul Cercueil SND_SOC_DAPM_INPUT("MIC2P"), 263e9d97b05SPaul Cercueil SND_SOC_DAPM_INPUT("MIC2N"), 264e9d97b05SPaul Cercueil 265e9d97b05SPaul Cercueil SND_SOC_DAPM_INPUT("LLINEIN"), 266e9d97b05SPaul Cercueil SND_SOC_DAPM_INPUT("RLINEIN"), 267e9d97b05SPaul Cercueil 268e9d97b05SPaul Cercueil SND_SOC_DAPM_OUTPUT("LHPOUT"), 269e9d97b05SPaul Cercueil SND_SOC_DAPM_OUTPUT("RHPOUT"), 270e9d97b05SPaul Cercueil }; 271e9d97b05SPaul Cercueil 272e9d97b05SPaul Cercueil static const struct snd_soc_dapm_route jz4725b_codec_dapm_routes[] = { 273e9d97b05SPaul Cercueil {"Mic 1", NULL, "MIC1P"}, 274e9d97b05SPaul Cercueil {"Mic 1", NULL, "MIC1N"}, 275e9d97b05SPaul Cercueil {"Mic 2", NULL, "MIC2P"}, 276e9d97b05SPaul Cercueil {"Mic 2", NULL, "MIC2N"}, 277e9d97b05SPaul Cercueil 278e9d97b05SPaul Cercueil {"Line In", NULL, "LLINEIN"}, 279e9d97b05SPaul Cercueil {"Line In", NULL, "RLINEIN"}, 280e9d97b05SPaul Cercueil 281e9d97b05SPaul Cercueil {"Mixer", "Line In Bypass", "Line In"}, 282e9d97b05SPaul Cercueil {"DAC to Mixer", NULL, "DAC"}, 283e9d97b05SPaul Cercueil {"Mixer", NULL, "DAC to Mixer"}, 284e9d97b05SPaul Cercueil 285e9d97b05SPaul Cercueil {"Mixer to ADC", NULL, "Mixer"}, 286e9d97b05SPaul Cercueil {"ADC Source", "Mixer", "Mixer to ADC"}, 287e9d97b05SPaul Cercueil {"ADC Source", "Line In", "Line In"}, 288e9d97b05SPaul Cercueil {"ADC Source", "Mic 1", "Mic 1"}, 289e9d97b05SPaul Cercueil {"ADC Source", "Mic 2", "Mic 2"}, 290e9d97b05SPaul Cercueil {"ADC", NULL, "ADC Source"}, 291e9d97b05SPaul Cercueil 292e9d97b05SPaul Cercueil {"Out Stage", NULL, "Mixer"}, 293e9d97b05SPaul Cercueil {"HP Out", NULL, "Out Stage"}, 294e9d97b05SPaul Cercueil {"LHPOUT", NULL, "HP Out"}, 295e9d97b05SPaul Cercueil {"RHPOUT", NULL, "HP Out"}, 296e9d97b05SPaul Cercueil }; 297e9d97b05SPaul Cercueil 298e9d97b05SPaul Cercueil static int jz4725b_codec_set_bias_level(struct snd_soc_component *component, 299e9d97b05SPaul Cercueil enum snd_soc_bias_level level) 300e9d97b05SPaul Cercueil { 301e9d97b05SPaul Cercueil struct jz_icdc *icdc = snd_soc_component_get_drvdata(component); 302e9d97b05SPaul Cercueil struct regmap *map = icdc->regmap; 303e9d97b05SPaul Cercueil 304e9d97b05SPaul Cercueil switch (level) { 305e9d97b05SPaul Cercueil case SND_SOC_BIAS_ON: 306e9d97b05SPaul Cercueil regmap_update_bits(map, JZ4725B_CODEC_REG_PMR2, 307e9d97b05SPaul Cercueil BIT(REG_PMR2_SB_SLEEP_OFFSET), 0); 308e9d97b05SPaul Cercueil break; 309e9d97b05SPaul Cercueil case SND_SOC_BIAS_PREPARE: 310e9d97b05SPaul Cercueil /* Enable sound hardware */ 311e9d97b05SPaul Cercueil regmap_update_bits(map, JZ4725B_CODEC_REG_PMR2, 312e9d97b05SPaul Cercueil BIT(REG_PMR2_SB_OFFSET), 0); 313e9d97b05SPaul Cercueil msleep(224); 314e9d97b05SPaul Cercueil break; 315e9d97b05SPaul Cercueil case SND_SOC_BIAS_STANDBY: 316e9d97b05SPaul Cercueil regmap_update_bits(map, JZ4725B_CODEC_REG_PMR2, 317e9d97b05SPaul Cercueil BIT(REG_PMR2_SB_SLEEP_OFFSET), 318e9d97b05SPaul Cercueil BIT(REG_PMR2_SB_SLEEP_OFFSET)); 319e9d97b05SPaul Cercueil break; 320e9d97b05SPaul Cercueil case SND_SOC_BIAS_OFF: 321e9d97b05SPaul Cercueil regmap_update_bits(map, JZ4725B_CODEC_REG_PMR2, 322e9d97b05SPaul Cercueil BIT(REG_PMR2_SB_OFFSET), 323e9d97b05SPaul Cercueil BIT(REG_PMR2_SB_OFFSET)); 324e9d97b05SPaul Cercueil break; 325e9d97b05SPaul Cercueil } 326e9d97b05SPaul Cercueil 327e9d97b05SPaul Cercueil return 0; 328e9d97b05SPaul Cercueil } 329e9d97b05SPaul Cercueil 330e9d97b05SPaul Cercueil static int jz4725b_codec_dev_probe(struct snd_soc_component *component) 331e9d97b05SPaul Cercueil { 332e9d97b05SPaul Cercueil struct jz_icdc *icdc = snd_soc_component_get_drvdata(component); 333e9d97b05SPaul Cercueil struct regmap *map = icdc->regmap; 334e9d97b05SPaul Cercueil 335e9d97b05SPaul Cercueil clk_prepare_enable(icdc->clk); 336e9d97b05SPaul Cercueil 337e9d97b05SPaul Cercueil /* Write CONFIGn (n=1 to 8) bits. 338e9d97b05SPaul Cercueil * The value 0x0f is specified in the datasheet as a requirement. 339e9d97b05SPaul Cercueil */ 340e9d97b05SPaul Cercueil regmap_write(map, JZ4725B_CODEC_REG_AICR, 341e9d97b05SPaul Cercueil 0xf << REG_AICR_CONFIG1_OFFSET); 342e9d97b05SPaul Cercueil regmap_write(map, JZ4725B_CODEC_REG_CCR1, 343e9d97b05SPaul Cercueil 0x0 << REG_CCR1_CONFIG4_OFFSET); 344e9d97b05SPaul Cercueil 345e9d97b05SPaul Cercueil return 0; 346e9d97b05SPaul Cercueil } 347e9d97b05SPaul Cercueil 348e9d97b05SPaul Cercueil static void jz4725b_codec_dev_remove(struct snd_soc_component *component) 349e9d97b05SPaul Cercueil { 350e9d97b05SPaul Cercueil struct jz_icdc *icdc = snd_soc_component_get_drvdata(component); 351e9d97b05SPaul Cercueil 352e9d97b05SPaul Cercueil clk_disable_unprepare(icdc->clk); 353e9d97b05SPaul Cercueil } 354e9d97b05SPaul Cercueil 355e9d97b05SPaul Cercueil static const struct snd_soc_component_driver jz4725b_codec = { 356e9d97b05SPaul Cercueil .probe = jz4725b_codec_dev_probe, 357e9d97b05SPaul Cercueil .remove = jz4725b_codec_dev_remove, 358e9d97b05SPaul Cercueil .set_bias_level = jz4725b_codec_set_bias_level, 359e9d97b05SPaul Cercueil .controls = jz4725b_codec_controls, 360e9d97b05SPaul Cercueil .num_controls = ARRAY_SIZE(jz4725b_codec_controls), 361e9d97b05SPaul Cercueil .dapm_widgets = jz4725b_codec_dapm_widgets, 362e9d97b05SPaul Cercueil .num_dapm_widgets = ARRAY_SIZE(jz4725b_codec_dapm_widgets), 363e9d97b05SPaul Cercueil .dapm_routes = jz4725b_codec_dapm_routes, 364e9d97b05SPaul Cercueil .num_dapm_routes = ARRAY_SIZE(jz4725b_codec_dapm_routes), 365e9d97b05SPaul Cercueil .suspend_bias_off = 1, 366e9d97b05SPaul Cercueil .use_pmdown_time = 1, 367e9d97b05SPaul Cercueil }; 368e9d97b05SPaul Cercueil 369e9d97b05SPaul Cercueil static const unsigned int jz4725b_codec_sample_rates[] = { 370e9d97b05SPaul Cercueil 96000, 48000, 44100, 32000, 371e9d97b05SPaul Cercueil 24000, 22050, 16000, 12000, 372e9d97b05SPaul Cercueil 11025, 9600, 8000, 373e9d97b05SPaul Cercueil }; 374e9d97b05SPaul Cercueil 375e9d97b05SPaul Cercueil static int jz4725b_codec_hw_params(struct snd_pcm_substream *substream, 376e9d97b05SPaul Cercueil struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) 377e9d97b05SPaul Cercueil { 378e9d97b05SPaul Cercueil struct jz_icdc *icdc = snd_soc_component_get_drvdata(dai->component); 379e9d97b05SPaul Cercueil unsigned int rate, bit_width; 380e9d97b05SPaul Cercueil 381e9d97b05SPaul Cercueil switch (params_format(params)) { 382e9d97b05SPaul Cercueil case SNDRV_PCM_FORMAT_S16_LE: 383e9d97b05SPaul Cercueil bit_width = 0; 384e9d97b05SPaul Cercueil break; 385e9d97b05SPaul Cercueil case SNDRV_PCM_FORMAT_S18_3LE: 386e9d97b05SPaul Cercueil bit_width = 1; 387e9d97b05SPaul Cercueil break; 388e9d97b05SPaul Cercueil case SNDRV_PCM_FORMAT_S20_3LE: 389e9d97b05SPaul Cercueil bit_width = 2; 390e9d97b05SPaul Cercueil break; 391e9d97b05SPaul Cercueil case SNDRV_PCM_FORMAT_S24_3LE: 392e9d97b05SPaul Cercueil bit_width = 3; 393e9d97b05SPaul Cercueil break; 394e9d97b05SPaul Cercueil default: 395e9d97b05SPaul Cercueil return -EINVAL; 396e9d97b05SPaul Cercueil } 397e9d97b05SPaul Cercueil 398e9d97b05SPaul Cercueil for (rate = 0; rate < ARRAY_SIZE(jz4725b_codec_sample_rates); rate++) { 399e9d97b05SPaul Cercueil if (jz4725b_codec_sample_rates[rate] == params_rate(params)) 400e9d97b05SPaul Cercueil break; 401e9d97b05SPaul Cercueil } 402e9d97b05SPaul Cercueil 403e9d97b05SPaul Cercueil if (rate == ARRAY_SIZE(jz4725b_codec_sample_rates)) 404e9d97b05SPaul Cercueil return -EINVAL; 405e9d97b05SPaul Cercueil 406e9d97b05SPaul Cercueil if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 407e9d97b05SPaul Cercueil regmap_update_bits(icdc->regmap, 408e9d97b05SPaul Cercueil JZ4725B_CODEC_REG_CR2, 409e9d97b05SPaul Cercueil REG_CR2_DAC_ADWL_MASK, 410e9d97b05SPaul Cercueil bit_width << REG_CR2_DAC_ADWL_OFFSET); 411e9d97b05SPaul Cercueil 412e9d97b05SPaul Cercueil regmap_update_bits(icdc->regmap, 413e9d97b05SPaul Cercueil JZ4725B_CODEC_REG_CCR2, 414e9d97b05SPaul Cercueil REG_CCR2_DFREQ_MASK, 415e9d97b05SPaul Cercueil rate << REG_CCR2_DFREQ_OFFSET); 416e9d97b05SPaul Cercueil } else { 417e9d97b05SPaul Cercueil regmap_update_bits(icdc->regmap, 418e9d97b05SPaul Cercueil JZ4725B_CODEC_REG_CR2, 419e9d97b05SPaul Cercueil REG_CR2_ADC_ADWL_MASK, 420e9d97b05SPaul Cercueil bit_width << REG_CR2_ADC_ADWL_OFFSET); 421e9d97b05SPaul Cercueil 422e9d97b05SPaul Cercueil regmap_update_bits(icdc->regmap, 423e9d97b05SPaul Cercueil JZ4725B_CODEC_REG_CCR2, 424e9d97b05SPaul Cercueil REG_CCR2_AFREQ_MASK, 425e9d97b05SPaul Cercueil rate << REG_CCR2_AFREQ_OFFSET); 426e9d97b05SPaul Cercueil } 427e9d97b05SPaul Cercueil 428e9d97b05SPaul Cercueil return 0; 429e9d97b05SPaul Cercueil } 430e9d97b05SPaul Cercueil 431e9d97b05SPaul Cercueil static const struct snd_soc_dai_ops jz4725b_codec_dai_ops = { 432e9d97b05SPaul Cercueil .hw_params = jz4725b_codec_hw_params, 433e9d97b05SPaul Cercueil }; 434e9d97b05SPaul Cercueil 435e9d97b05SPaul Cercueil #define JZ_ICDC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE | \ 436e9d97b05SPaul Cercueil SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_3LE) 437e9d97b05SPaul Cercueil 438e9d97b05SPaul Cercueil static struct snd_soc_dai_driver jz4725b_codec_dai = { 439e9d97b05SPaul Cercueil .name = "jz4725b-hifi", 440e9d97b05SPaul Cercueil .playback = { 441e9d97b05SPaul Cercueil .stream_name = "Playback", 442e9d97b05SPaul Cercueil .channels_min = 2, 443e9d97b05SPaul Cercueil .channels_max = 2, 444e9d97b05SPaul Cercueil .rates = SNDRV_PCM_RATE_8000_96000, 445e9d97b05SPaul Cercueil .formats = JZ_ICDC_FORMATS, 446e9d97b05SPaul Cercueil }, 447e9d97b05SPaul Cercueil .capture = { 448e9d97b05SPaul Cercueil .stream_name = "Capture", 449e9d97b05SPaul Cercueil .channels_min = 2, 450e9d97b05SPaul Cercueil .channels_max = 2, 451e9d97b05SPaul Cercueil .rates = SNDRV_PCM_RATE_8000_96000, 452e9d97b05SPaul Cercueil .formats = JZ_ICDC_FORMATS, 453e9d97b05SPaul Cercueil }, 454e9d97b05SPaul Cercueil .ops = &jz4725b_codec_dai_ops, 455e9d97b05SPaul Cercueil }; 456e9d97b05SPaul Cercueil 457e9d97b05SPaul Cercueil static bool jz4725b_codec_volatile(struct device *dev, unsigned int reg) 458e9d97b05SPaul Cercueil { 459e9d97b05SPaul Cercueil return reg == JZ4725B_CODEC_REG_IFR; 460e9d97b05SPaul Cercueil } 461e9d97b05SPaul Cercueil 462e9d97b05SPaul Cercueil static bool jz4725b_codec_can_access_reg(struct device *dev, unsigned int reg) 463e9d97b05SPaul Cercueil { 464e9d97b05SPaul Cercueil return (reg != JZ4725B_CODEC_REG_TR1) && (reg != JZ4725B_CODEC_REG_TR2); 465e9d97b05SPaul Cercueil } 466e9d97b05SPaul Cercueil 467e9d97b05SPaul Cercueil static int jz4725b_codec_io_wait(struct jz_icdc *icdc) 468e9d97b05SPaul Cercueil { 469e9d97b05SPaul Cercueil u32 reg; 470e9d97b05SPaul Cercueil 471e9d97b05SPaul Cercueil return readl_poll_timeout(icdc->base + ICDC_RGADW_OFFSET, reg, 472e9d97b05SPaul Cercueil !(reg & ICDC_RGADW_RGWR), 1000, 10000); 473e9d97b05SPaul Cercueil } 474e9d97b05SPaul Cercueil 475e9d97b05SPaul Cercueil static int jz4725b_codec_reg_read(void *context, unsigned int reg, 476e9d97b05SPaul Cercueil unsigned int *val) 477e9d97b05SPaul Cercueil { 478e9d97b05SPaul Cercueil struct jz_icdc *icdc = context; 479e9d97b05SPaul Cercueil unsigned int i; 480e9d97b05SPaul Cercueil u32 tmp; 481e9d97b05SPaul Cercueil int ret; 482e9d97b05SPaul Cercueil 483e9d97b05SPaul Cercueil ret = jz4725b_codec_io_wait(icdc); 484e9d97b05SPaul Cercueil if (ret) 485e9d97b05SPaul Cercueil return ret; 486e9d97b05SPaul Cercueil 487e9d97b05SPaul Cercueil tmp = readl(icdc->base + ICDC_RGADW_OFFSET); 488e9d97b05SPaul Cercueil tmp = (tmp & ~ICDC_RGADW_RGADDR_MASK) 489e9d97b05SPaul Cercueil | (reg << ICDC_RGADW_RGADDR_OFFSET); 490e9d97b05SPaul Cercueil writel(tmp, icdc->base + ICDC_RGADW_OFFSET); 491e9d97b05SPaul Cercueil 492e9d97b05SPaul Cercueil /* wait 6+ cycles */ 493e9d97b05SPaul Cercueil for (i = 0; i < 6; i++) 494e9d97b05SPaul Cercueil *val = readl(icdc->base + ICDC_RGDATA_OFFSET) & 495e9d97b05SPaul Cercueil ICDC_RGDATA_RGDOUT_MASK; 496e9d97b05SPaul Cercueil 497e9d97b05SPaul Cercueil return 0; 498e9d97b05SPaul Cercueil } 499e9d97b05SPaul Cercueil 500e9d97b05SPaul Cercueil static int jz4725b_codec_reg_write(void *context, unsigned int reg, 501e9d97b05SPaul Cercueil unsigned int val) 502e9d97b05SPaul Cercueil { 503e9d97b05SPaul Cercueil struct jz_icdc *icdc = context; 504e9d97b05SPaul Cercueil int ret; 505e9d97b05SPaul Cercueil 506e9d97b05SPaul Cercueil ret = jz4725b_codec_io_wait(icdc); 507e9d97b05SPaul Cercueil if (ret) 508e9d97b05SPaul Cercueil return ret; 509e9d97b05SPaul Cercueil 510e9d97b05SPaul Cercueil writel(ICDC_RGADW_RGWR | (reg << ICDC_RGADW_RGADDR_OFFSET) | val, 511e9d97b05SPaul Cercueil icdc->base + ICDC_RGADW_OFFSET); 512e9d97b05SPaul Cercueil 513e9d97b05SPaul Cercueil ret = jz4725b_codec_io_wait(icdc); 514e9d97b05SPaul Cercueil if (ret) 515e9d97b05SPaul Cercueil return ret; 516e9d97b05SPaul Cercueil 517e9d97b05SPaul Cercueil return 0; 518e9d97b05SPaul Cercueil } 519e9d97b05SPaul Cercueil 520e9d97b05SPaul Cercueil static const u8 jz4725b_codec_reg_defaults[] = { 521e9d97b05SPaul Cercueil 0x0c, 0xaa, 0x78, 0x00, 0x00, 0xff, 0x03, 0x51, 522e9d97b05SPaul Cercueil 0x3f, 0x00, 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 523e9d97b05SPaul Cercueil 0x04, 0x0a, 0x0a, 0x00, 0x00, 0x00, 0xc0, 0x34, 524e9d97b05SPaul Cercueil 0x07, 0x44, 0x1f, 0x00, 525e9d97b05SPaul Cercueil }; 526e9d97b05SPaul Cercueil 527e9d97b05SPaul Cercueil static const struct regmap_config jz4725b_codec_regmap_config = { 528e9d97b05SPaul Cercueil .reg_bits = 7, 529e9d97b05SPaul Cercueil .val_bits = 8, 530e9d97b05SPaul Cercueil 531e9d97b05SPaul Cercueil .max_register = JZ4725B_CODEC_REG_AGC5, 532e9d97b05SPaul Cercueil .volatile_reg = jz4725b_codec_volatile, 533e9d97b05SPaul Cercueil .readable_reg = jz4725b_codec_can_access_reg, 534e9d97b05SPaul Cercueil .writeable_reg = jz4725b_codec_can_access_reg, 535e9d97b05SPaul Cercueil 536e9d97b05SPaul Cercueil .reg_read = jz4725b_codec_reg_read, 537e9d97b05SPaul Cercueil .reg_write = jz4725b_codec_reg_write, 538e9d97b05SPaul Cercueil 539e9d97b05SPaul Cercueil .reg_defaults_raw = jz4725b_codec_reg_defaults, 540e9d97b05SPaul Cercueil .num_reg_defaults_raw = ARRAY_SIZE(jz4725b_codec_reg_defaults), 541e9d97b05SPaul Cercueil .cache_type = REGCACHE_FLAT, 542e9d97b05SPaul Cercueil }; 543e9d97b05SPaul Cercueil 544e9d97b05SPaul Cercueil static int jz4725b_codec_probe(struct platform_device *pdev) 545e9d97b05SPaul Cercueil { 546e9d97b05SPaul Cercueil struct device *dev = &pdev->dev; 547e9d97b05SPaul Cercueil struct jz_icdc *icdc; 548e9d97b05SPaul Cercueil struct resource *mem; 549e9d97b05SPaul Cercueil int ret; 550e9d97b05SPaul Cercueil 551e9d97b05SPaul Cercueil icdc = devm_kzalloc(dev, sizeof(*icdc), GFP_KERNEL); 552e9d97b05SPaul Cercueil if (!icdc) 553e9d97b05SPaul Cercueil return -ENOMEM; 554e9d97b05SPaul Cercueil 555e9d97b05SPaul Cercueil mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 556e9d97b05SPaul Cercueil icdc->base = devm_ioremap_resource(dev, mem); 557e9d97b05SPaul Cercueil if (IS_ERR(icdc->base)) 558e9d97b05SPaul Cercueil return PTR_ERR(icdc->base); 559e9d97b05SPaul Cercueil 560e9d97b05SPaul Cercueil icdc->regmap = devm_regmap_init(dev, NULL, icdc, 561e9d97b05SPaul Cercueil &jz4725b_codec_regmap_config); 562e9d97b05SPaul Cercueil if (IS_ERR(icdc->regmap)) 563e9d97b05SPaul Cercueil return PTR_ERR(icdc->regmap); 564e9d97b05SPaul Cercueil 565e9d97b05SPaul Cercueil icdc->clk = devm_clk_get(&pdev->dev, "aic"); 566e9d97b05SPaul Cercueil if (IS_ERR(icdc->clk)) 567e9d97b05SPaul Cercueil return PTR_ERR(icdc->clk); 568e9d97b05SPaul Cercueil 569e9d97b05SPaul Cercueil platform_set_drvdata(pdev, icdc); 570e9d97b05SPaul Cercueil 571e9d97b05SPaul Cercueil ret = devm_snd_soc_register_component(dev, &jz4725b_codec, 572e9d97b05SPaul Cercueil &jz4725b_codec_dai, 1); 573e9d97b05SPaul Cercueil if (ret) 574e9d97b05SPaul Cercueil dev_err(dev, "Failed to register codec\n"); 575e9d97b05SPaul Cercueil 576e9d97b05SPaul Cercueil return ret; 577e9d97b05SPaul Cercueil } 578e9d97b05SPaul Cercueil 579e9d97b05SPaul Cercueil #ifdef CONFIG_OF 580e9d97b05SPaul Cercueil static const struct of_device_id jz4725b_codec_of_matches[] = { 581e9d97b05SPaul Cercueil { .compatible = "ingenic,jz4725b-codec", }, 582e9d97b05SPaul Cercueil { } 583e9d97b05SPaul Cercueil }; 584e9d97b05SPaul Cercueil MODULE_DEVICE_TABLE(of, jz4725b_codec_of_matches); 585e9d97b05SPaul Cercueil #endif 586e9d97b05SPaul Cercueil 587e9d97b05SPaul Cercueil static struct platform_driver jz4725b_codec_driver = { 588e9d97b05SPaul Cercueil .probe = jz4725b_codec_probe, 589e9d97b05SPaul Cercueil .driver = { 590e9d97b05SPaul Cercueil .name = "jz4725b-codec", 591e9d97b05SPaul Cercueil .of_match_table = of_match_ptr(jz4725b_codec_of_matches), 592e9d97b05SPaul Cercueil }, 593e9d97b05SPaul Cercueil }; 594e9d97b05SPaul Cercueil module_platform_driver(jz4725b_codec_driver); 595e9d97b05SPaul Cercueil 596e9d97b05SPaul Cercueil MODULE_DESCRIPTION("JZ4725B SoC internal codec driver"); 597e9d97b05SPaul Cercueil MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>"); 598e9d97b05SPaul Cercueil MODULE_LICENSE("GPL v2"); 599