yuezonghe | 824eb0c | 2024-06-27 02:32:26 -0700 | [diff] [blame^] | 1 | /*
|
| 2 | * nau8810.c -- NAU8810 ALSA Soc Audio driver
|
| 3 | *
|
| 4 | * Copyright 2016 Nuvoton Technology Corp.
|
| 5 | *
|
| 6 | * Author: David Lin <ctlin0@nuvoton.com>
|
| 7 | *
|
| 8 | * Based on WM8974.c
|
| 9 | *
|
| 10 | * This program is free software; you can redistribute it and/or modify
|
| 11 | * it under the terms of the GNU General Public License version 2 as
|
| 12 | * published by the Free Software Foundation.
|
| 13 | */
|
| 14 |
|
| 15 | #include <linux/module.h>
|
| 16 | #include <linux/moduleparam.h>
|
| 17 | #include <linux/kernel.h>
|
| 18 | #include <linux/init.h>
|
| 19 | #include <linux/delay.h>
|
| 20 | #include <linux/pm.h>
|
| 21 | #include <linux/i2c.h>
|
| 22 | #include <linux/irq.h>
|
| 23 |
|
| 24 | #include <linux/regmap.h>
|
| 25 | #include <linux/slab.h>
|
| 26 | #include <sound/core.h>
|
| 27 | #include <sound/pcm.h>
|
| 28 | #include <sound/pcm_params.h>
|
| 29 | #include <sound/soc.h>
|
| 30 | #include <sound/initval.h>
|
| 31 | #include <sound/tlv.h>
|
| 32 | #include <linux/gpio.h>
|
| 33 | #include <mach/pcu.h>
|
| 34 | #include <sound/jack.h>
|
| 35 |
|
| 36 | #include "nau8810.h"
|
| 37 |
|
| 38 | #define NAU_PLL_FREQ_MAX 100000000
|
| 39 | #define NAU_PLL_FREQ_MIN 90000000
|
| 40 | #define NAU_PLL_REF_MAX 33000000
|
| 41 | #define NAU_PLL_REF_MIN 8000000
|
| 42 | #define NAU_PLL_OPTOP_MIN 6
|
| 43 |
|
| 44 | int snd_soc_bytes_info_ext(struct snd_kcontrol *kcontrol,
|
| 45 | struct snd_ctl_elem_info *ucontrol)
|
| 46 | {
|
| 47 | unsigned long params = kcontrol->private_value;
|
| 48 |
|
| 49 | ucontrol->type = SNDRV_CTL_ELEM_TYPE_BYTES;
|
| 50 | ucontrol->count = params;
|
| 51 |
|
| 52 | return 0;
|
| 53 | }
|
| 54 |
|
| 55 | #define SND_SOC_BYTES_EXT(xname, xcount, xhandler_get, xhandler_put) \
|
| 56 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
|
| 57 | .info = snd_soc_bytes_info_ext, \
|
| 58 | .get = xhandler_get, .put = xhandler_put, \
|
| 59 | .private_value = xcount }
|
| 60 |
|
| 61 |
|
| 62 | static const int nau8810_mclk_scaler[] = { 10, 15, 20, 30, 40, 60, 80, 120 };
|
| 63 |
|
| 64 | static const u16 nau8810_reg_defaults[] = {
|
| 65 | [ NAU8810_REG_POWER1] = 0x0100, //0x0000 0x0100
|
| 66 | [ NAU8810_REG_POWER2] = 0x0000,
|
| 67 | [ NAU8810_REG_POWER3] = 0x0000, //0x0000 0x00fd
|
| 68 | [ NAU8810_REG_IFACE] = 0x0050,
|
| 69 | [ NAU8810_REG_COMP] = 0x0000,
|
| 70 | [ NAU8810_REG_CLOCK] = 0x0140,
|
| 71 | [ NAU8810_REG_SMPLR] = 0x0000,
|
| 72 | [ NAU8810_REG_DAC] = 0x0008, //0x0000 0x0008
|
| 73 | [ NAU8810_REG_DACGAIN] = 0x00FF,
|
| 74 | [ NAU8810_REG_ADC] = 0x0100,
|
| 75 | [ NAU8810_REG_ADCGAIN] = 0x00FF,
|
| 76 | [ NAU8810_REG_EQ1] = 0x012C,
|
| 77 | [ NAU8810_REG_EQ2] = 0x002C,
|
| 78 | [ NAU8810_REG_EQ3] = 0x002C,
|
| 79 | [ NAU8810_REG_EQ4] = 0x002C,
|
| 80 | [ NAU8810_REG_EQ5] = 0x002C,
|
| 81 | [ NAU8810_REG_DACLIM1] = 0x0032,
|
| 82 | [ NAU8810_REG_DACLIM2] = 0x0000,
|
| 83 | [ NAU8810_REG_NOTCH1] = 0x0000,
|
| 84 | [ NAU8810_REG_NOTCH2] = 0x0000,
|
| 85 | [ NAU8810_REG_NOTCH3] = 0x0000,
|
| 86 | [ NAU8810_REG_NOTCH4] = 0x0000,
|
| 87 | [ NAU8810_REG_ALC1] = 0x0038,
|
| 88 | [ NAU8810_REG_ALC2] = 0x000B,
|
| 89 | [ NAU8810_REG_ALC3] = 0x0032,
|
| 90 | [ NAU8810_REG_NOISEGATE] = 0x0000,
|
| 91 | [ NAU8810_REG_PLLN] = 0x0008,
|
| 92 | [ NAU8810_REG_PLLK1] = 0x000C,
|
| 93 | [ NAU8810_REG_PLLK2] = 0x0093,
|
| 94 | [ NAU8810_REG_PLLK3] = 0x00E9,
|
| 95 | [ NAU8810_REG_ATTEN] = 0x0000,
|
| 96 | [ NAU8810_REG_INPUT_SIGNAL] = 0x0003, //0x0003
|
| 97 | [ NAU8810_REG_PGAGAIN] = 0x0010,
|
| 98 | [ NAU8810_REG_ADCBOOST] = 0x0100,
|
| 99 | [ NAU8810_REG_OUTPUT] = 0x000e, //0x0002 (0x0006)
|
| 100 | [ NAU8810_REG_SPKMIX] = 0x0001, //def
|
| 101 | [ NAU8810_REG_SPKGAIN] = 0x0039, //0x0039 0x00bf
|
| 102 | [ NAU8810_REG_MONOMIX] = 0x0001,
|
| 103 | [ NAU8810_REG_POWER4] = 0x0000,
|
| 104 | [ NAU8810_REG_TSLOTCTL1] = 0x0000,
|
| 105 | [ NAU8810_REG_TSLOTCTL2] = 0x0020,
|
| 106 | [ NAU8810_REG_DEVICE_REVID] = 0x0000,
|
| 107 | [ NAU8810_REG_I2C_DEVICEID] = 0x001A,
|
| 108 | [ NAU8810_REG_ADDITIONID] = 0x00CA,
|
| 109 | [ NAU8810_REG_RESERVE] = 0x0124,
|
| 110 | [ NAU8810_REG_OUTCTL] = 0x0001,
|
| 111 | [ NAU8810_REG_ALC1ENHAN1] = 0x0010,
|
| 112 | [ NAU8810_REG_ALC1ENHAN2] = 0x0000,
|
| 113 | [ NAU8810_REG_MISCCTL] = 0x0000,
|
| 114 | [ NAU8810_REG_OUTTIEOFF] = 0x0000,
|
| 115 | [ NAU8810_REG_AGCP2POUT] = 0x0000,
|
| 116 | [ NAU8810_REG_AGCPOUT] = 0x0000,
|
| 117 | [ NAU8810_REG_AMTCTL] = 0x0000,
|
| 118 | [ NAU8810_REG_OUTTIEOFFMAN] = 0x0000,
|
| 119 | };
|
| 120 |
|
| 121 | static bool nau8810_readable_reg(struct device *dev, unsigned int reg)
|
| 122 | {
|
| 123 | switch (reg) {
|
| 124 | case NAU8810_REG_RESET ... NAU8810_REG_SMPLR:
|
| 125 | case NAU8810_REG_DAC ... NAU8810_REG_DACGAIN:
|
| 126 | case NAU8810_REG_ADC ... NAU8810_REG_ADCGAIN:
|
| 127 | case NAU8810_REG_EQ1 ... NAU8810_REG_EQ5:
|
| 128 | case NAU8810_REG_DACLIM1 ... NAU8810_REG_DACLIM2:
|
| 129 | case NAU8810_REG_NOTCH1 ... NAU8810_REG_NOTCH4:
|
| 130 | case NAU8810_REG_ALC1 ... NAU8810_REG_ATTEN:
|
| 131 | case NAU8810_REG_INPUT_SIGNAL ... NAU8810_REG_PGAGAIN:
|
| 132 | case NAU8810_REG_ADCBOOST:
|
| 133 | case NAU8810_REG_OUTPUT ... NAU8810_REG_SPKMIX:
|
| 134 | case NAU8810_REG_SPKGAIN:
|
| 135 | case NAU8810_REG_MONOMIX:
|
| 136 | case NAU8810_REG_POWER4 ... NAU8810_REG_TSLOTCTL2:
|
| 137 | case NAU8810_REG_DEVICE_REVID ... NAU8810_REG_RESERVE:
|
| 138 | case NAU8810_REG_OUTCTL ... NAU8810_REG_ALC1ENHAN2:
|
| 139 | case NAU8810_REG_MISCCTL:
|
| 140 | case NAU8810_REG_OUTTIEOFF ... NAU8810_REG_OUTTIEOFFMAN:
|
| 141 | return true;
|
| 142 | default:
|
| 143 | return false;
|
| 144 | }
|
| 145 | }
|
| 146 |
|
| 147 | static bool nau8810_writeable_reg(struct device *dev, unsigned int reg)
|
| 148 | {
|
| 149 | switch (reg) {
|
| 150 | case NAU8810_REG_RESET ... NAU8810_REG_SMPLR:
|
| 151 | case NAU8810_REG_DAC ... NAU8810_REG_DACGAIN:
|
| 152 | case NAU8810_REG_ADC ... NAU8810_REG_ADCGAIN:
|
| 153 | case NAU8810_REG_EQ1 ... NAU8810_REG_EQ5:
|
| 154 | case NAU8810_REG_DACLIM1 ... NAU8810_REG_DACLIM2:
|
| 155 | case NAU8810_REG_NOTCH1 ... NAU8810_REG_NOTCH4:
|
| 156 | case NAU8810_REG_ALC1 ... NAU8810_REG_ATTEN:
|
| 157 | case NAU8810_REG_INPUT_SIGNAL ... NAU8810_REG_PGAGAIN:
|
| 158 | case NAU8810_REG_ADCBOOST:
|
| 159 | case NAU8810_REG_OUTPUT ... NAU8810_REG_SPKMIX:
|
| 160 | case NAU8810_REG_SPKGAIN:
|
| 161 | case NAU8810_REG_MONOMIX:
|
| 162 | case NAU8810_REG_POWER4 ... NAU8810_REG_TSLOTCTL2:
|
| 163 | case NAU8810_REG_OUTCTL ... NAU8810_REG_ALC1ENHAN2:
|
| 164 | case NAU8810_REG_MISCCTL:
|
| 165 | case NAU8810_REG_OUTTIEOFF ... NAU8810_REG_OUTTIEOFFMAN:
|
| 166 | return true;
|
| 167 | default:
|
| 168 | return false;
|
| 169 | }
|
| 170 | }
|
| 171 |
|
| 172 | static int nau8810_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int value)
|
| 173 | {
|
| 174 | int ret;
|
| 175 |
|
| 176 | u8 data[2];
|
| 177 |
|
| 178 | data[0] = ((reg << 1)|((value>>8)&0x1)) & 0xff;
|
| 179 | data[1] = value & 0xff;
|
| 180 | printk(KERN_INFO "nau8810_write reg 0x%x= 0x%x\n", reg, value);
|
| 181 | if (codec->hw_write(codec->control_data, data, 2) == 2)
|
| 182 | {
|
| 183 | return 0;
|
| 184 | } else {
|
| 185 | return -EIO;
|
| 186 | }
|
| 187 | }
|
| 188 |
|
| 189 | static unsigned int nau8810_read(struct snd_soc_codec *codec, unsigned int reg)
|
| 190 | {
|
| 191 | int ret;
|
| 192 | //u16 val;
|
| 193 | u8 fixed_reg = 0;
|
| 194 | fixed_reg = (u8)((reg << 1) & 0xfe);
|
| 195 | u8 data[2];
|
| 196 | struct i2c_client *i2c = (struct i2c_client *)codec->control_data;
|
| 197 | struct i2c_msg xfer[2] = {
|
| 198 | {
|
| 199 | .addr = i2c->addr,
|
| 200 | .flags = 0,
|
| 201 | .buf = &fixed_reg,
|
| 202 | .len = 1
|
| 203 | },
|
| 204 | {
|
| 205 | .addr = i2c->addr,
|
| 206 | .flags = I2C_M_RD,
|
| 207 | .buf = data,
|
| 208 | .len = 2
|
| 209 | }
|
| 210 |
|
| 211 | };
|
| 212 |
|
| 213 | ret = i2c_transfer(i2c->adapter, xfer, ARRAY_SIZE(xfer));
|
| 214 | printk(KERN_INFO "nau8810_read reg 0x%x=0x%x\n", reg,(((data[0]<<8) & 0x100) |(data[1] & 0xff)));
|
| 215 |
|
| 216 | return (ret == 2) ? (((data[0]<<8) & 0x100) |(data[1] & 0xff)) : -1;
|
| 217 | }
|
| 218 |
|
| 219 | static bool nau8810_volatile_reg(struct device *dev, unsigned int reg)
|
| 220 | {
|
| 221 | switch (reg) {
|
| 222 | case NAU8810_REG_RESET:
|
| 223 | case NAU8810_REG_DEVICE_REVID ... NAU8810_REG_RESERVE:
|
| 224 | return true;
|
| 225 | default:
|
| 226 | return false;
|
| 227 | }
|
| 228 | }
|
| 229 |
|
| 230 | /* The EQ parameters get function is to get the 5 band equalizer control.
|
| 231 | * The regmap raw read can't work here because regmap doesn't provide
|
| 232 | * value format for value width of 9 bits. Therefore, the driver reads data
|
| 233 | * from cache and makes value format according to the endianness of
|
| 234 | * bytes type control element.
|
| 235 | */
|
| 236 | static int nau8810_eq_get(struct snd_kcontrol *kcontrol,
|
| 237 | struct snd_ctl_elem_value *ucontrol)
|
| 238 | {
|
| 239 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
|
| 240 | struct nau8810 *nau8810 = snd_soc_codec_get_drvdata(codec);
|
| 241 | unsigned long params = kcontrol->private_value;
|
| 242 | int i, reg, reg_val;
|
| 243 | u16 *val;
|
| 244 |
|
| 245 | val = (u16 *)ucontrol->value.bytes.data;
|
| 246 | reg = NAU8810_REG_EQ1;
|
| 247 | for (i = 0; i < params / sizeof(u16); i++) {
|
| 248 | reg_val = nau8810_read(codec, reg + i);
|
| 249 | /* conversion of 16-bit integers between native CPU format
|
| 250 | * and big endian format
|
| 251 | */
|
| 252 | reg_val = cpu_to_be16(reg_val);
|
| 253 | memcpy(val + i, ®_val, sizeof(reg_val));
|
| 254 | }
|
| 255 |
|
| 256 | return 0;
|
| 257 | }
|
| 258 |
|
| 259 | /* The EQ parameters put function is to make configuration of 5 band equalizer
|
| 260 | * control. These configuration includes central frequency, equalizer gain,
|
| 261 | * cut-off frequency, bandwidth control, and equalizer path.
|
| 262 | * The regmap raw write can't work here because regmap doesn't provide
|
| 263 | * register and value format for register with address 7 bits and value 9 bits.
|
| 264 | * Therefore, the driver makes value format according to the endianness of
|
| 265 | * bytes type control element and writes data to codec.
|
| 266 | */
|
| 267 | static int nau8810_eq_put(struct snd_kcontrol *kcontrol,
|
| 268 | struct snd_ctl_elem_value *ucontrol)
|
| 269 | {
|
| 270 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
|
| 271 | struct nau8810 *nau8810 = snd_soc_codec_get_drvdata(codec);
|
| 272 |
|
| 273 | unsigned long params = kcontrol->private_value;
|
| 274 | void *data;
|
| 275 | u16 *val, value;
|
| 276 | int i, reg, ret;
|
| 277 |
|
| 278 | data = kmemdup(ucontrol->value.bytes.data,
|
| 279 | params, GFP_KERNEL | GFP_DMA);
|
| 280 | if (!data)
|
| 281 | return -ENOMEM;
|
| 282 |
|
| 283 | val = (u16 *)data;
|
| 284 | reg = NAU8810_REG_EQ1;
|
| 285 | for (i = 0; i < params / sizeof(u16); i++) {
|
| 286 | /* conversion of 16-bit integers between native CPU format
|
| 287 | * and big endian format
|
| 288 | */
|
| 289 | value = be16_to_cpu(*(val + i));
|
| 290 | ret = nau8810_write(codec, reg + i, value);
|
| 291 | if (ret) {
|
| 292 | dev_err(codec->dev, "EQ configuration fail, register: %x ret: %d\n",
|
| 293 | reg + i, ret);
|
| 294 | kfree(data);
|
| 295 | return ret;
|
| 296 | }
|
| 297 | }
|
| 298 | kfree(data);
|
| 299 |
|
| 300 | return 0;
|
| 301 | }
|
| 302 |
|
| 303 | static const char * const nau8810_companding[] = {
|
| 304 | "Off", "NC", "u-law", "A-law" };
|
| 305 |
|
| 306 | static const struct soc_enum nau8810_companding_adc_enum =
|
| 307 | SOC_ENUM_SINGLE(NAU8810_REG_COMP, NAU8810_ADCCM_SFT,
|
| 308 | ARRAY_SIZE(nau8810_companding), nau8810_companding);
|
| 309 |
|
| 310 | static const struct soc_enum nau8810_companding_dac_enum =
|
| 311 | SOC_ENUM_SINGLE(NAU8810_REG_COMP, NAU8810_DACCM_SFT,
|
| 312 | ARRAY_SIZE(nau8810_companding), nau8810_companding);
|
| 313 |
|
| 314 | static const char * const nau8810_deemp[] = {
|
| 315 | "None", "32kHz", "44.1kHz", "48kHz" };
|
| 316 |
|
| 317 | static const struct soc_enum nau8810_deemp_enum =
|
| 318 | SOC_ENUM_SINGLE(NAU8810_REG_DAC, NAU8810_DEEMP_SFT,
|
| 319 | ARRAY_SIZE(nau8810_deemp), nau8810_deemp);
|
| 320 |
|
| 321 | static const char * const nau8810_eqmode[] = {"Capture", "Playback" };
|
| 322 |
|
| 323 | static const struct soc_enum nau8810_eqmode_enum =
|
| 324 | SOC_ENUM_SINGLE(NAU8810_REG_EQ1, NAU8810_EQM_SFT,
|
| 325 | ARRAY_SIZE(nau8810_eqmode), nau8810_eqmode);
|
| 326 |
|
| 327 | static const char * const nau8810_alc[] = {"Normal", "Limiter" };
|
| 328 |
|
| 329 | static const struct soc_enum nau8810_alc_enum =
|
| 330 | SOC_ENUM_SINGLE(NAU8810_REG_ALC3, NAU8810_ALCM_SFT,
|
| 331 | ARRAY_SIZE(nau8810_alc), nau8810_alc);
|
| 332 |
|
| 333 | static const DECLARE_TLV_DB_SCALE(digital_tlv, -12750, 50, 1);
|
| 334 | static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
|
| 335 | static const DECLARE_TLV_DB_SCALE(inpga_tlv, -1200, 75, 0);
|
| 336 | static const DECLARE_TLV_DB_SCALE(spk_tlv, -5700, 100, 0);
|
| 337 |
|
| 338 | static const struct snd_kcontrol_new nau8810_snd_controls[] = {
|
| 339 | SOC_ENUM("ADC Companding", nau8810_companding_adc_enum),
|
| 340 | SOC_ENUM("DAC Companding", nau8810_companding_dac_enum),
|
| 341 | SOC_ENUM("DAC De-emphasis", nau8810_deemp_enum),
|
| 342 |
|
| 343 | SOC_ENUM("EQ Function", nau8810_eqmode_enum),
|
| 344 | SND_SOC_BYTES_EXT("EQ Parameters", 10,
|
| 345 | nau8810_eq_get, nau8810_eq_put),
|
| 346 |
|
| 347 | SOC_SINGLE("VER NAU8810", NAU8810_REG_DEVICE_REVID,
|
| 348 | 0, 0xff, 0),
|
| 349 | SOC_SINGLE("DAC Inversion Switch", NAU8810_REG_DAC,
|
| 350 | NAU8810_DACPL_SFT, 1, 0),
|
| 351 | SOC_SINGLE_TLV("Playback Volume", NAU8810_REG_DACGAIN,
|
| 352 | NAU8810_DACGAIN_SFT, 0xff, 0, digital_tlv),
|
| 353 |
|
| 354 | SOC_SINGLE("High Pass Filter Switch", NAU8810_REG_ADC,
|
| 355 | NAU8810_HPFEN_SFT, 1, 0),
|
| 356 | SOC_SINGLE("High Pass Cut Off", NAU8810_REG_ADC,
|
| 357 | NAU8810_HPF_SFT, 0x7, 0),
|
| 358 |
|
| 359 | SOC_SINGLE("ADC Inversion Switch", NAU8810_REG_ADC,
|
| 360 | NAU8810_ADCPL_SFT, 1, 0),
|
| 361 | SOC_SINGLE_TLV("Capture Volume", NAU8810_REG_ADCGAIN,
|
| 362 | NAU8810_ADCGAIN_SFT, 0xff, 0, digital_tlv),
|
| 363 |
|
| 364 | SOC_SINGLE_TLV("EQ1 Volume", NAU8810_REG_EQ1,
|
| 365 | NAU8810_EQ1GC_SFT, 0x18, 1, eq_tlv),
|
| 366 | SOC_SINGLE_TLV("EQ2 Volume", NAU8810_REG_EQ2,
|
| 367 | NAU8810_EQ2GC_SFT, 0x18, 1, eq_tlv),
|
| 368 | SOC_SINGLE_TLV("EQ3 Volume", NAU8810_REG_EQ3,
|
| 369 | NAU8810_EQ3GC_SFT, 0x18, 1, eq_tlv),
|
| 370 | SOC_SINGLE_TLV("EQ4 Volume", NAU8810_REG_EQ4,
|
| 371 | NAU8810_EQ4GC_SFT, 0x18, 1, eq_tlv),
|
| 372 | SOC_SINGLE_TLV("EQ5 Volume", NAU8810_REG_EQ5,
|
| 373 | NAU8810_EQ5GC_SFT, 0x18, 1, eq_tlv),
|
| 374 |
|
| 375 | SOC_SINGLE("DAC Limiter Switch", NAU8810_REG_DACLIM1,
|
| 376 | NAU8810_DACLIMEN_SFT, 1, 0),
|
| 377 | SOC_SINGLE("DAC Limiter Decay", NAU8810_REG_DACLIM1,
|
| 378 | NAU8810_DACLIMDCY_SFT, 0xf, 0),
|
| 379 | SOC_SINGLE("DAC Limiter Attack", NAU8810_REG_DACLIM1,
|
| 380 | NAU8810_DACLIMATK_SFT, 0xf, 0),
|
| 381 | SOC_SINGLE("DAC Limiter Threshold", NAU8810_REG_DACLIM2,
|
| 382 | NAU8810_DACLIMTHL_SFT, 0x7, 0),
|
| 383 | SOC_SINGLE("DAC Limiter Boost", NAU8810_REG_DACLIM2,
|
| 384 | NAU8810_DACLIMBST_SFT, 0xf, 0),
|
| 385 |
|
| 386 | SOC_ENUM("ALC Mode", nau8810_alc_enum),
|
| 387 | SOC_SINGLE("ALC Enable Switch", NAU8810_REG_ALC1,
|
| 388 | NAU8810_ALCEN_SFT, 1, 0),
|
| 389 | SOC_SINGLE("ALC Max Volume", NAU8810_REG_ALC1,
|
| 390 | NAU8810_ALCMXGAIN_SFT, 0x7, 0),
|
| 391 | SOC_SINGLE("ALC Min Volume", NAU8810_REG_ALC1,
|
| 392 | NAU8810_ALCMINGAIN_SFT, 0x7, 0),
|
| 393 | SOC_SINGLE("ALC ZC Switch", NAU8810_REG_ALC2,
|
| 394 | NAU8810_ALCZC_SFT, 1, 0),
|
| 395 | SOC_SINGLE("ALC Hold", NAU8810_REG_ALC2,
|
| 396 | NAU8810_ALCHT_SFT, 0xf, 0),
|
| 397 | SOC_SINGLE("ALC Target", NAU8810_REG_ALC2,
|
| 398 | NAU8810_ALCSL_SFT, 0xf, 0),
|
| 399 | SOC_SINGLE("ALC Decay", NAU8810_REG_ALC3,
|
| 400 | NAU8810_ALCDCY_SFT, 0xf, 0),
|
| 401 | SOC_SINGLE("ALC Attack", NAU8810_REG_ALC3,
|
| 402 | NAU8810_ALCATK_SFT, 0xf, 0),
|
| 403 | SOC_SINGLE("ALC Noise Gate Switch", NAU8810_REG_NOISEGATE,
|
| 404 | NAU8810_ALCNEN_SFT, 1, 0),
|
| 405 | SOC_SINGLE("ALC Noise Gate Threshold", NAU8810_REG_NOISEGATE,
|
| 406 | NAU8810_ALCNTH_SFT, 0x7, 0),
|
| 407 |
|
| 408 | SOC_SINGLE("PGA ZC Switch", NAU8810_REG_PGAGAIN,
|
| 409 | NAU8810_PGAZC_SFT, 1, 0),
|
| 410 | SOC_SINGLE_TLV("PGA Volume", NAU8810_REG_PGAGAIN,
|
| 411 | NAU8810_PGAGAIN_SFT, 0x3f, 0, inpga_tlv),
|
| 412 |
|
| 413 | SOC_SINGLE("Speaker ZC Switch", NAU8810_REG_SPKGAIN,
|
| 414 | NAU8810_SPKZC_SFT, 1, 0),
|
| 415 | SOC_SINGLE("Speaker Mute Switch", NAU8810_REG_SPKGAIN,
|
| 416 | NAU8810_SPKMT_SFT, 1, 0),
|
| 417 | SOC_SINGLE_TLV("Speaker Volume", NAU8810_REG_SPKGAIN,
|
| 418 | NAU8810_SPKGAIN_SFT, 0x3f, 0, spk_tlv),
|
| 419 | SOC_SINGLE("Capture Boost(+20dB)", NAU8810_REG_ADCBOOST,
|
| 420 | NAU8810_PGABST_SFT, 1, 0),
|
| 421 | SOC_SINGLE("Mono Mute Switch", NAU8810_REG_MONOMIX,
|
| 422 | NAU8810_MOUTMXMT_SFT, 1, 0),
|
| 423 |
|
| 424 | SOC_SINGLE("DAC Oversampling Rate(128x) Switch", NAU8810_REG_DAC,
|
| 425 | NAU8810_DACOS_SFT, 1, 0),
|
| 426 | SOC_SINGLE("ADC Oversampling Rate(128x) Switch", NAU8810_REG_ADC,
|
| 427 | NAU8810_ADCOS_SFT, 1, 0),
|
| 428 | };
|
| 429 |
|
| 430 | /* Speaker Output Mixer */
|
| 431 | static const struct snd_kcontrol_new nau8810_speaker_mixer_controls[] = {
|
| 432 | SOC_DAPM_SINGLE("Line Bypass Switch", NAU8810_REG_SPKMIX,
|
| 433 | NAU8810_BYPSPK_SFT, 1, 0),
|
| 434 | SOC_DAPM_SINGLE("PCM Playback Switch", NAU8810_REG_SPKMIX,
|
| 435 | NAU8810_DACSPK_SFT, 1, 0),
|
| 436 | };
|
| 437 |
|
| 438 | /* Mono Output Mixer */
|
| 439 | static const struct snd_kcontrol_new nau8810_mono_mixer_controls[] = {
|
| 440 | SOC_DAPM_SINGLE("Line Bypass Switch", NAU8810_REG_MONOMIX,
|
| 441 | NAU8810_BYPMOUT_SFT, 1, 0),
|
| 442 | SOC_DAPM_SINGLE("PCM Playback Switch", NAU8810_REG_MONOMIX,
|
| 443 | NAU8810_DACMOUT_SFT, 1, 0),
|
| 444 | };
|
| 445 |
|
| 446 | /* PGA Mute */
|
| 447 | static const struct snd_kcontrol_new nau8810_inpga_mute[] = {
|
| 448 | SOC_DAPM_SINGLE("PGA Mute Switch", NAU8810_REG_PGAGAIN,
|
| 449 | NAU8810_PGAMT_SFT, 1, 0),
|
| 450 | };
|
| 451 |
|
| 452 | /* Input PGA */
|
| 453 | static const struct snd_kcontrol_new nau8810_inpga[] = {
|
| 454 | SOC_DAPM_SINGLE("MicN Switch", NAU8810_REG_INPUT_SIGNAL,
|
| 455 | NAU8810_NMICPGA_SFT, 1, 0),
|
| 456 | SOC_DAPM_SINGLE("MicP Switch", NAU8810_REG_INPUT_SIGNAL,
|
| 457 | NAU8810_PMICPGA_SFT, 1, 0),
|
| 458 | };
|
| 459 |
|
| 460 | /* Mic Input boost vol */
|
| 461 | static const struct snd_kcontrol_new nau8810_mic_boost_controls[]= {
|
| 462 | SOC_DAPM_SINGLE("Mic Volume", NAU8810_REG_ADCBOOST,
|
| 463 | NAU8810_PMICBSTGAIN_SFT, 0x7, 0)
|
| 464 | };
|
| 465 |
|
| 466 | /* Loopback Switch */
|
| 467 | static const struct snd_kcontrol_new nau8810_loopback =
|
| 468 | SOC_DAPM_SINGLE("Switch", NAU8810_REG_COMP,
|
| 469 | NAU8810_ADDAP_SFT, 1, 0);
|
| 470 |
|
| 471 | static int check_mclk_select_pll(struct snd_soc_dapm_widget *source,
|
| 472 | struct snd_soc_dapm_widget *sink)
|
| 473 | {
|
| 474 | struct nau8810 *nau8810 = snd_soc_codec_get_drvdata(source->codec);
|
| 475 | unsigned int value;
|
| 476 |
|
| 477 | value = nau8810_read(source->codec, NAU8810_REG_CLOCK);
|
| 478 | return (value & NAU8810_CLKM_MASK);
|
| 479 | }
|
| 480 |
|
| 481 | static const struct snd_soc_dapm_widget nau8810_dapm_widgets[] = {
|
| 482 |
|
| 483 | SND_SOC_DAPM_MIXER("Speaker Mixer", NAU8810_REG_POWER3,
|
| 484 | NAU8810_SPKMX_EN_SFT, 0, &nau8810_speaker_mixer_controls[0],
|
| 485 | ARRAY_SIZE(nau8810_speaker_mixer_controls)),
|
| 486 | SND_SOC_DAPM_MIXER("Mono Mixer", NAU8810_REG_POWER3,
|
| 487 | NAU8810_MOUTMX_EN_SFT, 0, &nau8810_mono_mixer_controls[0],
|
| 488 | ARRAY_SIZE(nau8810_mono_mixer_controls)),
|
| 489 | SND_SOC_DAPM_DAC("DAC", "HiFi Playback", NAU8810_REG_POWER3,
|
| 490 | NAU8810_DAC_EN_SFT, 0),
|
| 491 | SND_SOC_DAPM_ADC("ADC", "HiFi Capture", NAU8810_REG_POWER2,
|
| 492 | NAU8810_ADC_EN_SFT, 0),
|
| 493 | SND_SOC_DAPM_PGA("SpkN Out", NAU8810_REG_POWER3,
|
| 494 | NAU8810_NSPK_EN_SFT, 0, NULL, 0),
|
| 495 | SND_SOC_DAPM_PGA("SpkP Out", NAU8810_REG_POWER3,
|
| 496 | NAU8810_PSPK_EN_SFT, 0, NULL, 0),
|
| 497 | SND_SOC_DAPM_PGA("Mono Out", NAU8810_REG_POWER3,
|
| 498 | NAU8810_MOUT_EN_SFT, 0, NULL, 0),
|
| 499 |
|
| 500 | SND_SOC_DAPM_MIXER("Input PGA", NAU8810_REG_POWER2,
|
| 501 | NAU8810_PGA_EN_SFT, 0, nau8810_inpga,
|
| 502 | ARRAY_SIZE(nau8810_inpga)),
|
| 503 |
|
| 504 | //SND_SOC_DAPM_MIXER("Linein Path", NAU8810_REG_ADCBOOST,
|
| 505 | //NAU8810_PMICBSTGAIN_SFT, 0, nau8810_mic_boost_controls,
|
| 506 | //ARRAY_SIZE(nau8810_mic_boost_controls)),
|
| 507 |
|
| 508 | SND_SOC_DAPM_MIXER("Input Boost Stage", NAU8810_REG_POWER2,
|
| 509 | NAU8810_BST_EN_SFT, 0, nau8810_inpga_mute,
|
| 510 | ARRAY_SIZE(nau8810_inpga_mute)),
|
| 511 |
|
| 512 | SND_SOC_DAPM_SUPPLY("Mic Bias", NAU8810_REG_POWER1,
|
| 513 | NAU8810_MICBIAS_EN_SFT, 0, NULL, 0),
|
| 514 | SND_SOC_DAPM_SUPPLY("PLL", NAU8810_REG_POWER1,
|
| 515 | NAU8810_PLL_EN_SFT, 0, NULL, 0),
|
| 516 |
|
| 517 | SND_SOC_DAPM_SWITCH("Digital Loopback", SND_SOC_NOPM, 0, 0,
|
| 518 | &nau8810_loopback),
|
| 519 |
|
| 520 | SND_SOC_DAPM_INPUT("MICN"),
|
| 521 | SND_SOC_DAPM_INPUT("MICP"),
|
| 522 | SND_SOC_DAPM_OUTPUT("MONOOUT"),
|
| 523 | SND_SOC_DAPM_OUTPUT("SPKOUTP"),
|
| 524 | SND_SOC_DAPM_OUTPUT("SPKOUTN"),
|
| 525 | };
|
| 526 |
|
| 527 | static const struct snd_soc_dapm_route nau8810_dapm_routes[] = {
|
| 528 | {"DAC", NULL, "PLL", check_mclk_select_pll},
|
| 529 |
|
| 530 | /* Mono output mixer */
|
| 531 | {"Mono Mixer", "PCM Playback Switch", "DAC"},
|
| 532 | {"Mono Mixer", "Line Bypass Switch", "Input Boost Stage"},
|
| 533 |
|
| 534 | /* Speaker output mixer */
|
| 535 | {"Speaker Mixer", "PCM Playback Switch", "DAC"},//==>
|
| 536 | {"Speaker Mixer", "Line Bypass Switch", "Input Boost Stage"},
|
| 537 |
|
| 538 | /* Outputs */
|
| 539 | {"Mono Out", NULL, "Mono Mixer"},
|
| 540 | {"MONOOUT", NULL, "Mono Out"},
|
| 541 | {"SpkN Out", NULL, "Speaker Mixer"},
|
| 542 | {"SpkP Out", NULL, "Speaker Mixer"},
|
| 543 | {"SPKOUTN", NULL, "SpkN Out"},
|
| 544 | {"SPKOUTP", NULL, "SpkP Out"},
|
| 545 |
|
| 546 | /* Input Boost Stage */
|
| 547 | {"ADC", NULL, "Input Boost Stage"},
|
| 548 | {"ADC", NULL, "PLL", check_mclk_select_pll},
|
| 549 | {"Input Boost Stage", NULL, "Input PGA"},
|
| 550 | {"Input Boost Stage", NULL, "MICP"},
|
| 551 | {"Input Boost Stage", NULL, "MICN"},
|
| 552 | /* Linein */
|
| 553 | //{"ADC",NULL, "Linein Path"},
|
| 554 | //{"Linein Path", NULL, "Mic Bias"},
|
| 555 | //{"Linein Path","Mic Volume", "MICP"},
|
| 556 |
|
| 557 | /* Input PGA */
|
| 558 | {"Input PGA", NULL, "Mic Bias"},
|
| 559 | {"Input PGA", "MicN Switch", "MICN"},
|
| 560 | {"Input PGA", "MicP Switch", "MICP"},
|
| 561 |
|
| 562 | /* Digital Looptack */
|
| 563 | {"Digital Loopback", "Switch", "ADC"},
|
| 564 | {"DAC", NULL, "Digital Loopback"},
|
| 565 | };
|
| 566 |
|
| 567 | static int nau8810_set_sysclk(struct snd_soc_dai *dai,
|
| 568 | int clk_id, unsigned int freq, int dir)
|
| 569 | {
|
| 570 | struct snd_soc_codec *codec = dai->codec;
|
| 571 | struct nau8810 *nau8810 = snd_soc_codec_get_drvdata(codec);
|
| 572 |
|
| 573 | nau8810->clk_id = clk_id;
|
| 574 | nau8810->sysclk = freq;
|
| 575 | dev_dbg(nau8810->dev, "master sysclk %dHz, source %s\n",
|
| 576 | freq, clk_id == NAU8810_SCLK_PLL ? "PLL" : "MCLK");
|
| 577 |
|
| 578 | return 0;
|
| 579 | }
|
| 580 |
|
| 581 | static int nau88l0_calc_pll(unsigned int pll_in,
|
| 582 | unsigned int fs, struct nau8810_pll *pll_param)
|
| 583 | {
|
| 584 | u64 f2, f2_max, pll_ratio;
|
| 585 | int i, scal_sel;
|
| 586 |
|
| 587 | if (pll_in > NAU_PLL_REF_MAX || pll_in < NAU_PLL_REF_MIN)
|
| 588 | return -EINVAL;
|
| 589 |
|
| 590 | f2_max = 0;
|
| 591 | scal_sel = ARRAY_SIZE(nau8810_mclk_scaler);
|
| 592 | for (i = 0; i < ARRAY_SIZE(nau8810_mclk_scaler); i++) {
|
| 593 | f2 = 256 * fs * 4 * nau8810_mclk_scaler[i] / 10;
|
| 594 | if (f2 > NAU_PLL_FREQ_MIN && f2 < NAU_PLL_FREQ_MAX &&
|
| 595 | f2_max < f2) {
|
| 596 | f2_max = f2;
|
| 597 | scal_sel = i;
|
| 598 | }
|
| 599 | }
|
| 600 | if (ARRAY_SIZE(nau8810_mclk_scaler) == scal_sel)
|
| 601 | return -EINVAL;
|
| 602 | pll_param->mclk_scaler = scal_sel;
|
| 603 | f2 = f2_max;
|
| 604 |
|
| 605 | /* Calculate the PLL 4-bit integer input and the PLL 24-bit fractional
|
| 606 | * input; round up the 24+4bit.
|
| 607 | */
|
| 608 | pll_ratio = div_u64(f2 << 28, pll_in);
|
| 609 | pll_param->pre_factor = 0;
|
| 610 | if (((pll_ratio >> 28) & 0xF) < NAU_PLL_OPTOP_MIN) {
|
| 611 | pll_ratio <<= 1;
|
| 612 | pll_param->pre_factor = 1;
|
| 613 | }
|
| 614 | pll_param->pll_int = (pll_ratio >> 28) & 0xF;
|
| 615 | pll_param->pll_frac = ((pll_ratio & 0xFFFFFFF) >> 4);
|
| 616 |
|
| 617 | return 0;
|
| 618 | }
|
| 619 |
|
| 620 | static int nau8810_set_pll(struct snd_soc_dai *codec_dai, int pll_id,
|
| 621 | int source, unsigned int freq_in, unsigned int freq_out)
|
| 622 | {
|
| 623 | struct snd_soc_codec *codec = codec_dai->codec;
|
| 624 | struct nau8810 *nau8810 = snd_soc_codec_get_drvdata(codec);
|
| 625 | struct regmap *map = nau8810->regmap;
|
| 626 | struct nau8810_pll *pll_param = &nau8810->pll;
|
| 627 | int ret, fs;
|
| 628 |
|
| 629 | fs = freq_out / 256;
|
| 630 | ret = nau88l0_calc_pll(freq_in, fs, pll_param);
|
| 631 | if (ret < 0) {
|
| 632 | dev_err(nau8810->dev, "Unsupported input clock %d\n", freq_in);
|
| 633 | return ret;
|
| 634 | }
|
| 635 |
|
| 636 | dev_info(nau8810->dev, "pll_int=%x pll_frac=%x mclk_scaler=%x pre_factor=%x\n",
|
| 637 | pll_param->pll_int, pll_param->pll_frac, pll_param->mclk_scaler,
|
| 638 | pll_param->pre_factor);
|
| 639 |
|
| 640 | snd_soc_update_bits(codec, NAU8810_REG_PLLN,
|
| 641 | NAU8810_PLLMCLK_DIV2 | NAU8810_PLLN_MASK,
|
| 642 | (pll_param->pre_factor ? NAU8810_PLLMCLK_DIV2 : 0) |
|
| 643 | pll_param->pll_int);
|
| 644 | nau8810_write(codec, NAU8810_REG_PLLK1,
|
| 645 | (pll_param->pll_frac >> NAU8810_PLLK1_SFT) &
|
| 646 | NAU8810_PLLK1_MASK);
|
| 647 | nau8810_write(codec, NAU8810_REG_PLLK2,
|
| 648 | (pll_param->pll_frac >> NAU8810_PLLK2_SFT) &
|
| 649 | NAU8810_PLLK2_MASK);
|
| 650 | nau8810_write(codec, NAU8810_REG_PLLK3,
|
| 651 | pll_param->pll_frac & NAU8810_PLLK3_MASK);
|
| 652 | snd_soc_update_bits(codec, NAU8810_REG_CLOCK, NAU8810_MCLKSEL_MASK,
|
| 653 | pll_param->mclk_scaler << NAU8810_MCLKSEL_SFT);
|
| 654 | snd_soc_update_bits(codec, NAU8810_REG_CLOCK,
|
| 655 | NAU8810_CLKM_MASK, NAU8810_CLKM_PLL);
|
| 656 |
|
| 657 | return 0;
|
| 658 | }
|
| 659 |
|
| 660 | static int nau8810_set_dai_fmt(struct snd_soc_dai *codec_dai,
|
| 661 | unsigned int fmt)
|
| 662 | {
|
| 663 | struct snd_soc_codec *codec = codec_dai->codec;
|
| 664 | struct nau8810 *nau8810 = snd_soc_codec_get_drvdata(codec);
|
| 665 | u16 ctrl1_val = 0, ctrl2_val = 0;
|
| 666 |
|
| 667 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
|
| 668 | case SND_SOC_DAIFMT_CBM_CFM:
|
| 669 | ctrl2_val |= NAU8810_CLKIO_MASTER;
|
| 670 | break;
|
| 671 | case SND_SOC_DAIFMT_CBS_CFS:
|
| 672 | break;
|
| 673 | default:
|
| 674 | return -EINVAL;
|
| 675 | }
|
| 676 |
|
| 677 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
| 678 | case SND_SOC_DAIFMT_I2S:
|
| 679 | ctrl1_val |= NAU8810_AIFMT_I2S;
|
| 680 | break;
|
| 681 | case SND_SOC_DAIFMT_RIGHT_J:
|
| 682 | break;
|
| 683 | case SND_SOC_DAIFMT_LEFT_J:
|
| 684 | ctrl1_val |= NAU8810_AIFMT_LEFT;
|
| 685 | break;
|
| 686 | case SND_SOC_DAIFMT_DSP_A:
|
| 687 | ctrl1_val |= NAU8810_AIFMT_PCM_A;
|
| 688 | break;
|
| 689 | default:
|
| 690 | return -EINVAL;
|
| 691 | }
|
| 692 |
|
| 693 | switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
|
| 694 | case SND_SOC_DAIFMT_NB_NF:
|
| 695 | break;
|
| 696 | case SND_SOC_DAIFMT_IB_IF:
|
| 697 | ctrl1_val |= NAU8810_BCLKP_IB | NAU8810_FSP_IF;
|
| 698 | break;
|
| 699 | case SND_SOC_DAIFMT_IB_NF:
|
| 700 | ctrl1_val |= NAU8810_BCLKP_IB;
|
| 701 | break;
|
| 702 | case SND_SOC_DAIFMT_NB_IF:
|
| 703 | ctrl1_val |= NAU8810_FSP_IF;
|
| 704 | break;
|
| 705 | default:
|
| 706 | return -EINVAL;
|
| 707 | }
|
| 708 |
|
| 709 | snd_soc_update_bits(codec, NAU8810_REG_IFACE,
|
| 710 | NAU8810_AIFMT_MASK | NAU8810_FSP_IF |
|
| 711 | NAU8810_BCLKP_IB, ctrl1_val);
|
| 712 | snd_soc_update_bits(codec, NAU8810_REG_CLOCK,
|
| 713 | NAU8810_CLKIO_MASK, ctrl2_val);
|
| 714 |
|
| 715 | return 0;
|
| 716 | }
|
| 717 |
|
| 718 | static int nau8810_mclk_clkdiv(struct nau8810 *nau8810, int rate)
|
| 719 | {
|
| 720 | int i, sclk, imclk = rate * 256, div = 0;
|
| 721 |
|
| 722 | if (!nau8810->sysclk) {
|
| 723 | dev_err(nau8810->dev, "Make mclk div configuration fail because of invalid system clock\n");
|
| 724 | return -EINVAL;
|
| 725 | }
|
| 726 |
|
| 727 | /* Configure the master clock prescaler div to make system
|
| 728 | * clock to approximate the internal master clock (IMCLK);
|
| 729 | * and large or equal to IMCLK.
|
| 730 | */
|
| 731 | for (i = 1; i < ARRAY_SIZE(nau8810_mclk_scaler); i++) {
|
| 732 | sclk = (nau8810->sysclk * 10) /
|
| 733 | nau8810_mclk_scaler[i];
|
| 734 | if (sclk < imclk)
|
| 735 | break;
|
| 736 | div = i;
|
| 737 | }
|
| 738 | dev_dbg(nau8810->dev,
|
| 739 | "master clock prescaler %x for fs %d\n", div, rate);
|
| 740 |
|
| 741 | /* master clock from MCLK and disable PLL */
|
| 742 | snd_soc_update_bits(nau8810->codec, NAU8810_REG_CLOCK,
|
| 743 | NAU8810_MCLKSEL_MASK, (div << NAU8810_MCLKSEL_SFT));
|
| 744 | snd_soc_update_bits(nau8810->codec, NAU8810_REG_CLOCK,
|
| 745 | NAU8810_CLKM_MASK, NAU8810_CLKM_MCLK);
|
| 746 |
|
| 747 | return 0;
|
| 748 | }
|
| 749 |
|
| 750 | static int nau8810_pcm_hw_params(struct snd_pcm_substream *substream,
|
| 751 | struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
|
| 752 | {
|
| 753 | struct snd_soc_codec *codec = dai->codec;
|
| 754 | struct nau8810 *nau8810 = snd_soc_codec_get_drvdata(codec);
|
| 755 | int val_len = 0, val_rate = 0, ret = 0;
|
| 756 |
|
| 757 | switch (params_format(params)) {
|
| 758 | case SNDRV_PCM_FORMAT_S16_LE:
|
| 759 | val_len |= NAU8810_WLEN_16;
|
| 760 | break;
|
| 761 | case SNDRV_PCM_FORMAT_S24_LE:
|
| 762 | val_len |= NAU8810_WLEN_24;
|
| 763 | break;
|
| 764 | case SNDRV_PCM_FORMAT_S32_LE:
|
| 765 | val_len |= NAU8810_WLEN_32;
|
| 766 | break;
|
| 767 | default:
|
| 768 | dev_err(dai->dev, "Unknown data format %d\n",params_format(params));
|
| 769 | return -EINVAL;
|
| 770 | }
|
| 771 |
|
| 772 | switch (params_rate(params)) {
|
| 773 | case 8000:
|
| 774 | val_rate |= NAU8810_SMPLR_8K;
|
| 775 | break;
|
| 776 | case 11025:
|
| 777 | val_rate |= NAU8810_SMPLR_12K;
|
| 778 | break;
|
| 779 | case 16000:
|
| 780 | val_rate |= NAU8810_SMPLR_16K;
|
| 781 | break;
|
| 782 | case 22050:
|
| 783 | val_rate |= NAU8810_SMPLR_24K;
|
| 784 | break;
|
| 785 | case 32000:
|
| 786 | val_rate |= NAU8810_SMPLR_32K;
|
| 787 | break;
|
| 788 | case 44100:
|
| 789 | case 48000:
|
| 790 | break;
|
| 791 | }
|
| 792 | printk("[PLL]nau8810_pcm_hw_params val_len(0x%x) = 0x%x,val_rate(%d) = 0x%x\n",params_format(params),val_len,params_rate(params),val_rate);
|
| 793 | snd_soc_update_bits(codec, NAU8810_REG_IFACE,
|
| 794 | NAU8810_WLEN_MASK, val_len);
|
| 795 | snd_soc_update_bits(codec, NAU8810_REG_SMPLR,
|
| 796 | NAU8810_SMPLR_MASK, val_rate);
|
| 797 |
|
| 798 | /* If the master clock is from MCLK, provide the runtime FS for driver
|
| 799 | * to get the master clock prescaler configuration.
|
| 800 | */
|
| 801 | if (nau8810->clk_id == NAU8810_SCLK_MCLK) {
|
| 802 | ret = nau8810_mclk_clkdiv(nau8810, params_rate(params));
|
| 803 | if (ret < 0)
|
| 804 | dev_err(nau8810->dev, "MCLK div configuration fail\n");
|
| 805 | }
|
| 806 |
|
| 807 | return ret;
|
| 808 | }
|
| 809 |
|
| 810 | static int nau8810_set_bias_level(struct snd_soc_codec *codec,
|
| 811 | enum snd_soc_bias_level level)
|
| 812 | {
|
| 813 | // struct nau8810 *nau8810 = snd_soc_codec_get_drvdata(codec);
|
| 814 | // struct regmap *map = nau8810->regmap;
|
| 815 |
|
| 816 | switch (level) {
|
| 817 | case SND_SOC_BIAS_ON:
|
| 818 | case SND_SOC_BIAS_PREPARE:
|
| 819 | snd_soc_update_bits(codec, NAU8810_REG_POWER1,
|
| 820 | NAU8810_REFIMP_MASK, NAU8810_REFIMP_80K);
|
| 821 | break;
|
| 822 |
|
| 823 | case SND_SOC_BIAS_STANDBY:
|
| 824 | snd_soc_update_bits(codec, NAU8810_REG_POWER1,
|
| 825 | NAU8810_IOBUF_EN | NAU8810_ABIAS_EN,
|
| 826 | NAU8810_IOBUF_EN | NAU8810_ABIAS_EN);
|
| 827 |
|
| 828 | if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
|
| 829 | // regcache_sync(map);
|
| 830 | snd_soc_update_bits(codec, NAU8810_REG_POWER1,
|
| 831 | NAU8810_REFIMP_MASK, NAU8810_REFIMP_3K);
|
| 832 | mdelay(100);
|
| 833 | }
|
| 834 | snd_soc_update_bits(codec, NAU8810_REG_POWER1,
|
| 835 | NAU8810_REFIMP_MASK, NAU8810_REFIMP_300K);
|
| 836 | break;
|
| 837 |
|
| 838 | case SND_SOC_BIAS_OFF:
|
| 839 | nau8810_write(codec, NAU8810_REG_POWER1, 0);
|
| 840 | nau8810_write(codec, NAU8810_REG_POWER2, 0);
|
| 841 | nau8810_write(codec, NAU8810_REG_POWER3, 0);
|
| 842 | break;
|
| 843 | }
|
| 844 | codec->dapm.bias_level = level;
|
| 845 |
|
| 846 | return 0;
|
| 847 | }
|
| 848 |
|
| 849 | #define NAU8810_RATES (SNDRV_PCM_RATE_8000_48000)
|
| 850 |
|
| 851 | #define NAU8810_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
|
| 852 | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
|
| 853 |
|
| 854 | static const struct snd_soc_dai_ops nau8810_ops = {
|
| 855 | .hw_params = nau8810_pcm_hw_params,
|
| 856 | .set_fmt = nau8810_set_dai_fmt,
|
| 857 | .set_sysclk = nau8810_set_sysclk,
|
| 858 | .set_pll = nau8810_set_pll,
|
| 859 | };
|
| 860 |
|
| 861 | static struct snd_soc_dai_driver nau8810_dai = {
|
| 862 | .name = "nau8810-hifi",
|
| 863 | .playback = {
|
| 864 | .stream_name = "Playback",
|
| 865 | .channels_min = 1,
|
| 866 | .channels_max = 2, /* Only 1 channel of data */
|
| 867 | .rates = NAU8810_RATES,
|
| 868 | .formats = NAU8810_FORMATS,
|
| 869 | },
|
| 870 | .capture = {
|
| 871 | .stream_name = "Capture",
|
| 872 | .channels_min = 1,
|
| 873 | .channels_max = 2, /* Only 1 channel of data */
|
| 874 | .rates = NAU8810_RATES,
|
| 875 | .formats = NAU8810_FORMATS,
|
| 876 | },
|
| 877 | .ops = &nau8810_ops,
|
| 878 | .symmetric_rates = 1,
|
| 879 | };
|
| 880 |
|
| 881 | #if 0
|
| 882 | static const struct regmap_config nau8810_regmap_config = {
|
| 883 | .reg_bits = 7,
|
| 884 | .val_bits = 9,
|
| 885 |
|
| 886 | .max_register = NAU8810_REG_MAX,
|
| 887 | .readable_reg = nau8810_readable_reg,
|
| 888 | .writeable_reg = nau8810_writeable_reg,
|
| 889 | .volatile_reg = nau8810_volatile_reg,
|
| 890 |
|
| 891 | .cache_type = REGCACHE_RBTREE,
|
| 892 | .reg_defaults = nau8810_reg_defaults,
|
| 893 | .num_reg_defaults = ARRAY_SIZE(nau8810_reg_defaults),
|
| 894 | };
|
| 895 | #endif
|
| 896 |
|
| 897 | int nau8810_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack)
|
| 898 | {
|
| 899 | struct nau8810 *nau8810 = snd_soc_codec_get_drvdata(codec);
|
| 900 | int irq_mask, enable;
|
| 901 |
|
| 902 | nau8810->jack = jack;
|
| 903 | /* Send an initial empty report */
|
| 904 | snd_soc_jack_report(nau8810->jack, 0,
|
| 905 | SND_JACK_MICROPHONE);
|
| 906 |
|
| 907 | return 0;
|
| 908 | }
|
| 909 | EXPORT_SYMBOL(nau8810_mic_detect);
|
| 910 |
|
| 911 | static irqreturn_t nau8810_irq_handler(int irq, void *dev_id)
|
| 912 | {
|
| 913 | printk("howard nau8810_irq_handler irq=%d.\n", irq);
|
| 914 | pcu_clr_irq_pending(irq);
|
| 915 |
|
| 916 | return IRQ_WAKE_THREAD;
|
| 917 | }
|
| 918 |
|
| 919 | static irqreturn_t nau8810_irq(int irq, void *data)
|
| 920 | {
|
| 921 | struct device *dev = data;
|
| 922 | struct nau8810 *nau8810 = dev_get_drvdata(dev);
|
| 923 | int status = 0;
|
| 924 | int gpio_level;
|
| 925 |
|
| 926 | gpio_level = gpio_get_value_cansleep(ZX29_GPIO_51);
|
| 927 | printk( "[NAU8810]nau8810_irq Headset detected level (%d) 0:insert 1:removal\n",gpio_level);
|
| 928 |
|
| 929 | if (gpio_level == GPIO_LOW) {
|
| 930 | // ²åÈë¶ú»ú²Ù×÷
|
| 931 | status |= SND_JACK_HEADPHONE;
|
| 932 | zx29_gpio_set_inttype(ZX29_GPIO_51, IRQ_TYPE_EDGE_RISING);
|
| 933 | snd_soc_update_bits(nau8810->codec, NAU8810_REG_INPUT_SIGNAL,
|
| 934 | 0x7, 0x2);
|
| 935 | } else if (gpio_level == GPIO_HIGH) {
|
| 936 | //°Î³ö¶ú»ú²Ù×÷
|
| 937 | status = 0;
|
| 938 | zx29_gpio_set_inttype(ZX29_GPIO_51, IRQ_TYPE_EDGE_FALLING);
|
| 939 | snd_soc_update_bits(nau8810->codec, NAU8810_REG_INPUT_SIGNAL,
|
| 940 | 0x7, 0x5);
|
| 941 | } else {
|
| 942 | dev_err(dev, "[NAU8810]Failed to read gpio level\n");
|
| 943 | return IRQ_NONE;
|
| 944 | }
|
| 945 |
|
| 946 |
|
| 947 | snd_soc_jack_report(nau8810->jack, status,
|
| 948 | SND_JACK_HEADPHONE);
|
| 949 |
|
| 950 | return IRQ_HANDLED;
|
| 951 | }
|
| 952 |
|
| 953 | static int nau8810_codec_probe(struct snd_soc_codec *codec)
|
| 954 | {
|
| 955 | int ret = 0;
|
| 956 | struct nau8810 *nau8810 = snd_soc_codec_get_drvdata(codec);
|
| 957 |
|
| 958 | struct snd_soc_dapm_context *dapm = &codec->dapm;
|
| 959 | int i;
|
| 960 |
|
| 961 | dev_dbg(nau8810->dev, "## %s\n", __func__);
|
| 962 | codec->hw_write = (hw_write_t) i2c_master_send;
|
| 963 |
|
| 964 | codec->control_data = nau8810->control_data;
|
| 965 | codec->using_regmap = false;
|
| 966 |
|
| 967 | nau8810->codec = codec;
|
| 968 |
|
| 969 | nau8810_write(codec, NAU8810_REG_RESET, 0x00);
|
| 970 |
|
| 971 | // for headset detect
|
| 972 |
|
| 973 | ret = gpio_request(ZX29_GPIO_51, "earp_int");
|
| 974 | if (ret < 0) {
|
| 975 | dev_err(nau8810->dev, "not able to acquire irq gpio\n");
|
| 976 | return ret;
|
| 977 | }
|
| 978 | nau8810->irq_num = gpio_to_irq(ZX29_GPIO_51);
|
| 979 |
|
| 980 | zx29_gpio_config(ZX29_GPIO_51, GPIO51_EXT_INT4);
|
| 981 | zx29_gpio_set_inttype(ZX29_GPIO_51, IRQ_TYPE_LEVEL_LOW); //IRQ_TYPE_EDGE_FALLING IRQ_TYPE_EDGE_RISING
|
| 982 | zx29_gpio_pd_pu_set(ZX29_GPIO_51, IO_CFG_PULL_DISABLE);
|
| 983 |
|
| 984 | ret = irq_set_irq_wake(nau8810->irq_num, 1);
|
| 985 | //printk("[NAU8810]howard irq_set_irq_wake irq_num %d, ret %d\n", nau8810->irq_num, ret);
|
| 986 |
|
| 987 | pcu_clr_irq_pending(nau8810->irq_num);
|
| 988 |
|
| 989 | ret = request_threaded_irq(nau8810->irq_num, nau8810_irq_handler, nau8810_irq, IRQF_ONESHOT,
|
| 990 | "nau8810", codec->dev);
|
| 991 | if (ret != 0) {
|
| 992 | dev_err(codec->dev, "Failed to request IRQ %d\n", ret);
|
| 993 | /* Non-fatal */
|
| 994 | } else {
|
| 995 | printk("headset request_irq irq_num=%d.\n", nau8810->irq_num);
|
| 996 | /* Enable some IRQs by default */
|
| 997 | }
|
| 998 |
|
| 999 | // for ptt detect
|
| 1000 | #if 0
|
| 1001 | ret = gpio_request(ZX29_GPIO_51, "earp_int");
|
| 1002 | if (ret < 0) {
|
| 1003 | dev_err(nau8810->dev, "not able to acquire irq gpio\n");
|
| 1004 | return ret;
|
| 1005 | }
|
| 1006 | nau8810->irq_num = gpio_to_irq(ZX29_GPIO_51);
|
| 1007 |
|
| 1008 | zx29_gpio_config(ZX29_GPIO_51, GPIO51_EXT_INT4);
|
| 1009 | zx29_gpio_set_inttype(ZX29_GPIO_51, IRQ_TYPE_LEVEL_LOW); //IRQ_TYPE_EDGE_FALLING IRQ_TYPE_EDGE_RISING
|
| 1010 | zx29_gpio_pd_pu_set(ZX29_GPIO_51, IO_CFG_PULL_DISABLE);
|
| 1011 |
|
| 1012 | ret = irq_set_irq_wake(nau8810->irq_num, 1);
|
| 1013 | //printk("[NAU8810]howard irq_set_irq_wake irq_num %d, ret %d\n", nau8810->irq_num, ret);
|
| 1014 |
|
| 1015 | pcu_clr_irq_pending(nau8810->irq_num);
|
| 1016 |
|
| 1017 | ret = request_threaded_irq(nau8810->irq_num, nau8810_irq_handler, nau8810_irq, IRQF_ONESHOT,
|
| 1018 | "nau8810", codec->dev);
|
| 1019 | if (ret != 0) {
|
| 1020 | dev_err(codec->dev, "Failed to request IRQ %d\n", ret);
|
| 1021 | /* Non-fatal */
|
| 1022 | } else {
|
| 1023 | printk("headset request_irq irq_num=%d.\n", nau8810->irq_num);
|
| 1024 | /* Enable some IRQs by default */
|
| 1025 | }
|
| 1026 | #endif
|
| 1027 | return ret;
|
| 1028 | }
|
| 1029 |
|
| 1030 | static int nau8810_codec_remove(struct snd_soc_codec *codec)
|
| 1031 | {
|
| 1032 | //struct nau8810 *nau8810 = snd_soc_codec_get_drvdata(codec);
|
| 1033 |
|
| 1034 | /* power down chip */
|
| 1035 | nau8810_set_bias_level(codec, SND_SOC_BIAS_OFF);
|
| 1036 |
|
| 1037 | return 0;
|
| 1038 | }
|
| 1039 |
|
| 1040 | static struct snd_soc_codec_driver nau8810_codec_driver = {
|
| 1041 | .probe = nau8810_codec_probe,
|
| 1042 | .remove = nau8810_codec_remove,
|
| 1043 | .set_bias_level = nau8810_set_bias_level,
|
| 1044 |
|
| 1045 | .read = nau8810_read,
|
| 1046 | .write = nau8810_write,
|
| 1047 |
|
| 1048 | .reg_cache_size = ARRAY_SIZE(nau8810_reg_defaults),
|
| 1049 | .reg_word_size = sizeof(u16),
|
| 1050 | .reg_cache_default = nau8810_reg_defaults,
|
| 1051 | .reg_cache_step = 1,
|
| 1052 |
|
| 1053 | .controls = nau8810_snd_controls,
|
| 1054 | .num_controls = ARRAY_SIZE(nau8810_snd_controls),
|
| 1055 | .dapm_widgets = nau8810_dapm_widgets,
|
| 1056 | .num_dapm_widgets = ARRAY_SIZE(nau8810_dapm_widgets),
|
| 1057 | .dapm_routes = nau8810_dapm_routes,
|
| 1058 | .num_dapm_routes = ARRAY_SIZE(nau8810_dapm_routes),
|
| 1059 |
|
| 1060 | };
|
| 1061 |
|
| 1062 | static int nau8810_i2c_probe(struct i2c_client *i2c,
|
| 1063 | const struct i2c_device_id *id)
|
| 1064 | {
|
| 1065 | struct device *dev = &i2c->dev;
|
| 1066 | struct nau8810 *nau8810 = dev_get_platdata(dev);
|
| 1067 |
|
| 1068 | if (!nau8810) {
|
| 1069 | nau8810 = devm_kzalloc(dev, sizeof(*nau8810), GFP_KERNEL);
|
| 1070 | if (!nau8810)
|
| 1071 | return -ENOMEM;
|
| 1072 | }
|
| 1073 | i2c_set_clientdata(i2c, nau8810);
|
| 1074 |
|
| 1075 | //nau8810->regmap = devm_regmap_init_i2c(i2c, &nau8810_regmap_config);
|
| 1076 | //if (IS_ERR(nau8810->regmap))
|
| 1077 | //return PTR_ERR(nau8810->regmap);
|
| 1078 | nau8810->dev = dev;
|
| 1079 | nau8810->control_data = i2c;
|
| 1080 |
|
| 1081 |
|
| 1082 |
|
| 1083 | return snd_soc_register_codec(dev,
|
| 1084 | &nau8810_codec_driver, &nau8810_dai, 1);
|
| 1085 | }
|
| 1086 |
|
| 1087 | static int nau8810_i2c_remove(struct i2c_client *client)
|
| 1088 | {
|
| 1089 | snd_soc_unregister_codec(&client->dev);
|
| 1090 |
|
| 1091 | return 0;
|
| 1092 | }
|
| 1093 |
|
| 1094 | static const struct i2c_device_id nau8810_i2c_id[] = {
|
| 1095 | { "nau8810", 0 },
|
| 1096 | { }
|
| 1097 | };
|
| 1098 | MODULE_DEVICE_TABLE(i2c, nau8810_i2c_id);
|
| 1099 |
|
| 1100 | #ifdef CONFIG_OF
|
| 1101 | static const struct of_device_id nau8810_of_match[] = {
|
| 1102 | { .compatible = "nuvoton,nau8810", },
|
| 1103 | { }
|
| 1104 | };
|
| 1105 | MODULE_DEVICE_TABLE(of, nau8810_of_match);
|
| 1106 | #endif
|
| 1107 |
|
| 1108 | static struct i2c_driver nau8810_i2c_driver = {
|
| 1109 | .driver = {
|
| 1110 | .name = "nau8810",
|
| 1111 | .of_match_table = of_match_ptr(nau8810_of_match),
|
| 1112 | },
|
| 1113 | .probe = nau8810_i2c_probe,
|
| 1114 | .remove = nau8810_i2c_remove,
|
| 1115 | .id_table = nau8810_i2c_id,
|
| 1116 | };
|
| 1117 |
|
| 1118 | module_i2c_driver(nau8810_i2c_driver);
|
| 1119 |
|
| 1120 | MODULE_DESCRIPTION("ASoC NAU8810 driver");
|
| 1121 | MODULE_AUTHOR("David Lin <ctlin0@nuvoton.com>");
|
| 1122 | MODULE_LICENSE("GPL v2");
|