Fixed MTP to work with TWRP

This commit is contained in:
awab228 2018-06-19 23:16:04 +02:00
commit f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,96 @@
/*
* 88pm860x-codec.h -- 88PM860x ALSA SoC Audio Driver
*
* Copyright 2010 Marvell International Ltd.
* Haojian Zhuang <haojian.zhuang@marvell.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef __88PM860X_H
#define __88PM860X_H
#define PM860X_PCM_IFACE_1 0xb0
#define PM860X_PCM_IFACE_2 0xb1
#define PM860X_PCM_IFACE_3 0xb2
#define PM860X_PCM_RATE 0xb3
#define PM860X_EC_PATH 0xb4
#define PM860X_SIDETONE_L_GAIN 0xb5
#define PM860X_SIDETONE_R_GAIN 0xb6
#define PM860X_SIDETONE_SHIFT 0xb7
#define PM860X_ADC_OFFSET_1 0xb8
#define PM860X_ADC_OFFSET_2 0xb9
#define PM860X_DMIC_DELAY 0xba
#define PM860X_I2S_IFACE_1 0xbb
#define PM860X_I2S_IFACE_2 0xbc
#define PM860X_I2S_IFACE_3 0xbd
#define PM860X_I2S_IFACE_4 0xbe
#define PM860X_EQUALIZER_N0_1 0xbf
#define PM860X_EQUALIZER_N0_2 0xc0
#define PM860X_EQUALIZER_N1_1 0xc1
#define PM860X_EQUALIZER_N1_2 0xc2
#define PM860X_EQUALIZER_D1_1 0xc3
#define PM860X_EQUALIZER_D1_2 0xc4
#define PM860X_LOFI_GAIN_LEFT 0xc5
#define PM860X_LOFI_GAIN_RIGHT 0xc6
#define PM860X_HIFIL_GAIN_LEFT 0xc7
#define PM860X_HIFIL_GAIN_RIGHT 0xc8
#define PM860X_HIFIR_GAIN_LEFT 0xc9
#define PM860X_HIFIR_GAIN_RIGHT 0xca
#define PM860X_DAC_OFFSET 0xcb
#define PM860X_OFFSET_LEFT_1 0xcc
#define PM860X_OFFSET_LEFT_2 0xcd
#define PM860X_OFFSET_RIGHT_1 0xce
#define PM860X_OFFSET_RIGHT_2 0xcf
#define PM860X_ADC_ANA_1 0xd0
#define PM860X_ADC_ANA_2 0xd1
#define PM860X_ADC_ANA_3 0xd2
#define PM860X_ADC_ANA_4 0xd3
#define PM860X_ANA_TO_ANA 0xd4
#define PM860X_HS1_CTRL 0xd5
#define PM860X_HS2_CTRL 0xd6
#define PM860X_LO1_CTRL 0xd7
#define PM860X_LO2_CTRL 0xd8
#define PM860X_EAR_CTRL_1 0xd9
#define PM860X_EAR_CTRL_2 0xda
#define PM860X_AUDIO_SUPPLIES_1 0xdb
#define PM860X_AUDIO_SUPPLIES_2 0xdc
#define PM860X_ADC_EN_1 0xdd
#define PM860X_ADC_EN_2 0xde
#define PM860X_DAC_EN_1 0xdf
#define PM860X_DAC_EN_2 0xe1
#define PM860X_AUDIO_CAL_1 0xe2
#define PM860X_AUDIO_CAL_2 0xe3
#define PM860X_AUDIO_CAL_3 0xe4
#define PM860X_AUDIO_CAL_4 0xe5
#define PM860X_AUDIO_CAL_5 0xe6
#define PM860X_ANA_INPUT_SEL_1 0xe7
#define PM860X_ANA_INPUT_SEL_2 0xe8
#define PM860X_PCM_IFACE_4 0xe9
#define PM860X_I2S_IFACE_5 0xea
#define PM860X_SHORTS 0x3b
#define PM860X_PLL_ADJ_1 0x3c
#define PM860X_PLL_ADJ_2 0x3d
/* bits definition */
#define PM860X_CLK_DIR_IN 0
#define PM860X_CLK_DIR_OUT 1
#define PM860X_DET_HEADSET (1 << 0)
#define PM860X_DET_MIC (1 << 1)
#define PM860X_DET_HOOK (1 << 2)
#define PM860X_SHORT_HEADSET (1 << 3)
#define PM860X_SHORT_LINEOUT (1 << 4)
#define PM860X_DET_MASK 0x1F
extern int pm860x_hs_jack_detect(struct snd_soc_codec *, struct snd_soc_jack *,
int, int, int, int);
extern int pm860x_mic_jack_detect(struct snd_soc_codec *, struct snd_soc_jack *,
int);
#endif /* __88PM860X_H */

836
sound/soc/codecs/Kconfig Executable file
View file

@ -0,0 +1,836 @@
# Helper to resolve issues with configs that have SPI enabled but I2C
# modular, meaning we can't build the codec driver in with I2C support.
# We use an ordered list of conditional defaults to pick the appropriate
# setting - SPI can't be modular so that case doesn't need to be covered.
config SND_SOC_I2C_AND_SPI
tristate
default m if I2C=m
default y if I2C=y
default y if SPI_MASTER=y
menu "CODEC drivers"
config SND_SOC_ALL_CODECS
tristate "Build all ASoC CODEC drivers"
depends on COMPILE_TEST
select SND_SOC_88PM860X if MFD_88PM860X
select SND_SOC_L3
select SND_SOC_AB8500_CODEC if ABX500_CORE
select SND_SOC_AC97_CODEC if SND_SOC_AC97_BUS
select SND_SOC_AD1836 if SPI_MASTER
select SND_SOC_AD193X_SPI if SPI_MASTER
select SND_SOC_AD193X_I2C if I2C
select SND_SOC_AD1980 if SND_SOC_AC97_BUS
select SND_SOC_AD73311
select SND_SOC_ADAU1373 if I2C
select SND_SOC_ADAU1761_I2C if I2C
select SND_SOC_ADAU1761_SPI if SPI
select SND_SOC_ADAU1781_I2C if I2C
select SND_SOC_ADAU1781_SPI if SPI
select SND_SOC_ADAV801 if SPI_MASTER
select SND_SOC_ADAV803 if I2C
select SND_SOC_ADAU1977_SPI if SPI_MASTER
select SND_SOC_ADAU1977_I2C if I2C
select SND_SOC_ADAU1701 if I2C
select SND_SOC_ADS117X
select SND_SOC_AK4104 if SPI_MASTER
select SND_SOC_AK4535 if I2C
select SND_SOC_AK4554
select SND_SOC_AK4641 if I2C
select SND_SOC_AK4642 if I2C
select SND_SOC_AK4671 if I2C
select SND_SOC_AK5386
select SND_SOC_ALC5623 if I2C
select SND_SOC_ALC5632 if I2C
select SND_SOC_CQ0093VC if MFD_DAVINCI_VOICECODEC
select SND_SOC_CS35L32 if I2C
select SND_SOC_CS42L51_I2C if I2C
select SND_SOC_CS42L52 if I2C && INPUT
select SND_SOC_CS42L56 if I2C && INPUT
select SND_SOC_CS42L73 if I2C
select SND_SOC_CS4265 if I2C
select SND_SOC_CS4270 if I2C
select SND_SOC_CS4271 if SND_SOC_I2C_AND_SPI
select SND_SOC_CS42XX8_I2C if I2C
select SND_SOC_LARGO if MFD_LARGO
select SND_SOC_CX20442 if TTY
select SND_SOC_DA7210 if I2C
select SND_SOC_DA7213 if I2C
select SND_SOC_DA732X if I2C
select SND_SOC_DA9055 if I2C
select SND_SOC_DMIC
select SND_SOC_BT_SCO
select SND_SOC_ES8328_SPI if SPI_MASTER
select SND_SOC_ES8328_I2C if I2C
select SND_SOC_ISABELLE if I2C
select SND_SOC_JZ4740_CODEC
select SND_SOC_LM4857 if I2C
select SND_SOC_LM49453 if I2C
select SND_SOC_MAX98088 if I2C
select SND_SOC_MAX98090 if I2C
select SND_SOC_MAX98095 if I2C
select SND_SOC_MAX9850 if I2C
select SND_SOC_MAX9768 if I2C
select SND_SOC_MAX9877 if I2C
select SND_SOC_MC13783 if MFD_MC13XXX
select SND_SOC_ML26124 if I2C
select SND_SOC_HDMI_CODEC
select SND_SOC_PCM1681 if I2C
select SND_SOC_PCM1792A if SPI_MASTER
select SND_SOC_PCM3008
select SND_SOC_PCM512x_I2C if I2C
select SND_SOC_PCM512x_SPI if SPI_MASTER
select SND_SOC_RT286 if I2C
select SND_SOC_RT5631 if I2C
select SND_SOC_RT5640 if I2C
select SND_SOC_RT5645 if I2C
select SND_SOC_RT5651 if I2C
select SND_SOC_RT5670 if I2C
select SND_SOC_RT5677 if I2C
select SND_SOC_SGTL5000 if I2C
select SND_SOC_SI476X if MFD_SI476X_CORE
select SND_SOC_SIRF_AUDIO_CODEC
select SND_SOC_SN95031 if INTEL_SCU_IPC
select SND_SOC_SPDIF
select SND_SOC_SSM2518 if I2C
select SND_SOC_SSM2602_SPI if SPI_MASTER
select SND_SOC_SSM2602_I2C if I2C
select SND_SOC_SSM4567 if I2C
select SND_SOC_STA32X if I2C
select SND_SOC_STA350 if I2C
select SND_SOC_STA529 if I2C
select SND_SOC_STAC9766 if SND_SOC_AC97_BUS
select SND_SOC_TAS2552 if I2C
select SND_SOC_TAS5086 if I2C
select SND_SOC_TLV320AIC23_I2C if I2C
select SND_SOC_TLV320AIC23_SPI if SPI_MASTER
select SND_SOC_TLV320AIC26 if SPI_MASTER
select SND_SOC_TLV320AIC31XX if I2C
select SND_SOC_TLV320AIC32X4 if I2C
select SND_SOC_TLV320AIC3X if I2C
select SND_SOC_TPA6130A2 if I2C
select SND_SOC_TLV320DAC33 if I2C
select SND_SOC_TWL4030 if TWL4030_CORE
select SND_SOC_TWL6040 if TWL6040_CORE
select SND_SOC_UDA134X
select SND_SOC_UDA1380 if I2C
select SND_SOC_WL1273 if MFD_WL1273_CORE
select SND_SOC_WM0010 if SPI_MASTER
select SND_SOC_WM1250_EV1 if I2C
select SND_SOC_WM2000 if I2C
select SND_SOC_WM2200 if I2C
select SND_SOC_WM5100 if I2C
select SND_SOC_WM5102 if MFD_WM5102
select SND_SOC_WM5110 if MFD_WM5110
select SND_SOC_WM8350 if MFD_WM8350
select SND_SOC_WM8400 if MFD_WM8400
select SND_SOC_WM8510 if SND_SOC_I2C_AND_SPI
select SND_SOC_WM8523 if I2C
select SND_SOC_WM8580 if I2C
select SND_SOC_WM8711 if SND_SOC_I2C_AND_SPI
select SND_SOC_WM8727
select SND_SOC_WM8728 if SND_SOC_I2C_AND_SPI
select SND_SOC_WM8731 if SND_SOC_I2C_AND_SPI
select SND_SOC_WM8737 if SND_SOC_I2C_AND_SPI
select SND_SOC_WM8741 if SND_SOC_I2C_AND_SPI
select SND_SOC_WM8750 if SND_SOC_I2C_AND_SPI
select SND_SOC_WM8753 if SND_SOC_I2C_AND_SPI
select SND_SOC_WM8770 if SPI_MASTER
select SND_SOC_WM8776 if SND_SOC_I2C_AND_SPI
select SND_SOC_WM8782
select SND_SOC_WM8804 if SND_SOC_I2C_AND_SPI
select SND_SOC_WM8900 if I2C
select SND_SOC_WM8903 if I2C
select SND_SOC_WM8904 if I2C
select SND_SOC_WM8940 if I2C
select SND_SOC_WM8955 if I2C
select SND_SOC_WM8960 if I2C
select SND_SOC_WM8961 if I2C
select SND_SOC_WM8962 if I2C && INPUT
select SND_SOC_WM8971 if I2C
select SND_SOC_WM8974 if I2C
select SND_SOC_WM8978 if I2C
select SND_SOC_WM8983 if SND_SOC_I2C_AND_SPI
select SND_SOC_WM8985 if SND_SOC_I2C_AND_SPI
select SND_SOC_WM8988 if SND_SOC_I2C_AND_SPI
select SND_SOC_WM8990 if I2C
select SND_SOC_WM8991 if I2C
select SND_SOC_WM8993 if I2C
select SND_SOC_WM8994 if MFD_WM8994
select SND_SOC_WM8995 if SND_SOC_I2C_AND_SPI
select SND_SOC_WM8996 if I2C
select SND_SOC_WM8997 if MFD_WM8997
select SND_SOC_WM9081 if I2C
select SND_SOC_WM9090 if I2C
select SND_SOC_WM9705 if SND_SOC_AC97_BUS
select SND_SOC_WM9712 if SND_SOC_AC97_BUS
select SND_SOC_WM9713 if SND_SOC_AC97_BUS
help
Normally ASoC codec drivers are only built if a machine driver which
uses them is also built since they are only usable with a machine
driver. Selecting this option will allow these drivers to be built
without an explicit machine driver for test and development purposes.
Support for the bus types used to access the codecs to be built must
be selected separately.
If unsure select "N".
config SND_SOC_88PM860X
tristate
config SND_SOC_ARIZONA
tristate
default y if SND_SOC_WM5102=y
default y if SND_SOC_WM5110=y
default y if SND_SOC_WM8997=y
default y if SND_SOC_LARGO=y
default m if SND_SOC_WM5102=m
default m if SND_SOC_WM5110=m
default m if SND_SOC_WM8997=m
default m if SND_SOC_LARGO=m
config SND_SOC_WM_HUBS
tristate
default y if SND_SOC_WM8993=y || SND_SOC_WM8994=y
default m if SND_SOC_WM8993=m || SND_SOC_WM8994=m
config SND_SOC_WM_ADSP
tristate
default y if SND_SOC_WM5102=y
default y if SND_SOC_WM5110=y
default y if SND_SOC_WM2200=y
default y if SND_SOC_LARGO=y
default m if SND_SOC_WM5102=m
default m if SND_SOC_WM5110=m
default m if SND_SOC_WM2200=m
default m if SND_SOC_LARGO=m
config SND_SOC_AB8500_CODEC
tristate
config SND_SOC_AC97_CODEC
tristate
select SND_AC97_CODEC
config SND_SOC_AD1836
tristate
config SND_SOC_AD193X
tristate
config SND_SOC_AD193X_SPI
tristate
select SND_SOC_AD193X
config SND_SOC_AD193X_I2C
tristate
select SND_SOC_AD193X
config SND_SOC_AD1980
tristate
config SND_SOC_AD73311
tristate
config SND_SOC_ADAU1373
tristate
config SND_SOC_ADAU1701
tristate "Analog Devices ADAU1701 CODEC"
depends on I2C
select SND_SOC_SIGMADSP_I2C
config SND_SOC_ADAU17X1
tristate
select SND_SOC_SIGMADSP_REGMAP
config SND_SOC_ADAU1761
tristate
select SND_SOC_ADAU17X1
config SND_SOC_ADAU1761_I2C
tristate
select SND_SOC_ADAU1761
select REGMAP_I2C
config SND_SOC_ADAU1761_SPI
tristate
select SND_SOC_ADAU1761
select REGMAP_SPI
config SND_SOC_ADAU1781
select SND_SOC_ADAU17X1
tristate
config SND_SOC_ADAU1781_I2C
tristate
select SND_SOC_ADAU1781
select REGMAP_I2C
config SND_SOC_ADAU1781_SPI
tristate
select SND_SOC_ADAU1781
select REGMAP_SPI
config SND_SOC_ADAU1977
tristate
config SND_SOC_ADAU1977_SPI
tristate
select SND_SOC_ADAU1977
select REGMAP_SPI
config SND_SOC_ADAU1977_I2C
tristate
select SND_SOC_ADAU1977
select REGMAP_I2C
config SND_SOC_ADAV80X
tristate
config SND_SOC_ADAV801
tristate
select SND_SOC_ADAV80X
config SND_SOC_ADAV803
tristate
select SND_SOC_ADAV80X
config SND_SOC_ADS117X
tristate
config SND_SOC_AK4104
tristate "AKM AK4104 CODEC"
depends on SPI_MASTER
config SND_SOC_AK4535
tristate
config SND_SOC_AK4554
tristate "AKM AK4554 CODEC"
config SND_SOC_AK4641
tristate
config SND_SOC_AK4642
tristate "AKM AK4642 CODEC"
depends on I2C
config SND_SOC_AK4671
tristate
config SND_SOC_AK5386
tristate "AKM AK5638 CODEC"
config SND_SOC_ALC5623
tristate "Realtek ALC5623 CODEC"
depends on I2C
config SND_SOC_ALC5632
tristate
config SND_SOC_CQ0093VC
tristate
config SND_SOC_CS35L32
tristate "Cirrus Logic CS35L32 CODEC"
depends on I2C
config SND_SOC_CS42L51
tristate
config SND_SOC_CS42L51_I2C
tristate
select SND_SOC_CS42L51
config SND_SOC_CS42L52
tristate "Cirrus Logic CS42L52 CODEC"
depends on I2C && INPUT
config SND_SOC_CS42L56
tristate "Cirrus Logic CS42L56 CODEC"
depends on I2C && INPUT
config SND_SOC_CS42L73
tristate "Cirrus Logic CS42L73 CODEC"
depends on I2C
config SND_SOC_CS4265
tristate "Cirrus Logic CS4265 CODEC"
depends on I2C
select REGMAP_I2C
# Cirrus Logic CS4270 Codec
config SND_SOC_CS4270
tristate "Cirrus Logic CS4270 CODEC"
depends on I2C
# Cirrus Logic CS4270 Codec VD = 3.3V Errata
# Select if you are affected by the errata where the part will not function
# if MCLK divide-by-1.5 is selected and VD is set to 3.3V. The driver will
# not select any sample rates that require MCLK to be divided by 1.5.
config SND_SOC_CS4270_VD33_ERRATA
bool
depends on SND_SOC_CS4270
config SND_SOC_CS4271
tristate "Cirrus Logic CS4271 CODEC"
depends on SND_SOC_I2C_AND_SPI
config SND_SOC_CS42XX8
tristate
config SND_SOC_CS42XX8_I2C
tristate "Cirrus Logic CS42448/CS42888 CODEC (I2C)"
depends on I2C
select SND_SOC_CS42XX8
select REGMAP_I2C
config SND_SOC_LARGO
tristate
config SND_SOC_CX20442
tristate
depends on TTY
config SND_SOC_COD3026X
tristate
default n
config SND_SOC_COD9002X
tristate
default y
config SND_SOC_JZ4740_CODEC
select REGMAP_MMIO
tristate
config SND_SOC_L3
tristate
config SND_SOC_DA7210
tristate
config SND_SOC_DA7213
tristate
config SND_SOC_DA732X
tristate
config SND_SOC_DA9055
tristate
config SND_SOC_BT_SCO
tristate
config SND_SOC_DMIC
tristate
config SND_SOC_HDMI_CODEC
tristate "HDMI stub CODEC"
config SND_SOC_ES8328
tristate "Everest Semi ES8328 CODEC"
config SND_SOC_ES8328_I2C
tristate
select SND_SOC_ES8328
config SND_SOC_ES8328_SPI
tristate
select SND_SOC_ES8328
config SND_SOC_ISABELLE
tristate
config SND_SOC_LM49453
tristate
config SND_SOC_MAX98088
tristate
config SND_SOC_MAX98090
tristate
config SND_SOC_MAX98095
tristate
config SND_SOC_MAX9850
tristate
config SND_SOC_PCM1681
tristate "Texas Instruments PCM1681 CODEC"
depends on I2C
config SND_SOC_PCM1792A
tristate "Texas Instruments PCM1792A CODEC"
depends on SPI_MASTER
config SND_SOC_PCM3008
tristate
config SND_SOC_PCM512x
tristate
config SND_SOC_PCM512x_I2C
tristate "Texas Instruments PCM512x CODECs - I2C"
depends on I2C
select SND_SOC_PCM512x
select REGMAP_I2C
config SND_SOC_PCM512x_SPI
tristate "Texas Instruments PCM512x CODECs - SPI"
depends on SPI_MASTER
select SND_SOC_PCM512x
select REGMAP_SPI
config SND_SOC_RL6231
tristate
default y if SND_SOC_RT5640=y
default y if SND_SOC_RT5645=y
default y if SND_SOC_RT5651=y
default y if SND_SOC_RT5670=y
default y if SND_SOC_RT5677=y
default m if SND_SOC_RT5640=m
default m if SND_SOC_RT5645=m
default m if SND_SOC_RT5651=m
default m if SND_SOC_RT5670=m
default m if SND_SOC_RT5677=m
config SND_SOC_RT286
tristate
depends on I2C
config SND_SOC_RT5631
tristate
config SND_SOC_RT5640
tristate
config SND_SOC_RT5645
tristate
config SND_SOC_RT5651
tristate
config SND_SOC_RT5670
tristate
config SND_SOC_RT5677
tristate
#Freescale sgtl5000 codec
config SND_SOC_SGTL5000
tristate "Freescale SGTL5000 CODEC"
depends on I2C
config SND_SOC_SI476X
tristate
config SND_SOC_SIGMADSP
tristate
select CRC32
config SND_SOC_SIGMADSP_I2C
tristate
select SND_SOC_SIGMADSP
config SND_SOC_SIGMADSP_REGMAP
tristate
select SND_SOC_SIGMADSP
config SND_SOC_SIRF_AUDIO_CODEC
tristate "SiRF SoC internal audio codec"
select REGMAP_MMIO
config SND_SOC_SN95031
tristate
config SND_SOC_SPDIF
tristate "S/PDIF CODEC"
config SND_SOC_EXYNOS_AUDMIXER
tristate
select REGMAP_MMIO
select REGMAP_I2C
config SND_SOC_SSM2518
tristate
config SND_SOC_SSM2602
tristate
config SND_SOC_SSM2602_SPI
tristate "Analog Devices SSM2602 CODEC - SPI"
depends on SPI_MASTER
select SND_SOC_SSM2602
select REGMAP_SPI
config SND_SOC_SSM2602_I2C
tristate "Analog Devices SSM2602 CODEC - I2C"
depends on I2C
select SND_SOC_SSM2602
select REGMAP_I2C
config SND_SOC_SSM4567
tristate "Analog Devices ssm4567 amplifier driver support"
depends on I2C
config SND_SOC_STA32X
tristate
config SND_SOC_STA350
tristate "STA350 speaker amplifier"
depends on I2C
config SND_SOC_STA529
tristate
config SND_SOC_STAC9766
tristate
config SND_SOC_TAS2552
tristate "Texas Instruments TAS2552 Mono Audio amplifier"
depends on I2C
config SND_SOC_TAS5086
tristate "Texas Instruments TAS5086 speaker amplifier"
depends on I2C
config SND_SOC_TLV320AIC23
tristate
config SND_SOC_TLV320AIC23_I2C
tristate
select SND_SOC_TLV320AIC23
config SND_SOC_TLV320AIC23_SPI
tristate
select SND_SOC_TLV320AIC23
config SND_SOC_TLV320AIC26
tristate
depends on SPI
config SND_SOC_TLV320AIC31XX
tristate "Texas Instruments TLV320AIC31xx CODECs"
depends on I2C
select REGMAP_I2C
config SND_SOC_TLV320AIC32X4
tristate
config SND_SOC_TLV320AIC3X
tristate "Texas Instruments TLV320AIC3x CODECs"
depends on I2C
config SND_SOC_TLV320DAC33
tristate
config SND_SOC_TWL4030
select MFD_TWL4030_AUDIO
tristate
config SND_SOC_TWL6040
tristate
config SND_SOC_UDA134X
tristate
config SND_SOC_UDA1380
tristate
config SND_SOC_WL1273
tristate
config SND_SOC_WM0010
tristate
config SND_SOC_WM1250_EV1
tristate
config SND_SOC_WM2000
tristate
config SND_SOC_WM2200
tristate
config SND_SOC_WM5100
tristate
config SND_SOC_WM5102
tristate
config SND_SOC_WM5110
tristate
config SND_SOC_WM8350
tristate
config SND_SOC_WM8400
tristate
config SND_SOC_WM8510
tristate "Wolfson Microelectronics WM8510 CODEC"
depends on SND_SOC_I2C_AND_SPI
config SND_SOC_WM8523
tristate "Wolfson Microelectronics WM8523 DAC"
depends on I2C
config SND_SOC_WM8580
tristate "Wolfson Microelectronics WM8523 CODEC"
depends on I2C
config SND_SOC_WM8711
tristate "Wolfson Microelectronics WM8711 CODEC"
depends on SND_SOC_I2C_AND_SPI
config SND_SOC_WM8727
tristate
config SND_SOC_WM8728
tristate "Wolfson Microelectronics WM8728 DAC"
depends on SND_SOC_I2C_AND_SPI
config SND_SOC_WM8731
tristate "Wolfson Microelectronics WM8731 CODEC"
depends on SND_SOC_I2C_AND_SPI
config SND_SOC_WM8737
tristate "Wolfson Microelectronics WM8737 ADC"
depends on SND_SOC_I2C_AND_SPI
config SND_SOC_WM8741
tristate "Wolfson Microelectronics WM8737 DAC"
depends on SND_SOC_I2C_AND_SPI
config SND_SOC_WM8750
tristate "Wolfson Microelectronics WM8750 CODEC"
depends on SND_SOC_I2C_AND_SPI
config SND_SOC_WM8753
tristate "Wolfson Microelectronics WM8753 CODEC"
depends on SND_SOC_I2C_AND_SPI
config SND_SOC_WM8770
tristate "Wolfson Microelectronics WM8770 CODEC"
depends on SPI_MASTER
config SND_SOC_WM8776
tristate "Wolfson Microelectronics WM8776 CODEC"
depends on SND_SOC_I2C_AND_SPI
config SND_SOC_WM8782
tristate
config SND_SOC_WM8804
tristate "Wolfson Microelectronics WM8804 S/PDIF transceiver"
depends on SND_SOC_I2C_AND_SPI
config SND_SOC_WM8900
tristate
config SND_SOC_WM8903
tristate "Wolfson Microelectronics WM8903 CODEC"
depends on I2C
config SND_SOC_WM8904
tristate
config SND_SOC_WM8940
tristate
config SND_SOC_WM8955
tristate
config SND_SOC_WM8960
tristate
config SND_SOC_WM8961
tristate
config SND_SOC_WM8962
tristate "Wolfson Microelectronics WM8962 CODEC"
depends on I2C && INPUT
config SND_SOC_WM8971
tristate
config SND_SOC_WM8974
tristate
config SND_SOC_WM8978
tristate "Wolfson Microelectronics WM8978 codec"
depends on I2C
config SND_SOC_WM8983
tristate
config SND_SOC_WM8985
tristate
config SND_SOC_WM8988
tristate
config SND_SOC_WM8990
tristate
config SND_SOC_WM8991
tristate
config SND_SOC_WM8993
tristate
config SND_SOC_WM8994
tristate
config SND_SOC_WM8995
tristate
config SND_SOC_WM8996
tristate
config SND_SOC_WM8997
tristate
config SND_SOC_WM9081
tristate
config SND_SOC_WM9090
tristate
config SND_SOC_WM9705
tristate
config SND_SOC_WM9712
tristate
config SND_SOC_WM9713
tristate
config SND_SOC_DUMMY_CODEC
tristate
# Amp
config SND_SOC_LM4857
tristate
config SND_SOC_MAX9768
tristate
config SND_SOC_MAX9877
tristate
config SND_SOC_MC13783
tristate
config SND_SOC_ML26124
tristate
config SND_SOC_TPA6130A2
tristate "Texas Instruments TPA6130A2 headphone amplifier"
depends on I2C
endmenu

355
sound/soc/codecs/Makefile Executable file
View file

@ -0,0 +1,355 @@
snd-soc-88pm860x-objs := 88pm860x-codec.o
snd-soc-ab8500-codec-objs := ab8500-codec.o
snd-soc-ac97-objs := ac97.o
snd-soc-ad1836-objs := ad1836.o
snd-soc-ad193x-objs := ad193x.o
snd-soc-ad193x-spi-objs := ad193x-spi.o
snd-soc-ad193x-i2c-objs := ad193x-i2c.o
snd-soc-ad1980-objs := ad1980.o
snd-soc-ad73311-objs := ad73311.o
snd-soc-adau1373-objs := adau1373.o
snd-soc-adau1701-objs := adau1701.o
snd-soc-adau17x1-objs := adau17x1.o
snd-soc-adau1761-objs := adau1761.o
snd-soc-adau1761-i2c-objs := adau1761-i2c.o
snd-soc-adau1761-spi-objs := adau1761-spi.o
snd-soc-adau1781-objs := adau1781.o
snd-soc-adau1781-i2c-objs := adau1781-i2c.o
snd-soc-adau1781-spi-objs := adau1781-spi.o
snd-soc-adau1977-objs := adau1977.o
snd-soc-adau1977-spi-objs := adau1977-spi.o
snd-soc-adau1977-i2c-objs := adau1977-i2c.o
snd-soc-adav80x-objs := adav80x.o
snd-soc-adav801-objs := adav801.o
snd-soc-adav803-objs := adav803.o
snd-soc-ads117x-objs := ads117x.o
snd-soc-ak4104-objs := ak4104.o
snd-soc-ak4535-objs := ak4535.o
snd-soc-ak4554-objs := ak4554.o
snd-soc-ak4641-objs := ak4641.o
snd-soc-ak4642-objs := ak4642.o
snd-soc-ak4671-objs := ak4671.o
snd-soc-ak5386-objs := ak5386.o
snd-soc-arizona-objs := arizona.o
snd-soc-cod3026-objs := cod3026x.o
snd-soc-cod9002-objs := cod9002x.o
snd-soc-cq93vc-objs := cq93vc.o
snd-soc-cs35l32-objs := cs35l32.o
snd-soc-cs42l51-objs := cs42l51.o
snd-soc-cs42l51-i2c-objs := cs42l51-i2c.o
snd-soc-cs42l52-objs := cs42l52.o
snd-soc-cs42l56-objs := cs42l56.o
snd-soc-cs42l73-objs := cs42l73.o
snd-soc-cs4265-objs := cs4265.o
snd-soc-cs4270-objs := cs4270.o
snd-soc-cs4271-objs := cs4271.o
snd-soc-cs42xx8-objs := cs42xx8.o
snd-soc-cs42xx8-i2c-objs := cs42xx8-i2c.o
snd-soc-largo-objs := largo.o
snd-soc-cx20442-objs := cx20442.o
snd-soc-da7210-objs := da7210.o
snd-soc-da7213-objs := da7213.o
snd-soc-da732x-objs := da732x.o
snd-soc-da9055-objs := da9055.o
snd-soc-bt-sco-objs := bt-sco.o
snd-soc-dmic-objs := dmic.o
snd-soc-es8328-objs := es8328.o
snd-soc-es8328-i2c-objs := es8328-i2c.o
snd-soc-es8328-spi-objs := es8328-spi.o
snd-soc-isabelle-objs := isabelle.o
snd-soc-jz4740-codec-objs := jz4740.o
snd-soc-l3-objs := l3.o
snd-soc-lm4857-objs := lm4857.o
snd-soc-lm49453-objs := lm49453.o
snd-soc-max9768-objs := max9768.o
snd-soc-max98088-objs := max98088.o
snd-soc-max98090-objs := max98090.o
snd-soc-max98095-objs := max98095.o
snd-soc-max9850-objs := max9850.o
snd-soc-mc13783-objs := mc13783.o
snd-soc-ml26124-objs := ml26124.o
snd-soc-hdmi-codec-objs := hdmi.o
snd-soc-pcm1681-objs := pcm1681.o
snd-soc-pcm1792a-codec-objs := pcm1792a.o
snd-soc-pcm3008-objs := pcm3008.o
snd-soc-pcm512x-objs := pcm512x.o
snd-soc-pcm512x-i2c-objs := pcm512x-i2c.o
snd-soc-pcm512x-spi-objs := pcm512x-spi.o
snd-soc-rl6231-objs := rl6231.o
snd-soc-rt286-objs := rt286.o
snd-soc-rt5631-objs := rt5631.o
snd-soc-rt5640-objs := rt5640.o
snd-soc-rt5645-objs := rt5645.o
snd-soc-rt5651-objs := rt5651.o
snd-soc-rt5670-objs := rt5670.o
snd-soc-rt5677-objs := rt5677.o
snd-soc-sgtl5000-objs := sgtl5000.o
snd-soc-alc5623-objs := alc5623.o
snd-soc-alc5632-objs := alc5632.o
snd-soc-sigmadsp-objs := sigmadsp.o
snd-soc-sigmadsp-i2c-objs := sigmadsp-i2c.o
snd-soc-sigmadsp-regmap-objs := sigmadsp-regmap.o
snd-soc-si476x-objs := si476x.o
snd-soc-sirf-audio-codec-objs := sirf-audio-codec.o
snd-soc-sn95031-objs := sn95031.o
snd-soc-spdif-tx-objs := spdif_transmitter.o
snd-soc-spdif-rx-objs := spdif_receiver.o
snd-soc-audmixer-objs := exynos-audmixer.o
snd-soc-ssm2518-objs := ssm2518.o
snd-soc-ssm2602-objs := ssm2602.o
snd-soc-ssm2602-spi-objs := ssm2602-spi.o
snd-soc-ssm2602-i2c-objs := ssm2602-i2c.o
snd-soc-ssm4567-objs := ssm4567.o
snd-soc-sta32x-objs := sta32x.o
snd-soc-sta350-objs := sta350.o
snd-soc-sta529-objs := sta529.o
snd-soc-stac9766-objs := stac9766.o
snd-soc-tas5086-objs := tas5086.o
snd-soc-tlv320aic23-objs := tlv320aic23.o
snd-soc-tlv320aic23-i2c-objs := tlv320aic23-i2c.o
snd-soc-tlv320aic23-spi-objs := tlv320aic23-spi.o
snd-soc-tlv320aic26-objs := tlv320aic26.o
snd-soc-tlv320aic31xx-objs := tlv320aic31xx.o
snd-soc-tlv320aic32x4-objs := tlv320aic32x4.o
snd-soc-tlv320aic3x-objs := tlv320aic3x.o
snd-soc-tlv320dac33-objs := tlv320dac33.o
snd-soc-twl4030-objs := twl4030.o
snd-soc-twl6040-objs := twl6040.o
snd-soc-uda134x-objs := uda134x.o
snd-soc-uda1380-objs := uda1380.o
snd-soc-wl1273-objs := wl1273.o
snd-soc-wm-adsp-objs := wm_adsp.o
snd-soc-wm0010-objs := wm0010.o
snd-soc-wm1250-ev1-objs := wm1250-ev1.o
snd-soc-wm2000-objs := wm2000.o
snd-soc-wm2200-objs := wm2200.o
snd-soc-wm5100-objs := wm5100.o wm5100-tables.o
snd-soc-wm5102-objs := wm5102.o
snd-soc-wm5110-objs := wm5110.o
snd-soc-wm8350-objs := wm8350.o
snd-soc-wm8400-objs := wm8400.o
snd-soc-wm8510-objs := wm8510.o
snd-soc-wm8523-objs := wm8523.o
snd-soc-wm8580-objs := wm8580.o
snd-soc-wm8711-objs := wm8711.o
snd-soc-wm8727-objs := wm8727.o
snd-soc-wm8728-objs := wm8728.o
snd-soc-wm8731-objs := wm8731.o
snd-soc-wm8737-objs := wm8737.o
snd-soc-wm8741-objs := wm8741.o
snd-soc-wm8750-objs := wm8750.o
snd-soc-wm8753-objs := wm8753.o
snd-soc-wm8770-objs := wm8770.o
snd-soc-wm8776-objs := wm8776.o
snd-soc-wm8782-objs := wm8782.o
snd-soc-wm8804-objs := wm8804.o
snd-soc-wm8900-objs := wm8900.o
snd-soc-wm8903-objs := wm8903.o
snd-soc-wm8904-objs := wm8904.o
snd-soc-wm8996-objs := wm8996.o
snd-soc-wm8940-objs := wm8940.o
snd-soc-wm8955-objs := wm8955.o
snd-soc-wm8960-objs := wm8960.o
snd-soc-wm8961-objs := wm8961.o
snd-soc-wm8962-objs := wm8962.o
snd-soc-wm8971-objs := wm8971.o
snd-soc-wm8974-objs := wm8974.o
snd-soc-wm8978-objs := wm8978.o
snd-soc-wm8983-objs := wm8983.o
snd-soc-wm8985-objs := wm8985.o
snd-soc-wm8988-objs := wm8988.o
snd-soc-wm8990-objs := wm8990.o
snd-soc-wm8991-objs := wm8991.o
snd-soc-wm8993-objs := wm8993.o
snd-soc-wm8994-objs := wm8994.o wm8958-dsp2.o
snd-soc-wm8995-objs := wm8995.o
snd-soc-wm8997-objs := wm8997.o
snd-soc-wm9081-objs := wm9081.o
snd-soc-wm9090-objs := wm9090.o
snd-soc-wm9705-objs := wm9705.o
snd-soc-wm9712-objs := wm9712.o
snd-soc-wm9713-objs := wm9713.o
snd-soc-wm-hubs-objs := wm_hubs.o
snd-soc-dummy-objs := dummy_codec.o
# Amp
snd-soc-max9877-objs := max9877.o
snd-soc-tpa6130a2-objs := tpa6130a2.o
snd-soc-tas2552-objs := tas2552.o
obj-$(CONFIG_SND_SOC_88PM860X) += snd-soc-88pm860x.o
obj-$(CONFIG_SND_SOC_AB8500_CODEC) += snd-soc-ab8500-codec.o
obj-$(CONFIG_SND_SOC_AC97_CODEC) += snd-soc-ac97.o
obj-$(CONFIG_SND_SOC_AD1836) += snd-soc-ad1836.o
obj-$(CONFIG_SND_SOC_AD193X) += snd-soc-ad193x.o
obj-$(CONFIG_SND_SOC_AD193X_SPI) += snd-soc-ad193x-spi.o
obj-$(CONFIG_SND_SOC_AD193X_I2C) += snd-soc-ad193x-i2c.o
obj-$(CONFIG_SND_SOC_AD1980) += snd-soc-ad1980.o
obj-$(CONFIG_SND_SOC_AD73311) += snd-soc-ad73311.o
obj-$(CONFIG_SND_SOC_ADAU1373) += snd-soc-adau1373.o
obj-$(CONFIG_SND_SOC_ADAU1701) += snd-soc-adau1701.o
obj-$(CONFIG_SND_SOC_ADAU17X1) += snd-soc-adau17x1.o
obj-$(CONFIG_SND_SOC_ADAU1761) += snd-soc-adau1761.o
obj-$(CONFIG_SND_SOC_ADAU1761_I2C) += snd-soc-adau1761-i2c.o
obj-$(CONFIG_SND_SOC_ADAU1761_SPI) += snd-soc-adau1761-spi.o
obj-$(CONFIG_SND_SOC_ADAU1781) += snd-soc-adau1781.o
obj-$(CONFIG_SND_SOC_ADAU1781_I2C) += snd-soc-adau1781-i2c.o
obj-$(CONFIG_SND_SOC_ADAU1781_SPI) += snd-soc-adau1781-spi.o
obj-$(CONFIG_SND_SOC_ADAU1977) += snd-soc-adau1977.o
obj-$(CONFIG_SND_SOC_ADAU1977_SPI) += snd-soc-adau1977-spi.o
obj-$(CONFIG_SND_SOC_ADAU1977_I2C) += snd-soc-adau1977-i2c.o
obj-$(CONFIG_SND_SOC_ADAV80X) += snd-soc-adav80x.o
obj-$(CONFIG_SND_SOC_ADAV801) += snd-soc-adav801.o
obj-$(CONFIG_SND_SOC_ADAV803) += snd-soc-adav803.o
obj-$(CONFIG_SND_SOC_ADS117X) += snd-soc-ads117x.o
obj-$(CONFIG_SND_SOC_AK4104) += snd-soc-ak4104.o
obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.o
obj-$(CONFIG_SND_SOC_AK4554) += snd-soc-ak4554.o
obj-$(CONFIG_SND_SOC_AK4641) += snd-soc-ak4641.o
obj-$(CONFIG_SND_SOC_AK4642) += snd-soc-ak4642.o
obj-$(CONFIG_SND_SOC_AK4671) += snd-soc-ak4671.o
obj-$(CONFIG_SND_SOC_AK5386) += snd-soc-ak5386.o
obj-$(CONFIG_SND_SOC_ALC5623) += snd-soc-alc5623.o
obj-$(CONFIG_SND_SOC_ALC5632) += snd-soc-alc5632.o
obj-$(CONFIG_SND_SOC_ARIZONA) += snd-soc-arizona.o
obj-$(CONFIG_SND_SOC_COD3026X) += snd-soc-cod3026.o
obj-$(CONFIG_SND_SOC_COD9002X) += snd-soc-cod9002.o
obj-$(CONFIG_SND_SOC_CQ0093VC) += snd-soc-cq93vc.o
obj-$(CONFIG_SND_SOC_CS35L32) += snd-soc-cs35l32.o
obj-$(CONFIG_SND_SOC_CS42L51) += snd-soc-cs42l51.o
obj-$(CONFIG_SND_SOC_CS42L51_I2C) += snd-soc-cs42l51-i2c.o
obj-$(CONFIG_SND_SOC_CS42L52) += snd-soc-cs42l52.o
obj-$(CONFIG_SND_SOC_CS42L56) += snd-soc-cs42l56.o
obj-$(CONFIG_SND_SOC_CS42L73) += snd-soc-cs42l73.o
obj-$(CONFIG_SND_SOC_CS4265) += snd-soc-cs4265.o
obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o
obj-$(CONFIG_SND_SOC_CS4271) += snd-soc-cs4271.o
obj-$(CONFIG_SND_SOC_CS42XX8) += snd-soc-cs42xx8.o
obj-$(CONFIG_SND_SOC_CS42XX8_I2C) += snd-soc-cs42xx8-i2c.o
obj-$(CONFIG_SND_SOC_LARGO) += snd-soc-largo.o
obj-$(CONFIG_SND_SOC_CX20442) += snd-soc-cx20442.o
obj-$(CONFIG_SND_SOC_DA7210) += snd-soc-da7210.o
obj-$(CONFIG_SND_SOC_DA7213) += snd-soc-da7213.o
obj-$(CONFIG_SND_SOC_DA732X) += snd-soc-da732x.o
obj-$(CONFIG_SND_SOC_DA9055) += snd-soc-da9055.o
obj-$(CONFIG_SND_SOC_BT_SCO) += snd-soc-bt-sco.o
obj-$(CONFIG_SND_SOC_DMIC) += snd-soc-dmic.o
obj-$(CONFIG_SND_SOC_ES8328) += snd-soc-es8328.o
obj-$(CONFIG_SND_SOC_ES8328_I2C)+= snd-soc-es8328-i2c.o
obj-$(CONFIG_SND_SOC_ES8328_SPI)+= snd-soc-es8328-spi.o
obj-$(CONFIG_SND_SOC_ISABELLE) += snd-soc-isabelle.o
obj-$(CONFIG_SND_SOC_JZ4740_CODEC) += snd-soc-jz4740-codec.o
obj-$(CONFIG_SND_SOC_L3) += snd-soc-l3.o
obj-$(CONFIG_SND_SOC_LM4857) += snd-soc-lm4857.o
obj-$(CONFIG_SND_SOC_LM49453) += snd-soc-lm49453.o
obj-$(CONFIG_SND_SOC_MAX9768) += snd-soc-max9768.o
obj-$(CONFIG_SND_SOC_MAX98088) += snd-soc-max98088.o
obj-$(CONFIG_SND_SOC_MAX98090) += snd-soc-max98090.o
obj-$(CONFIG_SND_SOC_MAX98095) += snd-soc-max98095.o
obj-$(CONFIG_SND_SOC_MAX9850) += snd-soc-max9850.o
obj-$(CONFIG_SND_SOC_MC13783) += snd-soc-mc13783.o
obj-$(CONFIG_SND_SOC_ML26124) += snd-soc-ml26124.o
obj-$(CONFIG_SND_SOC_HDMI_CODEC) += snd-soc-hdmi-codec.o
obj-$(CONFIG_SND_SOC_PCM1681) += snd-soc-pcm1681.o
obj-$(CONFIG_SND_SOC_PCM1792A) += snd-soc-pcm1792a-codec.o
obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o
obj-$(CONFIG_SND_SOC_PCM512x) += snd-soc-pcm512x.o
obj-$(CONFIG_SND_SOC_PCM512x_I2C) += snd-soc-pcm512x-i2c.o
obj-$(CONFIG_SND_SOC_PCM512x_SPI) += snd-soc-pcm512x-spi.o
obj-$(CONFIG_SND_SOC_RL6231) += snd-soc-rl6231.o
obj-$(CONFIG_SND_SOC_RT286) += snd-soc-rt286.o
obj-$(CONFIG_SND_SOC_RT5631) += snd-soc-rt5631.o
obj-$(CONFIG_SND_SOC_RT5640) += snd-soc-rt5640.o
obj-$(CONFIG_SND_SOC_RT5645) += snd-soc-rt5645.o
obj-$(CONFIG_SND_SOC_RT5651) += snd-soc-rt5651.o
obj-$(CONFIG_SND_SOC_RT5670) += snd-soc-rt5670.o
obj-$(CONFIG_SND_SOC_RT5677) += snd-soc-rt5677.o
obj-$(CONFIG_SND_SOC_SGTL5000) += snd-soc-sgtl5000.o
obj-$(CONFIG_SND_SOC_SIGMADSP) += snd-soc-sigmadsp.o
obj-$(CONFIG_SND_SOC_SIGMADSP_I2C) += snd-soc-sigmadsp-i2c.o
obj-$(CONFIG_SND_SOC_SIGMADSP_REGMAP) += snd-soc-sigmadsp-regmap.o
obj-$(CONFIG_SND_SOC_SI476X) += snd-soc-si476x.o
obj-$(CONFIG_SND_SOC_SN95031) +=snd-soc-sn95031.o
obj-$(CONFIG_SND_SOC_SPDIF) += snd-soc-spdif-rx.o snd-soc-spdif-tx.o
obj-$(CONFIG_SND_SOC_EXYNOS_AUDMIXER) += snd-soc-audmixer.o
obj-$(CONFIG_SND_SOC_SSM2518) += snd-soc-ssm2518.o
obj-$(CONFIG_SND_SOC_SSM2602) += snd-soc-ssm2602.o
obj-$(CONFIG_SND_SOC_SSM2602_SPI) += snd-soc-ssm2602-spi.o
obj-$(CONFIG_SND_SOC_SSM2602_I2C) += snd-soc-ssm2602-i2c.o
obj-$(CONFIG_SND_SOC_SSM4567) += snd-soc-ssm4567.o
obj-$(CONFIG_SND_SOC_STA32X) += snd-soc-sta32x.o
obj-$(CONFIG_SND_SOC_STA350) += snd-soc-sta350.o
obj-$(CONFIG_SND_SOC_STA529) += snd-soc-sta529.o
obj-$(CONFIG_SND_SOC_STAC9766) += snd-soc-stac9766.o
obj-$(CONFIG_SND_SOC_TAS2552) += snd-soc-tas2552.o
obj-$(CONFIG_SND_SOC_TAS5086) += snd-soc-tas5086.o
obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o
obj-$(CONFIG_SND_SOC_TLV320AIC23_I2C) += snd-soc-tlv320aic23-i2c.o
obj-$(CONFIG_SND_SOC_TLV320AIC23_SPI) += snd-soc-tlv320aic23-spi.o
obj-$(CONFIG_SND_SOC_TLV320AIC26) += snd-soc-tlv320aic26.o
obj-$(CONFIG_SND_SOC_TLV320AIC31XX) += snd-soc-tlv320aic31xx.o
obj-$(CONFIG_SND_SOC_TLV320AIC32X4) += snd-soc-tlv320aic32x4.o
obj-$(CONFIG_SND_SOC_TLV320AIC3X) += snd-soc-tlv320aic3x.o
obj-$(CONFIG_SND_SOC_TLV320DAC33) += snd-soc-tlv320dac33.o
obj-$(CONFIG_SND_SOC_TWL4030) += snd-soc-twl4030.o
obj-$(CONFIG_SND_SOC_TWL6040) += snd-soc-twl6040.o
obj-$(CONFIG_SND_SOC_UDA134X) += snd-soc-uda134x.o
obj-$(CONFIG_SND_SOC_UDA1380) += snd-soc-uda1380.o
obj-$(CONFIG_SND_SOC_WL1273) += snd-soc-wl1273.o
obj-$(CONFIG_SND_SOC_WM0010) += snd-soc-wm0010.o
obj-$(CONFIG_SND_SOC_WM1250_EV1) += snd-soc-wm1250-ev1.o
obj-$(CONFIG_SND_SOC_WM2000) += snd-soc-wm2000.o
obj-$(CONFIG_SND_SOC_WM2200) += snd-soc-wm2200.o
obj-$(CONFIG_SND_SOC_WM5100) += snd-soc-wm5100.o
obj-$(CONFIG_SND_SOC_WM5102) += snd-soc-wm5102.o
obj-$(CONFIG_SND_SOC_WM5110) += snd-soc-wm5110.o
obj-$(CONFIG_SND_SOC_WM8350) += snd-soc-wm8350.o
obj-$(CONFIG_SND_SOC_WM8400) += snd-soc-wm8400.o
obj-$(CONFIG_SND_SOC_WM8510) += snd-soc-wm8510.o
obj-$(CONFIG_SND_SOC_WM8523) += snd-soc-wm8523.o
obj-$(CONFIG_SND_SOC_WM8580) += snd-soc-wm8580.o
obj-$(CONFIG_SND_SOC_WM8711) += snd-soc-wm8711.o
obj-$(CONFIG_SND_SOC_WM8727) += snd-soc-wm8727.o
obj-$(CONFIG_SND_SOC_WM8728) += snd-soc-wm8728.o
obj-$(CONFIG_SND_SOC_WM8731) += snd-soc-wm8731.o
obj-$(CONFIG_SND_SOC_WM8737) += snd-soc-wm8737.o
obj-$(CONFIG_SND_SOC_WM8741) += snd-soc-wm8741.o
obj-$(CONFIG_SND_SOC_WM8750) += snd-soc-wm8750.o
obj-$(CONFIG_SND_SOC_WM8753) += snd-soc-wm8753.o
obj-$(CONFIG_SND_SOC_WM8770) += snd-soc-wm8770.o
obj-$(CONFIG_SND_SOC_WM8776) += snd-soc-wm8776.o
obj-$(CONFIG_SND_SOC_WM8782) += snd-soc-wm8782.o
obj-$(CONFIG_SND_SOC_WM8804) += snd-soc-wm8804.o
obj-$(CONFIG_SND_SOC_WM8900) += snd-soc-wm8900.o
obj-$(CONFIG_SND_SOC_WM8903) += snd-soc-wm8903.o
obj-$(CONFIG_SND_SOC_WM8904) += snd-soc-wm8904.o
obj-$(CONFIG_SND_SOC_WM8996) += snd-soc-wm8996.o
obj-$(CONFIG_SND_SOC_WM8940) += snd-soc-wm8940.o
obj-$(CONFIG_SND_SOC_WM8955) += snd-soc-wm8955.o
obj-$(CONFIG_SND_SOC_WM8960) += snd-soc-wm8960.o
obj-$(CONFIG_SND_SOC_WM8961) += snd-soc-wm8961.o
obj-$(CONFIG_SND_SOC_WM8962) += snd-soc-wm8962.o
obj-$(CONFIG_SND_SOC_WM8971) += snd-soc-wm8971.o
obj-$(CONFIG_SND_SOC_WM8974) += snd-soc-wm8974.o
obj-$(CONFIG_SND_SOC_WM8978) += snd-soc-wm8978.o
obj-$(CONFIG_SND_SOC_WM8983) += snd-soc-wm8983.o
obj-$(CONFIG_SND_SOC_WM8985) += snd-soc-wm8985.o
obj-$(CONFIG_SND_SOC_WM8988) += snd-soc-wm8988.o
obj-$(CONFIG_SND_SOC_WM8990) += snd-soc-wm8990.o
obj-$(CONFIG_SND_SOC_WM8991) += snd-soc-wm8991.o
obj-$(CONFIG_SND_SOC_WM8993) += snd-soc-wm8993.o
obj-$(CONFIG_SND_SOC_WM8994) += snd-soc-wm8994.o
obj-$(CONFIG_SND_SOC_WM8995) += snd-soc-wm8995.o
obj-$(CONFIG_SND_SOC_WM8997) += snd-soc-wm8997.o
obj-$(CONFIG_SND_SOC_WM9081) += snd-soc-wm9081.o
obj-$(CONFIG_SND_SOC_WM9090) += snd-soc-wm9090.o
obj-$(CONFIG_SND_SOC_WM9705) += snd-soc-wm9705.o
obj-$(CONFIG_SND_SOC_WM9712) += snd-soc-wm9712.o
obj-$(CONFIG_SND_SOC_WM9713) += snd-soc-wm9713.o
obj-$(CONFIG_SND_SOC_WM_ADSP) += snd-soc-wm-adsp.o
obj-$(CONFIG_SND_SOC_WM_HUBS) += snd-soc-wm-hubs.o
obj-$(CONFIG_SND_SOC_DUMMY_CODEC) += snd-soc-dummy.o
# Amp
obj-$(CONFIG_SND_SOC_MAX9877) += snd-soc-max9877.o
obj-$(CONFIG_SND_SOC_TPA6130A2) += snd-soc-tpa6130a2.o

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,592 @@
/*
* Copyright (C) ST-Ericsson SA 2012
*
* Author: Ola Lilja <ola.o.lilja@stericsson.com>,
* Kristoffer Karlsson <kristoffer.karlsson@stericsson.com>,
* Roger Nilsson <roger.xr.nilsson@stericsson.com>,
* for ST-Ericsson.
*
* Based on the early work done by:
* Mikko J. Lehto <mikko.lehto@symbio.com>,
* Mikko Sarmanne <mikko.sarmanne@symbio.com>,
* for ST-Ericsson.
*
* License terms:
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*/
#ifndef AB8500_CODEC_REGISTERS_H
#define AB8500_CODEC_REGISTERS_H
#define AB8500_SUPPORTED_RATE (SNDRV_PCM_RATE_48000)
#define AB8500_SUPPORTED_FMT (SNDRV_PCM_FMTBIT_S16_LE)
/* AB8500 interface slot offset definitions */
#define AB8500_AD_DATA0_OFFSET 0
#define AB8500_DA_DATA0_OFFSET 8
#define AB8500_AD_DATA1_OFFSET 16
#define AB8500_DA_DATA1_OFFSET 24
/* AB8500 audio bank (0x0d) register definitions */
#define AB8500_POWERUP 0x00
#define AB8500_AUDSWRESET 0x01
#define AB8500_ADPATHENA 0x02
#define AB8500_DAPATHENA 0x03
#define AB8500_ANACONF1 0x04
#define AB8500_ANACONF2 0x05
#define AB8500_DIGMICCONF 0x06
#define AB8500_ANACONF3 0x07
#define AB8500_ANACONF4 0x08
#define AB8500_DAPATHCONF 0x09
#define AB8500_MUTECONF 0x0A
#define AB8500_SHORTCIRCONF 0x0B
#define AB8500_ANACONF5 0x0C
#define AB8500_ENVCPCONF 0x0D
#define AB8500_SIGENVCONF 0x0E
#define AB8500_PWMGENCONF1 0x0F
#define AB8500_PWMGENCONF2 0x10
#define AB8500_PWMGENCONF3 0x11
#define AB8500_PWMGENCONF4 0x12
#define AB8500_PWMGENCONF5 0x13
#define AB8500_ANAGAIN1 0x14
#define AB8500_ANAGAIN2 0x15
#define AB8500_ANAGAIN3 0x16
#define AB8500_ANAGAIN4 0x17
#define AB8500_DIGLINHSLGAIN 0x18
#define AB8500_DIGLINHSRGAIN 0x19
#define AB8500_ADFILTCONF 0x1A
#define AB8500_DIGIFCONF1 0x1B
#define AB8500_DIGIFCONF2 0x1C
#define AB8500_DIGIFCONF3 0x1D
#define AB8500_DIGIFCONF4 0x1E
#define AB8500_ADSLOTSEL1 0x1F
#define AB8500_ADSLOTSEL2 0x20
#define AB8500_ADSLOTSEL3 0x21
#define AB8500_ADSLOTSEL4 0x22
#define AB8500_ADSLOTSEL5 0x23
#define AB8500_ADSLOTSEL6 0x24
#define AB8500_ADSLOTSEL7 0x25
#define AB8500_ADSLOTSEL8 0x26
#define AB8500_ADSLOTSEL9 0x27
#define AB8500_ADSLOTSEL10 0x28
#define AB8500_ADSLOTSEL11 0x29
#define AB8500_ADSLOTSEL12 0x2A
#define AB8500_ADSLOTSEL13 0x2B
#define AB8500_ADSLOTSEL14 0x2C
#define AB8500_ADSLOTSEL15 0x2D
#define AB8500_ADSLOTSEL16 0x2E
#define AB8500_ADSLOTSEL(slot) (AB8500_ADSLOTSEL1 + (slot >> 1))
#define AB8500_ADSLOTHIZCTRL1 0x2F
#define AB8500_ADSLOTHIZCTRL2 0x30
#define AB8500_ADSLOTHIZCTRL3 0x31
#define AB8500_ADSLOTHIZCTRL4 0x32
#define AB8500_DASLOTCONF1 0x33
#define AB8500_DASLOTCONF2 0x34
#define AB8500_DASLOTCONF3 0x35
#define AB8500_DASLOTCONF4 0x36
#define AB8500_DASLOTCONF5 0x37
#define AB8500_DASLOTCONF6 0x38
#define AB8500_DASLOTCONF7 0x39
#define AB8500_DASLOTCONF8 0x3A
#define AB8500_CLASSDCONF1 0x3B
#define AB8500_CLASSDCONF2 0x3C
#define AB8500_CLASSDCONF3 0x3D
#define AB8500_DMICFILTCONF 0x3E
#define AB8500_DIGMULTCONF1 0x3F
#define AB8500_DIGMULTCONF2 0x40
#define AB8500_ADDIGGAIN1 0x41
#define AB8500_ADDIGGAIN2 0x42
#define AB8500_ADDIGGAIN3 0x43
#define AB8500_ADDIGGAIN4 0x44
#define AB8500_ADDIGGAIN5 0x45
#define AB8500_ADDIGGAIN6 0x46
#define AB8500_DADIGGAIN1 0x47
#define AB8500_DADIGGAIN2 0x48
#define AB8500_DADIGGAIN3 0x49
#define AB8500_DADIGGAIN4 0x4A
#define AB8500_DADIGGAIN5 0x4B
#define AB8500_DADIGGAIN6 0x4C
#define AB8500_ADDIGLOOPGAIN1 0x4D
#define AB8500_ADDIGLOOPGAIN2 0x4E
#define AB8500_HSLEARDIGGAIN 0x4F
#define AB8500_HSRDIGGAIN 0x50
#define AB8500_SIDFIRGAIN1 0x51
#define AB8500_SIDFIRGAIN2 0x52
#define AB8500_ANCCONF1 0x53
#define AB8500_ANCCONF2 0x54
#define AB8500_ANCCONF3 0x55
#define AB8500_ANCCONF4 0x56
#define AB8500_ANCCONF5 0x57
#define AB8500_ANCCONF6 0x58
#define AB8500_ANCCONF7 0x59
#define AB8500_ANCCONF8 0x5A
#define AB8500_ANCCONF9 0x5B
#define AB8500_ANCCONF10 0x5C
#define AB8500_ANCCONF11 0x5D
#define AB8500_ANCCONF12 0x5E
#define AB8500_ANCCONF13 0x5F
#define AB8500_ANCCONF14 0x60
#define AB8500_SIDFIRADR 0x61
#define AB8500_SIDFIRCOEF1 0x62
#define AB8500_SIDFIRCOEF2 0x63
#define AB8500_SIDFIRCONF 0x64
#define AB8500_AUDINTMASK1 0x65
#define AB8500_AUDINTSOURCE1 0x66
#define AB8500_AUDINTMASK2 0x67
#define AB8500_AUDINTSOURCE2 0x68
#define AB8500_FIFOCONF1 0x69
#define AB8500_FIFOCONF2 0x6A
#define AB8500_FIFOCONF3 0x6B
#define AB8500_FIFOCONF4 0x6C
#define AB8500_FIFOCONF5 0x6D
#define AB8500_FIFOCONF6 0x6E
#define AB8500_AUDREV 0x6F
#define AB8500_FIRST_REG AB8500_POWERUP
#define AB8500_LAST_REG AB8500_AUDREV
#define AB8500_CACHEREGNUM (AB8500_LAST_REG + 1)
#define AB8500_MASK_ALL 0xFF
#define AB8500_MASK_SLOT(slot) ((slot & 1) ? 0xF0 : 0x0F)
#define AB8500_MASK_NONE 0x00
/* AB8500_POWERUP */
#define AB8500_POWERUP_POWERUP 7
#define AB8500_POWERUP_ENANA 3
/* AB8500_AUDSWRESET */
#define AB8500_AUDSWRESET_SWRESET 7
/* AB8500_ADPATHENA */
#define AB8500_ADPATHENA_ENAD12 7
#define AB8500_ADPATHENA_ENAD34 5
#define AB8500_ADPATHENA_ENAD5768 3
/* AB8500_DAPATHENA */
#define AB8500_DAPATHENA_ENDA1 7
#define AB8500_DAPATHENA_ENDA2 6
#define AB8500_DAPATHENA_ENDA3 5
#define AB8500_DAPATHENA_ENDA4 4
#define AB8500_DAPATHENA_ENDA5 3
#define AB8500_DAPATHENA_ENDA6 2
/* AB8500_ANACONF1 */
#define AB8500_ANACONF1_HSLOWPOW 7
#define AB8500_ANACONF1_DACLOWPOW1 6
#define AB8500_ANACONF1_DACLOWPOW0 5
#define AB8500_ANACONF1_EARDACLOWPOW 4
#define AB8500_ANACONF1_EARSELCM 2
#define AB8500_ANACONF1_HSHPEN 1
#define AB8500_ANACONF1_EARDRVLOWPOW 0
/* AB8500_ANACONF2 */
#define AB8500_ANACONF2_ENMIC1 7
#define AB8500_ANACONF2_ENMIC2 6
#define AB8500_ANACONF2_ENLINL 5
#define AB8500_ANACONF2_ENLINR 4
#define AB8500_ANACONF2_MUTMIC1 3
#define AB8500_ANACONF2_MUTMIC2 2
#define AB8500_ANACONF2_MUTLINL 1
#define AB8500_ANACONF2_MUTLINR 0
/* AB8500_DIGMICCONF */
#define AB8500_DIGMICCONF_ENDMIC1 7
#define AB8500_DIGMICCONF_ENDMIC2 6
#define AB8500_DIGMICCONF_ENDMIC3 5
#define AB8500_DIGMICCONF_ENDMIC4 4
#define AB8500_DIGMICCONF_ENDMIC5 3
#define AB8500_DIGMICCONF_ENDMIC6 2
#define AB8500_DIGMICCONF_HSFADSPEED 0
/* AB8500_ANACONF3 */
#define AB8500_ANACONF3_MIC1SEL 7
#define AB8500_ANACONF3_LINRSEL 6
#define AB8500_ANACONF3_ENDRVHSL 5
#define AB8500_ANACONF3_ENDRVHSR 4
#define AB8500_ANACONF3_ENADCMIC 2
#define AB8500_ANACONF3_ENADCLINL 1
#define AB8500_ANACONF3_ENADCLINR 0
/* AB8500_ANACONF4 */
#define AB8500_ANACONF4_DISPDVSS 7
#define AB8500_ANACONF4_ENEAR 6
#define AB8500_ANACONF4_ENHSL 5
#define AB8500_ANACONF4_ENHSR 4
#define AB8500_ANACONF4_ENHFL 3
#define AB8500_ANACONF4_ENHFR 2
#define AB8500_ANACONF4_ENVIB1 1
#define AB8500_ANACONF4_ENVIB2 0
/* AB8500_DAPATHCONF */
#define AB8500_DAPATHCONF_ENDACEAR 6
#define AB8500_DAPATHCONF_ENDACHSL 5
#define AB8500_DAPATHCONF_ENDACHSR 4
#define AB8500_DAPATHCONF_ENDACHFL 3
#define AB8500_DAPATHCONF_ENDACHFR 2
#define AB8500_DAPATHCONF_ENDACVIB1 1
#define AB8500_DAPATHCONF_ENDACVIB2 0
/* AB8500_MUTECONF */
#define AB8500_MUTECONF_MUTEAR 6
#define AB8500_MUTECONF_MUTHSL 5
#define AB8500_MUTECONF_MUTHSR 4
#define AB8500_MUTECONF_MUTDACEAR 2
#define AB8500_MUTECONF_MUTDACHSL 1
#define AB8500_MUTECONF_MUTDACHSR 0
/* AB8500_SHORTCIRCONF */
#define AB8500_SHORTCIRCONF_ENSHORTPWD 7
#define AB8500_SHORTCIRCONF_EARSHORTDIS 6
#define AB8500_SHORTCIRCONF_HSSHORTDIS 5
#define AB8500_SHORTCIRCONF_HSPULLDEN 4
#define AB8500_SHORTCIRCONF_HSOSCEN 2
#define AB8500_SHORTCIRCONF_HSFADDIS 1
#define AB8500_SHORTCIRCONF_HSZCDDIS 0
/* Zero cross should be disabled */
/* AB8500_ANACONF5 */
#define AB8500_ANACONF5_ENCPHS 7
#define AB8500_ANACONF5_HSLDACTOLOL 5
#define AB8500_ANACONF5_HSRDACTOLOR 4
#define AB8500_ANACONF5_ENLOL 3
#define AB8500_ANACONF5_ENLOR 2
#define AB8500_ANACONF5_HSAUTOEN 0
/* AB8500_ENVCPCONF */
#define AB8500_ENVCPCONF_ENVDETHTHRE 4
#define AB8500_ENVCPCONF_ENVDETLTHRE 0
#define AB8500_ENVCPCONF_ENVDETHTHRE_MAX 0x0F
#define AB8500_ENVCPCONF_ENVDETLTHRE_MAX 0x0F
/* AB8500_SIGENVCONF */
#define AB8500_SIGENVCONF_CPLVEN 5
#define AB8500_SIGENVCONF_ENVDETCPEN 4
#define AB8500_SIGENVCONF_ENVDETTIME 0
#define AB8500_SIGENVCONF_ENVDETTIME_MAX 0x0F
/* AB8500_PWMGENCONF1 */
#define AB8500_PWMGENCONF1_PWMTOVIB1 7
#define AB8500_PWMGENCONF1_PWMTOVIB2 6
#define AB8500_PWMGENCONF1_PWM1CTRL 5
#define AB8500_PWMGENCONF1_PWM2CTRL 4
#define AB8500_PWMGENCONF1_PWM1NCTRL 3
#define AB8500_PWMGENCONF1_PWM1PCTRL 2
#define AB8500_PWMGENCONF1_PWM2NCTRL 1
#define AB8500_PWMGENCONF1_PWM2PCTRL 0
/* AB8500_PWMGENCONF2 */
/* AB8500_PWMGENCONF3 */
/* AB8500_PWMGENCONF4 */
/* AB8500_PWMGENCONF5 */
#define AB8500_PWMGENCONFX_PWMVIBXPOL 7
#define AB8500_PWMGENCONFX_PWMVIBXDUTCYC 0
#define AB8500_PWMGENCONFX_PWMVIBXDUTCYC_MAX 0x64
/* AB8500_ANAGAIN1 */
/* AB8500_ANAGAIN2 */
#define AB8500_ANAGAINX_ENSEMICX 7
#define AB8500_ANAGAINX_LOWPOWMICX 6
#define AB8500_ANAGAINX_MICXGAIN 0
#define AB8500_ANAGAINX_MICXGAIN_MAX 0x1F
/* AB8500_ANAGAIN3 */
#define AB8500_ANAGAIN3_HSLGAIN 4
#define AB8500_ANAGAIN3_HSRGAIN 0
#define AB8500_ANAGAIN3_HSXGAIN_MAX 0x0F
/* AB8500_ANAGAIN4 */
#define AB8500_ANAGAIN4_LINLGAIN 4
#define AB8500_ANAGAIN4_LINRGAIN 0
#define AB8500_ANAGAIN4_LINXGAIN_MAX 0x0F
/* AB8500_DIGLINHSLGAIN */
/* AB8500_DIGLINHSRGAIN */
#define AB8500_DIGLINHSXGAIN_LINTOHSXGAIN 0
#define AB8500_DIGLINHSXGAIN_LINTOHSXGAIN_MAX 0x13
/* AB8500_ADFILTCONF */
#define AB8500_ADFILTCONF_AD1NH 7
#define AB8500_ADFILTCONF_AD2NH 6
#define AB8500_ADFILTCONF_AD3NH 5
#define AB8500_ADFILTCONF_AD4NH 4
#define AB8500_ADFILTCONF_AD1VOICE 3
#define AB8500_ADFILTCONF_AD2VOICE 2
#define AB8500_ADFILTCONF_AD3VOICE 1
#define AB8500_ADFILTCONF_AD4VOICE 0
/* AB8500_DIGIFCONF1 */
#define AB8500_DIGIFCONF1_ENMASTGEN 7
#define AB8500_DIGIFCONF1_IF1BITCLKOS1 6
#define AB8500_DIGIFCONF1_IF1BITCLKOS0 5
#define AB8500_DIGIFCONF1_ENFSBITCLK1 4
#define AB8500_DIGIFCONF1_IF0BITCLKOS1 2
#define AB8500_DIGIFCONF1_IF0BITCLKOS0 1
#define AB8500_DIGIFCONF1_ENFSBITCLK0 0
/* AB8500_DIGIFCONF2 */
#define AB8500_DIGIFCONF2_FSYNC0P 6
#define AB8500_DIGIFCONF2_BITCLK0P 5
#define AB8500_DIGIFCONF2_IF0DEL 4
#define AB8500_DIGIFCONF2_IF0FORMAT1 3
#define AB8500_DIGIFCONF2_IF0FORMAT0 2
#define AB8500_DIGIFCONF2_IF0WL1 1
#define AB8500_DIGIFCONF2_IF0WL0 0
/* AB8500_DIGIFCONF3 */
#define AB8500_DIGIFCONF3_IF0DATOIF1AD 7
#define AB8500_DIGIFCONF3_IF0CLKTOIF1CLK 6
#define AB8500_DIGIFCONF3_IF1MASTER 5
#define AB8500_DIGIFCONF3_IF1DATOIF0AD 3
#define AB8500_DIGIFCONF3_IF1CLKTOIF0CLK 2
#define AB8500_DIGIFCONF3_IF0MASTER 1
#define AB8500_DIGIFCONF3_IF0BFIFOEN 0
/* AB8500_DIGIFCONF4 */
#define AB8500_DIGIFCONF4_FSYNC1P 6
#define AB8500_DIGIFCONF4_BITCLK1P 5
#define AB8500_DIGIFCONF4_IF1DEL 4
#define AB8500_DIGIFCONF4_IF1FORMAT1 3
#define AB8500_DIGIFCONF4_IF1FORMAT0 2
#define AB8500_DIGIFCONF4_IF1WL1 1
#define AB8500_DIGIFCONF4_IF1WL0 0
/* AB8500_ADSLOTSELX */
#define AB8500_AD_OUT1 0x0
#define AB8500_AD_OUT2 0x1
#define AB8500_AD_OUT3 0x2
#define AB8500_AD_OUT4 0x3
#define AB8500_AD_OUT5 0x4
#define AB8500_AD_OUT6 0x5
#define AB8500_AD_OUT7 0x6
#define AB8500_AD_OUT8 0x7
#define AB8500_ZEROES 0x8
#define AB8500_TRISTATE 0xF
#define AB8500_ADSLOTSELX_EVEN_SHIFT 0
#define AB8500_ADSLOTSELX_ODD_SHIFT 4
#define AB8500_ADSLOTSELX_AD_OUT_TO_SLOT(out, slot) \
((out) << (((slot) & 1) ? \
AB8500_ADSLOTSELX_ODD_SHIFT : AB8500_ADSLOTSELX_EVEN_SHIFT))
/* AB8500_ADSLOTHIZCTRL1 */
/* AB8500_ADSLOTHIZCTRL2 */
/* AB8500_ADSLOTHIZCTRL3 */
/* AB8500_ADSLOTHIZCTRL4 */
/* AB8500_DASLOTCONF1 */
#define AB8500_DASLOTCONF1_DA12VOICE 7
#define AB8500_DASLOTCONF1_SWAPDA12_34 6
#define AB8500_DASLOTCONF1_DAI7TOADO1 5
/* AB8500_DASLOTCONF2 */
#define AB8500_DASLOTCONF2_DAI8TOADO2 5
/* AB8500_DASLOTCONF3 */
#define AB8500_DASLOTCONF3_DA34VOICE 7
#define AB8500_DASLOTCONF3_DAI7TOADO3 5
/* AB8500_DASLOTCONF4 */
#define AB8500_DASLOTCONF4_DAI8TOADO4 5
/* AB8500_DASLOTCONF5 */
#define AB8500_DASLOTCONF5_DA56VOICE 7
#define AB8500_DASLOTCONF5_DAI7TOADO5 5
/* AB8500_DASLOTCONF6 */
#define AB8500_DASLOTCONF6_DAI8TOADO6 5
/* AB8500_DASLOTCONF7 */
#define AB8500_DASLOTCONF7_DAI8TOADO7 5
/* AB8500_DASLOTCONF8 */
#define AB8500_DASLOTCONF8_DAI7TOADO8 5
#define AB8500_DASLOTCONFX_SLTODAX_SHIFT 0
#define AB8500_DASLOTCONFX_SLTODAX_MASK 0x1F
/* AB8500_CLASSDCONF1 */
#define AB8500_CLASSDCONF1_PARLHF 7
#define AB8500_CLASSDCONF1_PARLVIB 6
#define AB8500_CLASSDCONF1_VIB1SWAPEN 3
#define AB8500_CLASSDCONF1_VIB2SWAPEN 2
#define AB8500_CLASSDCONF1_HFLSWAPEN 1
#define AB8500_CLASSDCONF1_HFRSWAPEN 0
/* AB8500_CLASSDCONF2 */
#define AB8500_CLASSDCONF2_FIRBYP3 7
#define AB8500_CLASSDCONF2_FIRBYP2 6
#define AB8500_CLASSDCONF2_FIRBYP1 5
#define AB8500_CLASSDCONF2_FIRBYP0 4
#define AB8500_CLASSDCONF2_HIGHVOLEN3 3
#define AB8500_CLASSDCONF2_HIGHVOLEN2 2
#define AB8500_CLASSDCONF2_HIGHVOLEN1 1
#define AB8500_CLASSDCONF2_HIGHVOLEN0 0
/* AB8500_CLASSDCONF3 */
#define AB8500_CLASSDCONF3_DITHHPGAIN 4
#define AB8500_CLASSDCONF3_DITHHPGAIN_MAX 0x0A
#define AB8500_CLASSDCONF3_DITHWGAIN 0
#define AB8500_CLASSDCONF3_DITHWGAIN_MAX 0x0A
/* AB8500_DMICFILTCONF */
#define AB8500_DMICFILTCONF_ANCINSEL 7
#define AB8500_DMICFILTCONF_DA3TOEAR 6
#define AB8500_DMICFILTCONF_DMIC1SINC3 5
#define AB8500_DMICFILTCONF_DMIC2SINC3 4
#define AB8500_DMICFILTCONF_DMIC3SINC3 3
#define AB8500_DMICFILTCONF_DMIC4SINC3 2
#define AB8500_DMICFILTCONF_DMIC5SINC3 1
#define AB8500_DMICFILTCONF_DMIC6SINC3 0
/* AB8500_DIGMULTCONF1 */
#define AB8500_DIGMULTCONF1_DATOHSLEN 7
#define AB8500_DIGMULTCONF1_DATOHSREN 6
#define AB8500_DIGMULTCONF1_AD1SEL 5
#define AB8500_DIGMULTCONF1_AD2SEL 4
#define AB8500_DIGMULTCONF1_AD3SEL 3
#define AB8500_DIGMULTCONF1_AD5SEL 2
#define AB8500_DIGMULTCONF1_AD6SEL 1
#define AB8500_DIGMULTCONF1_ANCSEL 0
/* AB8500_DIGMULTCONF2 */
#define AB8500_DIGMULTCONF2_DATOHFREN 7
#define AB8500_DIGMULTCONF2_DATOHFLEN 6
#define AB8500_DIGMULTCONF2_HFRSEL 5
#define AB8500_DIGMULTCONF2_HFLSEL 4
#define AB8500_DIGMULTCONF2_FIRSID1SEL 2
#define AB8500_DIGMULTCONF2_FIRSID2SEL 0
/* AB8500_ADDIGGAIN1 */
/* AB8500_ADDIGGAIN2 */
/* AB8500_ADDIGGAIN3 */
/* AB8500_ADDIGGAIN4 */
/* AB8500_ADDIGGAIN5 */
/* AB8500_ADDIGGAIN6 */
#define AB8500_ADDIGGAINX_FADEDISADX 6
#define AB8500_ADDIGGAINX_ADXGAIN_MAX 0x3F
/* AB8500_DADIGGAIN1 */
/* AB8500_DADIGGAIN2 */
/* AB8500_DADIGGAIN3 */
/* AB8500_DADIGGAIN4 */
/* AB8500_DADIGGAIN5 */
/* AB8500_DADIGGAIN6 */
#define AB8500_DADIGGAINX_FADEDISDAX 6
#define AB8500_DADIGGAINX_DAXGAIN_MAX 0x3F
/* AB8500_ADDIGLOOPGAIN1 */
/* AB8500_ADDIGLOOPGAIN2 */
#define AB8500_ADDIGLOOPGAINX_FADEDISADXL 6
#define AB8500_ADDIGLOOPGAINX_ADXLBGAIN_MAX 0x3F
/* AB8500_HSLEARDIGGAIN */
#define AB8500_HSLEARDIGGAIN_HSSINC1 7
#define AB8500_HSLEARDIGGAIN_FADEDISHSL 4
#define AB8500_HSLEARDIGGAIN_HSLDGAIN_MAX 0x09
/* AB8500_HSRDIGGAIN */
#define AB8500_HSRDIGGAIN_FADESPEED 6
#define AB8500_HSRDIGGAIN_FADEDISHSR 4
#define AB8500_HSRDIGGAIN_HSRDGAIN_MAX 0x09
/* AB8500_SIDFIRGAIN1 */
/* AB8500_SIDFIRGAIN2 */
#define AB8500_SIDFIRGAINX_FIRSIDXGAIN_MAX 0x1F
/* AB8500_ANCCONF1 */
#define AB8500_ANCCONF1_ANCIIRUPDATE 3
#define AB8500_ANCCONF1_ENANC 2
#define AB8500_ANCCONF1_ANCIIRINIT 1
#define AB8500_ANCCONF1_ANCFIRUPDATE 0
/* AB8500_ANCCONF2 */
#define AB8500_ANCCONF2_SHIFT 5
#define AB8500_ANCCONF2_MIN -0x10
#define AB8500_ANCCONF2_MAX 0xF
/* AB8500_ANCCONF3 */
#define AB8500_ANCCONF3_SHIFT 5
#define AB8500_ANCCONF3_MIN -0x10
#define AB8500_ANCCONF3_MAX 0xF
/* AB8500_ANCCONF4 */
#define AB8500_ANCCONF4_SHIFT 5
#define AB8500_ANCCONF4_MIN -0x10
#define AB8500_ANCCONF4_MAX 0xF
/* AB8500_ANC_FIR_COEFFS */
#define AB8500_ANC_FIR_COEFF_MIN -0x8000
#define AB8500_ANC_FIR_COEFF_MAX 0x7FFF
#define AB8500_ANC_FIR_COEFFS 15
/* AB8500_ANC_IIR_COEFFS */
#define AB8500_ANC_IIR_COEFF_MIN -0x800000
#define AB8500_ANC_IIR_COEFF_MAX 0x7FFFFF
#define AB8500_ANC_IIR_COEFFS 24
/* AB8500_ANC_WARP_DELAY */
#define AB8500_ANC_WARP_DELAY_SHIFT 16
#define AB8500_ANC_WARP_DELAY_MIN 0x0000
#define AB8500_ANC_WARP_DELAY_MAX 0xFFFF
/* AB8500_ANCCONF11 */
/* AB8500_ANCCONF12 */
/* AB8500_ANCCONF13 */
/* AB8500_ANCCONF14 */
/* AB8500_SIDFIRADR */
#define AB8500_SIDFIRADR_FIRSIDSET 7
#define AB8500_SIDFIRADR_ADDRESS_SHIFT 0
#define AB8500_SIDFIRADR_ADDRESS_MAX 0x7F
/* AB8500_SIDFIRCOEF1 */
/* AB8500_SIDFIRCOEF2 */
#define AB8500_SID_FIR_COEFF_MIN 0
#define AB8500_SID_FIR_COEFF_MAX 0xFFFF
#define AB8500_SID_FIR_COEFFS 128
/* AB8500_SIDFIRCONF */
#define AB8500_SIDFIRCONF_ENFIRSIDS 2
#define AB8500_SIDFIRCONF_FIRSIDSTOIF1 1
#define AB8500_SIDFIRCONF_FIRSIDBUSY 0
/* AB8500_AUDINTMASK1 */
/* AB8500_AUDINTSOURCE1 */
/* AB8500_AUDINTMASK2 */
/* AB8500_AUDINTSOURCE2 */
/* AB8500_FIFOCONF1 */
#define AB8500_FIFOCONF1_BFIFOMASK 0x80
#define AB8500_FIFOCONF1_BFIFO19M2 0x40
#define AB8500_FIFOCONF1_BFIFOINT_SHIFT 0
#define AB8500_FIFOCONF1_BFIFOINT_MAX 0x3F
/* AB8500_FIFOCONF2 */
#define AB8500_FIFOCONF2_BFIFOTX_SHIFT 0
#define AB8500_FIFOCONF2_BFIFOTX_MAX 0xFF
/* AB8500_FIFOCONF3 */
#define AB8500_FIFOCONF3_BFIFOEXSL_SHIFT 5
#define AB8500_FIFOCONF3_BFIFOEXSL_MAX 0x5
#define AB8500_FIFOCONF3_PREBITCLK0_SHIFT 2
#define AB8500_FIFOCONF3_PREBITCLK0_MAX 0x7
#define AB8500_FIFOCONF3_BFIFOMAST_SHIFT 1
#define AB8500_FIFOCONF3_BFIFORUN_SHIFT 0
/* AB8500_FIFOCONF4 */
#define AB8500_FIFOCONF4_BFIFOFRAMSW_SHIFT 0
#define AB8500_FIFOCONF4_BFIFOFRAMSW_MAX 0xFF
/* AB8500_FIFOCONF5 */
#define AB8500_FIFOCONF5_BFIFOWAKEUP_SHIFT 0
#define AB8500_FIFOCONF5_BFIFOWAKEUP_MAX 0xFF
/* AB8500_FIFOCONF6 */
#define AB8500_FIFOCONF6_BFIFOSAMPLE_SHIFT 0
#define AB8500_FIFOCONF6_BFIFOSAMPLE_MAX 0xFF
/* AB8500_AUDREV */
#endif

149
sound/soc/codecs/ac97.c Normal file
View file

@ -0,0 +1,149 @@
/*
* ac97.c -- ALSA Soc AC97 codec support
*
* Copyright 2005 Wolfson Microelectronics PLC.
* Author: Liam Girdwood <lrg@slimlogic.co.uk>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* Generic AC97 support.
*/
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/module.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/ac97_codec.h>
#include <sound/initval.h>
#include <sound/soc.h>
static const struct snd_soc_dapm_widget ac97_widgets[] = {
SND_SOC_DAPM_INPUT("RX"),
SND_SOC_DAPM_OUTPUT("TX"),
};
static const struct snd_soc_dapm_route ac97_routes[] = {
{ "AC97 Capture", NULL, "RX" },
{ "TX", NULL, "AC97 Playback" },
};
static int ac97_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
int reg = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
AC97_PCM_FRONT_DAC_RATE : AC97_PCM_LR_ADC_RATE;
return snd_ac97_set_rate(codec->ac97, reg, substream->runtime->rate);
}
#define STD_AC97_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 |\
SNDRV_PCM_RATE_48000)
static const struct snd_soc_dai_ops ac97_dai_ops = {
.prepare = ac97_prepare,
};
static struct snd_soc_dai_driver ac97_dai = {
.name = "ac97-hifi",
.ac97_control = 1,
.playback = {
.stream_name = "AC97 Playback",
.channels_min = 1,
.channels_max = 2,
.rates = STD_AC97_RATES,
.formats = SND_SOC_STD_AC97_FMTS,},
.capture = {
.stream_name = "AC97 Capture",
.channels_min = 1,
.channels_max = 2,
.rates = STD_AC97_RATES,
.formats = SND_SOC_STD_AC97_FMTS,},
.ops = &ac97_dai_ops,
};
static int ac97_soc_probe(struct snd_soc_codec *codec)
{
struct snd_ac97_bus *ac97_bus;
struct snd_ac97_template ac97_template;
int ret;
/* add codec as bus device for standard ac97 */
ret = snd_ac97_bus(codec->component.card->snd_card, 0, soc_ac97_ops,
NULL, &ac97_bus);
if (ret < 0)
return ret;
memset(&ac97_template, 0, sizeof(struct snd_ac97_template));
ret = snd_ac97_mixer(ac97_bus, &ac97_template, &codec->ac97);
if (ret < 0)
return ret;
return 0;
}
#ifdef CONFIG_PM
static int ac97_soc_suspend(struct snd_soc_codec *codec)
{
snd_ac97_suspend(codec->ac97);
return 0;
}
static int ac97_soc_resume(struct snd_soc_codec *codec)
{
snd_ac97_resume(codec->ac97);
return 0;
}
#else
#define ac97_soc_suspend NULL
#define ac97_soc_resume NULL
#endif
static struct snd_soc_codec_driver soc_codec_dev_ac97 = {
.probe = ac97_soc_probe,
.suspend = ac97_soc_suspend,
.resume = ac97_soc_resume,
.dapm_widgets = ac97_widgets,
.num_dapm_widgets = ARRAY_SIZE(ac97_widgets),
.dapm_routes = ac97_routes,
.num_dapm_routes = ARRAY_SIZE(ac97_routes),
};
static int ac97_probe(struct platform_device *pdev)
{
return snd_soc_register_codec(&pdev->dev,
&soc_codec_dev_ac97, &ac97_dai, 1);
}
static int ac97_remove(struct platform_device *pdev)
{
snd_soc_unregister_codec(&pdev->dev);
return 0;
}
static struct platform_driver ac97_codec_driver = {
.driver = {
.name = "ac97-codec",
.owner = THIS_MODULE,
},
.probe = ac97_probe,
.remove = ac97_remove,
};
module_platform_driver(ac97_codec_driver);
MODULE_DESCRIPTION("Soc Generic AC97 driver");
MODULE_AUTHOR("Liam Girdwood");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:ac97-codec");

418
sound/soc/codecs/ad1836.c Normal file
View file

@ -0,0 +1,418 @@
/*
* Audio Codec driver supporting:
* AD1835A, AD1836, AD1837A, AD1838A, AD1839A
*
* Copyright 2009-2011 Analog Devices Inc.
*
* Licensed under the GPL-2 or later.
*/
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/initval.h>
#include <sound/soc.h>
#include <sound/tlv.h>
#include <linux/spi/spi.h>
#include <linux/regmap.h>
#include "ad1836.h"
enum ad1836_type {
AD1835,
AD1836,
AD1838,
};
/* codec private data */
struct ad1836_priv {
enum ad1836_type type;
struct regmap *regmap;
};
/*
* AD1836 volume/mute/de-emphasis etc. controls
*/
static const char *ad1836_deemp[] = {"None", "44.1kHz", "32kHz", "48kHz"};
static SOC_ENUM_SINGLE_DECL(ad1836_deemp_enum,
AD1836_DAC_CTRL1, 8, ad1836_deemp);
#define AD1836_DAC_VOLUME(x) \
SOC_DOUBLE_R("DAC" #x " Playback Volume", AD1836_DAC_L_VOL(x), \
AD1836_DAC_R_VOL(x), 0, 0x3FF, 0)
#define AD1836_DAC_SWITCH(x) \
SOC_DOUBLE("DAC" #x " Playback Switch", AD1836_DAC_CTRL2, \
AD1836_MUTE_LEFT(x), AD1836_MUTE_RIGHT(x), 1, 1)
#define AD1836_ADC_SWITCH(x) \
SOC_DOUBLE("ADC" #x " Capture Switch", AD1836_ADC_CTRL2, \
AD1836_MUTE_LEFT(x), AD1836_MUTE_RIGHT(x), 1, 1)
static const struct snd_kcontrol_new ad183x_dac_controls[] = {
AD1836_DAC_VOLUME(1),
AD1836_DAC_SWITCH(1),
AD1836_DAC_VOLUME(2),
AD1836_DAC_SWITCH(2),
AD1836_DAC_VOLUME(3),
AD1836_DAC_SWITCH(3),
AD1836_DAC_VOLUME(4),
AD1836_DAC_SWITCH(4),
};
static const struct snd_soc_dapm_widget ad183x_dac_dapm_widgets[] = {
SND_SOC_DAPM_OUTPUT("DAC1OUT"),
SND_SOC_DAPM_OUTPUT("DAC2OUT"),
SND_SOC_DAPM_OUTPUT("DAC3OUT"),
SND_SOC_DAPM_OUTPUT("DAC4OUT"),
};
static const struct snd_soc_dapm_route ad183x_dac_routes[] = {
{ "DAC1OUT", NULL, "DAC" },
{ "DAC2OUT", NULL, "DAC" },
{ "DAC3OUT", NULL, "DAC" },
{ "DAC4OUT", NULL, "DAC" },
};
static const struct snd_kcontrol_new ad183x_adc_controls[] = {
AD1836_ADC_SWITCH(1),
AD1836_ADC_SWITCH(2),
AD1836_ADC_SWITCH(3),
};
static const struct snd_soc_dapm_widget ad183x_adc_dapm_widgets[] = {
SND_SOC_DAPM_INPUT("ADC1IN"),
SND_SOC_DAPM_INPUT("ADC2IN"),
};
static const struct snd_soc_dapm_route ad183x_adc_routes[] = {
{ "ADC", NULL, "ADC1IN" },
{ "ADC", NULL, "ADC2IN" },
};
static const struct snd_kcontrol_new ad183x_controls[] = {
/* ADC high-pass filter */
SOC_SINGLE("ADC High Pass Filter Switch", AD1836_ADC_CTRL1,
AD1836_ADC_HIGHPASS_FILTER, 1, 0),
/* DAC de-emphasis */
SOC_ENUM("Playback Deemphasis", ad1836_deemp_enum),
};
static const struct snd_soc_dapm_widget ad183x_dapm_widgets[] = {
SND_SOC_DAPM_DAC("DAC", "Playback", AD1836_DAC_CTRL1,
AD1836_DAC_POWERDOWN, 1),
SND_SOC_DAPM_ADC("ADC", "Capture", SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_SUPPLY("ADC_PWR", AD1836_ADC_CTRL1,
AD1836_ADC_POWERDOWN, 1, NULL, 0),
};
static const struct snd_soc_dapm_route ad183x_dapm_routes[] = {
{ "DAC", NULL, "ADC_PWR" },
{ "ADC", NULL, "ADC_PWR" },
};
static const DECLARE_TLV_DB_SCALE(ad1836_in_tlv, 0, 300, 0);
static const struct snd_kcontrol_new ad1836_controls[] = {
SOC_DOUBLE_TLV("ADC2 Capture Volume", AD1836_ADC_CTRL1, 3, 0, 4, 0,
ad1836_in_tlv),
};
/*
* DAI ops entries
*/
static int ad1836_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
/* at present, we support adc aux mode to interface with
* blackfin sport tdm mode
*/
case SND_SOC_DAIFMT_DSP_A:
break;
default:
return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_IB_IF:
break;
default:
return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
/* ALCLK,ABCLK are both output, AD1836 can only be master */
case SND_SOC_DAIFMT_CBM_CFM:
break;
default:
return -EINVAL;
}
return 0;
}
static int ad1836_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct ad1836_priv *ad1836 = snd_soc_codec_get_drvdata(dai->codec);
int word_len = 0;
/* bit size */
switch (params_width(params)) {
case 16:
word_len = AD1836_WORD_LEN_16;
break;
case 20:
word_len = AD1836_WORD_LEN_20;
break;
case 24:
case 32:
word_len = AD1836_WORD_LEN_24;
break;
default:
return -EINVAL;
}
regmap_update_bits(ad1836->regmap, AD1836_DAC_CTRL1,
AD1836_DAC_WORD_LEN_MASK,
word_len << AD1836_DAC_WORD_LEN_OFFSET);
regmap_update_bits(ad1836->regmap, AD1836_ADC_CTRL2,
AD1836_ADC_WORD_LEN_MASK,
word_len << AD1836_ADC_WORD_OFFSET);
return 0;
}
static const struct snd_soc_dai_ops ad1836_dai_ops = {
.hw_params = ad1836_hw_params,
.set_fmt = ad1836_set_dai_fmt,
};
#define AD183X_DAI(_name, num_dacs, num_adcs) \
{ \
.name = _name "-hifi", \
.playback = { \
.stream_name = "Playback", \
.channels_min = 2, \
.channels_max = (num_dacs) * 2, \
.rates = SNDRV_PCM_RATE_48000, \
.formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE, \
}, \
.capture = { \
.stream_name = "Capture", \
.channels_min = 2, \
.channels_max = (num_adcs) * 2, \
.rates = SNDRV_PCM_RATE_48000, \
.formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE, \
}, \
.ops = &ad1836_dai_ops, \
}
static struct snd_soc_dai_driver ad183x_dais[] = {
[AD1835] = AD183X_DAI("ad1835", 4, 1),
[AD1836] = AD183X_DAI("ad1836", 3, 2),
[AD1838] = AD183X_DAI("ad1838", 3, 1),
};
#ifdef CONFIG_PM
static int ad1836_suspend(struct snd_soc_codec *codec)
{
struct ad1836_priv *ad1836 = snd_soc_codec_get_drvdata(codec);
/* reset clock control mode */
return regmap_update_bits(ad1836->regmap, AD1836_ADC_CTRL2,
AD1836_ADC_SERFMT_MASK, 0);
}
static int ad1836_resume(struct snd_soc_codec *codec)
{
struct ad1836_priv *ad1836 = snd_soc_codec_get_drvdata(codec);
/* restore clock control mode */
return regmap_update_bits(ad1836->regmap, AD1836_ADC_CTRL2,
AD1836_ADC_SERFMT_MASK, AD1836_ADC_AUX);
}
#else
#define ad1836_suspend NULL
#define ad1836_resume NULL
#endif
static int ad1836_probe(struct snd_soc_codec *codec)
{
struct ad1836_priv *ad1836 = snd_soc_codec_get_drvdata(codec);
struct snd_soc_dapm_context *dapm = &codec->dapm;
int num_dacs, num_adcs;
int ret = 0;
int i;
num_dacs = ad183x_dais[ad1836->type].playback.channels_max / 2;
num_adcs = ad183x_dais[ad1836->type].capture.channels_max / 2;
/* default setting for ad1836 */
/* de-emphasis: 48kHz, power-on dac */
regmap_write(ad1836->regmap, AD1836_DAC_CTRL1, 0x300);
/* unmute dac channels */
regmap_write(ad1836->regmap, AD1836_DAC_CTRL2, 0x0);
/* high-pass filter enable, power-on adc */
regmap_write(ad1836->regmap, AD1836_ADC_CTRL1, 0x100);
/* unmute adc channles, adc aux mode */
regmap_write(ad1836->regmap, AD1836_ADC_CTRL2, 0x180);
/* volume */
for (i = 1; i <= num_dacs; ++i) {
regmap_write(ad1836->regmap, AD1836_DAC_L_VOL(i), 0x3FF);
regmap_write(ad1836->regmap, AD1836_DAC_R_VOL(i), 0x3FF);
}
if (ad1836->type == AD1836) {
/* left/right diff:PGA/MUX */
regmap_write(ad1836->regmap, AD1836_ADC_CTRL3, 0x3A);
ret = snd_soc_add_codec_controls(codec, ad1836_controls,
ARRAY_SIZE(ad1836_controls));
if (ret)
return ret;
} else {
regmap_write(ad1836->regmap, AD1836_ADC_CTRL3, 0x00);
}
ret = snd_soc_add_codec_controls(codec, ad183x_dac_controls, num_dacs * 2);
if (ret)
return ret;
ret = snd_soc_add_codec_controls(codec, ad183x_adc_controls, num_adcs);
if (ret)
return ret;
ret = snd_soc_dapm_new_controls(dapm, ad183x_dac_dapm_widgets, num_dacs);
if (ret)
return ret;
ret = snd_soc_dapm_new_controls(dapm, ad183x_adc_dapm_widgets, num_adcs);
if (ret)
return ret;
ret = snd_soc_dapm_add_routes(dapm, ad183x_dac_routes, num_dacs);
if (ret)
return ret;
ret = snd_soc_dapm_add_routes(dapm, ad183x_adc_routes, num_adcs);
if (ret)
return ret;
return ret;
}
/* power down chip */
static int ad1836_remove(struct snd_soc_codec *codec)
{
struct ad1836_priv *ad1836 = snd_soc_codec_get_drvdata(codec);
/* reset clock control mode */
return regmap_update_bits(ad1836->regmap, AD1836_ADC_CTRL2,
AD1836_ADC_SERFMT_MASK, 0);
}
static struct snd_soc_codec_driver soc_codec_dev_ad1836 = {
.probe = ad1836_probe,
.remove = ad1836_remove,
.suspend = ad1836_suspend,
.resume = ad1836_resume,
.controls = ad183x_controls,
.num_controls = ARRAY_SIZE(ad183x_controls),
.dapm_widgets = ad183x_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(ad183x_dapm_widgets),
.dapm_routes = ad183x_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(ad183x_dapm_routes),
};
static const struct reg_default ad1836_reg_defaults[] = {
{ AD1836_DAC_CTRL1, 0x0000 },
{ AD1836_DAC_CTRL2, 0x0000 },
{ AD1836_DAC_L_VOL(0), 0x0000 },
{ AD1836_DAC_R_VOL(0), 0x0000 },
{ AD1836_DAC_L_VOL(1), 0x0000 },
{ AD1836_DAC_R_VOL(1), 0x0000 },
{ AD1836_DAC_L_VOL(2), 0x0000 },
{ AD1836_DAC_R_VOL(2), 0x0000 },
{ AD1836_DAC_L_VOL(3), 0x0000 },
{ AD1836_DAC_R_VOL(3), 0x0000 },
{ AD1836_ADC_CTRL1, 0x0000 },
{ AD1836_ADC_CTRL2, 0x0000 },
{ AD1836_ADC_CTRL3, 0x0000 },
};
static const struct regmap_config ad1836_regmap_config = {
.val_bits = 12,
.reg_bits = 4,
.read_flag_mask = 0x08,
.max_register = AD1836_ADC_CTRL3,
.reg_defaults = ad1836_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(ad1836_reg_defaults),
.cache_type = REGCACHE_RBTREE,
};
static int ad1836_spi_probe(struct spi_device *spi)
{
struct ad1836_priv *ad1836;
int ret;
ad1836 = devm_kzalloc(&spi->dev, sizeof(struct ad1836_priv),
GFP_KERNEL);
if (ad1836 == NULL)
return -ENOMEM;
ad1836->regmap = devm_regmap_init_spi(spi, &ad1836_regmap_config);
if (IS_ERR(ad1836->regmap))
return PTR_ERR(ad1836->regmap);
ad1836->type = spi_get_device_id(spi)->driver_data;
spi_set_drvdata(spi, ad1836);
ret = snd_soc_register_codec(&spi->dev,
&soc_codec_dev_ad1836, &ad183x_dais[ad1836->type], 1);
return ret;
}
static int ad1836_spi_remove(struct spi_device *spi)
{
snd_soc_unregister_codec(&spi->dev);
return 0;
}
static const struct spi_device_id ad1836_ids[] = {
{ "ad1835", AD1835 },
{ "ad1836", AD1836 },
{ "ad1837", AD1835 },
{ "ad1838", AD1838 },
{ "ad1839", AD1838 },
{ },
};
MODULE_DEVICE_TABLE(spi, ad1836_ids);
static struct spi_driver ad1836_spi_driver = {
.driver = {
.name = "ad1836",
.owner = THIS_MODULE,
},
.probe = ad1836_spi_probe,
.remove = ad1836_spi_remove,
.id_table = ad1836_ids,
};
module_spi_driver(ad1836_spi_driver);
MODULE_DESCRIPTION("ASoC ad1836 driver");
MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
MODULE_LICENSE("GPL");

51
sound/soc/codecs/ad1836.h Normal file
View file

@ -0,0 +1,51 @@
/*
* Audio Codec driver supporting:
* AD1835A, AD1836, AD1837A, AD1838A, AD1839A
*
* Copyright 2009-2011 Analog Devices Inc.
*
* Licensed under the GPL-2 or later.
*/
#ifndef __AD1836_H__
#define __AD1836_H__
#define AD1836_DAC_CTRL1 0
#define AD1836_DAC_POWERDOWN 2
#define AD1836_DAC_SERFMT_MASK 0xE0
#define AD1836_DAC_SERFMT_PCK256 (0x4 << 5)
#define AD1836_DAC_SERFMT_PCK128 (0x5 << 5)
#define AD1836_DAC_WORD_LEN_MASK 0x18
#define AD1836_DAC_WORD_LEN_OFFSET 3
#define AD1836_DAC_CTRL2 1
/* These macros are one-based. So AD183X_MUTE_LEFT(1) will return the mute bit
* for the first ADC/DAC */
#define AD1836_MUTE_LEFT(x) (((x) * 2) - 2)
#define AD1836_MUTE_RIGHT(x) (((x) * 2) - 1)
#define AD1836_DAC_L_VOL(x) ((x) * 2)
#define AD1836_DAC_R_VOL(x) (1 + ((x) * 2))
#define AD1836_ADC_CTRL1 12
#define AD1836_ADC_POWERDOWN 7
#define AD1836_ADC_HIGHPASS_FILTER 8
#define AD1836_ADC_CTRL2 13
#define AD1836_ADC_WORD_LEN_MASK 0x30
#define AD1836_ADC_WORD_OFFSET 4
#define AD1836_ADC_SERFMT_MASK (7 << 6)
#define AD1836_ADC_SERFMT_PCK256 (0x4 << 6)
#define AD1836_ADC_SERFMT_PCK128 (0x5 << 6)
#define AD1836_ADC_AUX (0x6 << 6)
#define AD1836_ADC_CTRL3 14
#define AD1836_NUM_REGS 16
#define AD1836_WORD_LEN_24 0x0
#define AD1836_WORD_LEN_20 0x1
#define AD1836_WORD_LEN_16 0x2
#endif

View file

@ -0,0 +1,54 @@
/*
* AD1936/AD1937 audio driver
*
* Copyright 2014 Analog Devices Inc.
*
* Licensed under the GPL-2.
*/
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
#include <sound/soc.h>
#include "ad193x.h"
static const struct i2c_device_id ad193x_id[] = {
{ "ad1936", 0 },
{ "ad1937", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, ad193x_id);
static int ad193x_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct regmap_config config;
config = ad193x_regmap_config;
config.val_bits = 8;
config.reg_bits = 8;
return ad193x_probe(&client->dev, devm_regmap_init_i2c(client, &config));
}
static int ad193x_i2c_remove(struct i2c_client *client)
{
snd_soc_unregister_codec(&client->dev);
return 0;
}
static struct i2c_driver ad193x_i2c_driver = {
.driver = {
.name = "ad193x",
},
.probe = ad193x_i2c_probe,
.remove = ad193x_i2c_remove,
.id_table = ad193x_id,
};
module_i2c_driver(ad193x_i2c_driver);
MODULE_DESCRIPTION("ASoC AD1936/AD1937 audio CODEC driver");
MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,48 @@
/*
* AD1938/AD1939 audio driver
*
* Copyright 2014 Analog Devices Inc.
*
* Licensed under the GPL-2.
*/
#include <linux/module.h>
#include <linux/spi/spi.h>
#include <linux/regmap.h>
#include <sound/soc.h>
#include "ad193x.h"
static int ad193x_spi_probe(struct spi_device *spi)
{
struct regmap_config config;
config = ad193x_regmap_config;
config.val_bits = 8;
config.reg_bits = 16;
config.read_flag_mask = 0x09;
config.write_flag_mask = 0x08;
return ad193x_probe(&spi->dev, devm_regmap_init_spi(spi, &config));
}
static int ad193x_spi_remove(struct spi_device *spi)
{
snd_soc_unregister_codec(&spi->dev);
return 0;
}
static struct spi_driver ad193x_spi_driver = {
.driver = {
.name = "ad193x",
.owner = THIS_MODULE,
},
.probe = ad193x_spi_probe,
.remove = ad193x_spi_remove,
};
module_spi_driver(ad193x_spi_driver);
MODULE_DESCRIPTION("ASoC AD1938/AD1939 audio CODEC driver");
MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
MODULE_LICENSE("GPL");

388
sound/soc/codecs/ad193x.c Normal file
View file

@ -0,0 +1,388 @@
/*
* AD193X Audio Codec driver supporting AD1936/7/8/9
*
* Copyright 2010 Analog Devices Inc.
*
* Licensed under the GPL-2 or later.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/initval.h>
#include <sound/soc.h>
#include <sound/tlv.h>
#include "ad193x.h"
/* codec private data */
struct ad193x_priv {
struct regmap *regmap;
int sysclk;
};
/*
* AD193X volume/mute/de-emphasis etc. controls
*/
static const char * const ad193x_deemp[] = {"None", "48kHz", "44.1kHz", "32kHz"};
static SOC_ENUM_SINGLE_DECL(ad193x_deemp_enum, AD193X_DAC_CTRL2, 1,
ad193x_deemp);
static const DECLARE_TLV_DB_MINMAX(adau193x_tlv, -9563, 0);
static const struct snd_kcontrol_new ad193x_snd_controls[] = {
/* DAC volume control */
SOC_DOUBLE_R_TLV("DAC1 Volume", AD193X_DAC_L1_VOL,
AD193X_DAC_R1_VOL, 0, 0xFF, 1, adau193x_tlv),
SOC_DOUBLE_R_TLV("DAC2 Volume", AD193X_DAC_L2_VOL,
AD193X_DAC_R2_VOL, 0, 0xFF, 1, adau193x_tlv),
SOC_DOUBLE_R_TLV("DAC3 Volume", AD193X_DAC_L3_VOL,
AD193X_DAC_R3_VOL, 0, 0xFF, 1, adau193x_tlv),
SOC_DOUBLE_R_TLV("DAC4 Volume", AD193X_DAC_L4_VOL,
AD193X_DAC_R4_VOL, 0, 0xFF, 1, adau193x_tlv),
/* ADC switch control */
SOC_DOUBLE("ADC1 Switch", AD193X_ADC_CTRL0, AD193X_ADCL1_MUTE,
AD193X_ADCR1_MUTE, 1, 1),
SOC_DOUBLE("ADC2 Switch", AD193X_ADC_CTRL0, AD193X_ADCL2_MUTE,
AD193X_ADCR2_MUTE, 1, 1),
/* DAC switch control */
SOC_DOUBLE("DAC1 Switch", AD193X_DAC_CHNL_MUTE, AD193X_DACL1_MUTE,
AD193X_DACR1_MUTE, 1, 1),
SOC_DOUBLE("DAC2 Switch", AD193X_DAC_CHNL_MUTE, AD193X_DACL2_MUTE,
AD193X_DACR2_MUTE, 1, 1),
SOC_DOUBLE("DAC3 Switch", AD193X_DAC_CHNL_MUTE, AD193X_DACL3_MUTE,
AD193X_DACR3_MUTE, 1, 1),
SOC_DOUBLE("DAC4 Switch", AD193X_DAC_CHNL_MUTE, AD193X_DACL4_MUTE,
AD193X_DACR4_MUTE, 1, 1),
/* ADC high-pass filter */
SOC_SINGLE("ADC High Pass Filter Switch", AD193X_ADC_CTRL0,
AD193X_ADC_HIGHPASS_FILTER, 1, 0),
/* DAC de-emphasis */
SOC_ENUM("Playback Deemphasis", ad193x_deemp_enum),
};
static const struct snd_soc_dapm_widget ad193x_dapm_widgets[] = {
SND_SOC_DAPM_DAC("DAC", "Playback", AD193X_DAC_CTRL0, 0, 1),
SND_SOC_DAPM_ADC("ADC", "Capture", SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_SUPPLY("PLL_PWR", AD193X_PLL_CLK_CTRL0, 0, 1, NULL, 0),
SND_SOC_DAPM_SUPPLY("ADC_PWR", AD193X_ADC_CTRL0, 0, 1, NULL, 0),
SND_SOC_DAPM_SUPPLY("SYSCLK", AD193X_PLL_CLK_CTRL0, 7, 0, NULL, 0),
SND_SOC_DAPM_OUTPUT("DAC1OUT"),
SND_SOC_DAPM_OUTPUT("DAC2OUT"),
SND_SOC_DAPM_OUTPUT("DAC3OUT"),
SND_SOC_DAPM_OUTPUT("DAC4OUT"),
SND_SOC_DAPM_INPUT("ADC1IN"),
SND_SOC_DAPM_INPUT("ADC2IN"),
};
static const struct snd_soc_dapm_route audio_paths[] = {
{ "DAC", NULL, "SYSCLK" },
{ "ADC", NULL, "SYSCLK" },
{ "DAC", NULL, "ADC_PWR" },
{ "ADC", NULL, "ADC_PWR" },
{ "DAC1OUT", NULL, "DAC" },
{ "DAC2OUT", NULL, "DAC" },
{ "DAC3OUT", NULL, "DAC" },
{ "DAC4OUT", NULL, "DAC" },
{ "ADC", NULL, "ADC1IN" },
{ "ADC", NULL, "ADC2IN" },
{ "SYSCLK", NULL, "PLL_PWR" },
};
/*
* DAI ops entries
*/
static int ad193x_mute(struct snd_soc_dai *dai, int mute)
{
struct ad193x_priv *ad193x = snd_soc_codec_get_drvdata(dai->codec);
if (mute)
regmap_update_bits(ad193x->regmap, AD193X_DAC_CTRL2,
AD193X_DAC_MASTER_MUTE,
AD193X_DAC_MASTER_MUTE);
else
regmap_update_bits(ad193x->regmap, AD193X_DAC_CTRL2,
AD193X_DAC_MASTER_MUTE, 0);
return 0;
}
static int ad193x_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
unsigned int rx_mask, int slots, int width)
{
struct ad193x_priv *ad193x = snd_soc_codec_get_drvdata(dai->codec);
unsigned int channels;
switch (slots) {
case 2:
channels = AD193X_2_CHANNELS;
break;
case 4:
channels = AD193X_4_CHANNELS;
break;
case 8:
channels = AD193X_8_CHANNELS;
break;
case 16:
channels = AD193X_16_CHANNELS;
break;
default:
return -EINVAL;
}
regmap_update_bits(ad193x->regmap, AD193X_DAC_CTRL1,
AD193X_DAC_CHAN_MASK, channels << AD193X_DAC_CHAN_SHFT);
regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL2,
AD193X_ADC_CHAN_MASK, channels << AD193X_ADC_CHAN_SHFT);
return 0;
}
static int ad193x_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
struct ad193x_priv *ad193x = snd_soc_codec_get_drvdata(codec_dai->codec);
unsigned int adc_serfmt = 0;
unsigned int adc_fmt = 0;
unsigned int dac_fmt = 0;
/* At present, the driver only support AUX ADC mode(SND_SOC_DAIFMT_I2S
* with TDM) and ADC&DAC TDM mode(SND_SOC_DAIFMT_DSP_A)
*/
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
adc_serfmt |= AD193X_ADC_SERFMT_TDM;
break;
case SND_SOC_DAIFMT_DSP_A:
adc_serfmt |= AD193X_ADC_SERFMT_AUX;
break;
default:
return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF: /* normal bit clock + frame */
break;
case SND_SOC_DAIFMT_NB_IF: /* normal bclk + invert frm */
adc_fmt |= AD193X_ADC_LEFT_HIGH;
dac_fmt |= AD193X_DAC_LEFT_HIGH;
break;
case SND_SOC_DAIFMT_IB_NF: /* invert bclk + normal frm */
adc_fmt |= AD193X_ADC_BCLK_INV;
dac_fmt |= AD193X_DAC_BCLK_INV;
break;
case SND_SOC_DAIFMT_IB_IF: /* invert bclk + frm */
adc_fmt |= AD193X_ADC_LEFT_HIGH;
adc_fmt |= AD193X_ADC_BCLK_INV;
dac_fmt |= AD193X_DAC_LEFT_HIGH;
dac_fmt |= AD193X_DAC_BCLK_INV;
break;
default:
return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBM_CFM: /* codec clk & frm master */
adc_fmt |= AD193X_ADC_LCR_MASTER;
adc_fmt |= AD193X_ADC_BCLK_MASTER;
dac_fmt |= AD193X_DAC_LCR_MASTER;
dac_fmt |= AD193X_DAC_BCLK_MASTER;
break;
case SND_SOC_DAIFMT_CBS_CFM: /* codec clk slave & frm master */
adc_fmt |= AD193X_ADC_LCR_MASTER;
dac_fmt |= AD193X_DAC_LCR_MASTER;
break;
case SND_SOC_DAIFMT_CBM_CFS: /* codec clk master & frame slave */
adc_fmt |= AD193X_ADC_BCLK_MASTER;
dac_fmt |= AD193X_DAC_BCLK_MASTER;
break;
case SND_SOC_DAIFMT_CBS_CFS: /* codec clk & frm slave */
break;
default:
return -EINVAL;
}
regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL1,
AD193X_ADC_SERFMT_MASK, adc_serfmt);
regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL2,
AD193X_ADC_FMT_MASK, adc_fmt);
regmap_update_bits(ad193x->regmap, AD193X_DAC_CTRL1,
AD193X_DAC_FMT_MASK, dac_fmt);
return 0;
}
static int ad193x_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = codec_dai->codec;
struct ad193x_priv *ad193x = snd_soc_codec_get_drvdata(codec);
switch (freq) {
case 12288000:
case 18432000:
case 24576000:
case 36864000:
ad193x->sysclk = freq;
return 0;
}
return -EINVAL;
}
static int ad193x_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
int word_len = 0, master_rate = 0;
struct snd_soc_codec *codec = dai->codec;
struct ad193x_priv *ad193x = snd_soc_codec_get_drvdata(codec);
/* bit size */
switch (params_width(params)) {
case 16:
word_len = 3;
break;
case 20:
word_len = 1;
break;
case 24:
case 32:
word_len = 0;
break;
}
switch (ad193x->sysclk) {
case 12288000:
master_rate = AD193X_PLL_INPUT_256;
break;
case 18432000:
master_rate = AD193X_PLL_INPUT_384;
break;
case 24576000:
master_rate = AD193X_PLL_INPUT_512;
break;
case 36864000:
master_rate = AD193X_PLL_INPUT_768;
break;
}
regmap_update_bits(ad193x->regmap, AD193X_PLL_CLK_CTRL0,
AD193X_PLL_INPUT_MASK, master_rate);
regmap_update_bits(ad193x->regmap, AD193X_DAC_CTRL2,
AD193X_DAC_WORD_LEN_MASK,
word_len << AD193X_DAC_WORD_LEN_SHFT);
regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL1,
AD193X_ADC_WORD_LEN_MASK, word_len);
return 0;
}
static const struct snd_soc_dai_ops ad193x_dai_ops = {
.hw_params = ad193x_hw_params,
.digital_mute = ad193x_mute,
.set_tdm_slot = ad193x_set_tdm_slot,
.set_sysclk = ad193x_set_dai_sysclk,
.set_fmt = ad193x_set_dai_fmt,
};
/* codec DAI instance */
static struct snd_soc_dai_driver ad193x_dai = {
.name = "ad193x-hifi",
.playback = {
.stream_name = "Playback",
.channels_min = 2,
.channels_max = 8,
.rates = SNDRV_PCM_RATE_48000,
.formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE,
},
.capture = {
.stream_name = "Capture",
.channels_min = 2,
.channels_max = 4,
.rates = SNDRV_PCM_RATE_48000,
.formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE,
},
.ops = &ad193x_dai_ops,
};
static int ad193x_codec_probe(struct snd_soc_codec *codec)
{
struct ad193x_priv *ad193x = snd_soc_codec_get_drvdata(codec);
/* default setting for ad193x */
/* unmute dac channels */
regmap_write(ad193x->regmap, AD193X_DAC_CHNL_MUTE, 0x0);
/* de-emphasis: 48kHz, powedown dac */
regmap_write(ad193x->regmap, AD193X_DAC_CTRL2, 0x1A);
/* powerdown dac, dac in tdm mode */
regmap_write(ad193x->regmap, AD193X_DAC_CTRL0, 0x41);
/* high-pass filter enable */
regmap_write(ad193x->regmap, AD193X_ADC_CTRL0, 0x3);
/* sata delay=1, adc aux mode */
regmap_write(ad193x->regmap, AD193X_ADC_CTRL1, 0x43);
/* pll input: mclki/xi */
regmap_write(ad193x->regmap, AD193X_PLL_CLK_CTRL0, 0x99); /* mclk=24.576Mhz: 0x9D; mclk=12.288Mhz: 0x99 */
regmap_write(ad193x->regmap, AD193X_PLL_CLK_CTRL1, 0x04);
return 0;
}
static struct snd_soc_codec_driver soc_codec_dev_ad193x = {
.probe = ad193x_codec_probe,
.controls = ad193x_snd_controls,
.num_controls = ARRAY_SIZE(ad193x_snd_controls),
.dapm_widgets = ad193x_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(ad193x_dapm_widgets),
.dapm_routes = audio_paths,
.num_dapm_routes = ARRAY_SIZE(audio_paths),
};
static bool adau193x_reg_volatile(struct device *dev, unsigned int reg)
{
return false;
}
const struct regmap_config ad193x_regmap_config = {
.max_register = AD193X_NUM_REGS - 1,
.volatile_reg = adau193x_reg_volatile,
};
EXPORT_SYMBOL_GPL(ad193x_regmap_config);
int ad193x_probe(struct device *dev, struct regmap *regmap)
{
struct ad193x_priv *ad193x;
if (IS_ERR(regmap))
return PTR_ERR(regmap);
ad193x = devm_kzalloc(dev, sizeof(*ad193x), GFP_KERNEL);
if (ad193x == NULL)
return -ENOMEM;
ad193x->regmap = regmap;
dev_set_drvdata(dev, ad193x);
return snd_soc_register_codec(dev, &soc_codec_dev_ad193x,
&ad193x_dai, 1);
}
EXPORT_SYMBOL_GPL(ad193x_probe);
MODULE_DESCRIPTION("ASoC ad193x driver");
MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
MODULE_LICENSE("GPL");

92
sound/soc/codecs/ad193x.h Normal file
View file

@ -0,0 +1,92 @@
/*
* AD193X Audio Codec driver
*
* Copyright 2010 Analog Devices Inc.
*
* Licensed under the GPL-2 or later.
*/
#ifndef __AD193X_H__
#define __AD193X_H__
#include <linux/regmap.h>
struct device;
extern const struct regmap_config ad193x_regmap_config;
int ad193x_probe(struct device *dev, struct regmap *regmap);
#define AD193X_PLL_CLK_CTRL0 0x00
#define AD193X_PLL_POWERDOWN 0x01
#define AD193X_PLL_INPUT_MASK 0x6
#define AD193X_PLL_INPUT_256 (0 << 1)
#define AD193X_PLL_INPUT_384 (1 << 1)
#define AD193X_PLL_INPUT_512 (2 << 1)
#define AD193X_PLL_INPUT_768 (3 << 1)
#define AD193X_PLL_CLK_CTRL1 0x01
#define AD193X_DAC_CTRL0 0x02
#define AD193X_DAC_POWERDOWN 0x01
#define AD193X_DAC_SERFMT_MASK 0xC0
#define AD193X_DAC_SERFMT_STEREO (0 << 6)
#define AD193X_DAC_SERFMT_TDM (1 << 6)
#define AD193X_DAC_CTRL1 0x03
#define AD193X_DAC_CHAN_SHFT 1
#define AD193X_DAC_CHAN_MASK (3 << AD193X_DAC_CHAN_SHFT)
#define AD193X_DAC_LCR_MASTER (1 << 4)
#define AD193X_DAC_BCLK_MASTER (1 << 5)
#define AD193X_DAC_LEFT_HIGH (1 << 3)
#define AD193X_DAC_BCLK_INV (1 << 7)
#define AD193X_DAC_FMT_MASK (AD193X_DAC_LCR_MASTER | \
AD193X_DAC_BCLK_MASTER | AD193X_DAC_LEFT_HIGH | AD193X_DAC_BCLK_INV)
#define AD193X_DAC_CTRL2 0x04
#define AD193X_DAC_WORD_LEN_SHFT 3
#define AD193X_DAC_WORD_LEN_MASK 0x18
#define AD193X_DAC_MASTER_MUTE 1
#define AD193X_DAC_CHNL_MUTE 0x05
#define AD193X_DACL1_MUTE 0
#define AD193X_DACR1_MUTE 1
#define AD193X_DACL2_MUTE 2
#define AD193X_DACR2_MUTE 3
#define AD193X_DACL3_MUTE 4
#define AD193X_DACR3_MUTE 5
#define AD193X_DACL4_MUTE 6
#define AD193X_DACR4_MUTE 7
#define AD193X_DAC_L1_VOL 0x06
#define AD193X_DAC_R1_VOL 0x07
#define AD193X_DAC_L2_VOL 0x08
#define AD193X_DAC_R2_VOL 0x09
#define AD193X_DAC_L3_VOL 0x0a
#define AD193X_DAC_R3_VOL 0x0b
#define AD193X_DAC_L4_VOL 0x0c
#define AD193X_DAC_R4_VOL 0x0d
#define AD193X_ADC_CTRL0 0x0e
#define AD193X_ADC_POWERDOWN 0x01
#define AD193X_ADC_HIGHPASS_FILTER 1
#define AD193X_ADCL1_MUTE 2
#define AD193X_ADCR1_MUTE 3
#define AD193X_ADCL2_MUTE 4
#define AD193X_ADCR2_MUTE 5
#define AD193X_ADC_CTRL1 0x0f
#define AD193X_ADC_SERFMT_MASK 0x60
#define AD193X_ADC_SERFMT_STEREO (0 << 5)
#define AD193X_ADC_SERFMT_TDM (1 << 5)
#define AD193X_ADC_SERFMT_AUX (2 << 5)
#define AD193X_ADC_WORD_LEN_MASK 0x3
#define AD193X_ADC_CTRL2 0x10
#define AD193X_ADC_CHAN_SHFT 4
#define AD193X_ADC_CHAN_MASK (3 << AD193X_ADC_CHAN_SHFT)
#define AD193X_ADC_LCR_MASTER (1 << 3)
#define AD193X_ADC_BCLK_MASTER (1 << 6)
#define AD193X_ADC_LEFT_HIGH (1 << 2)
#define AD193X_ADC_BCLK_INV (1 << 1)
#define AD193X_ADC_FMT_MASK (AD193X_ADC_LCR_MASTER | \
AD193X_ADC_BCLK_MASTER | AD193X_ADC_LEFT_HIGH | AD193X_ADC_BCLK_INV)
#define AD193X_2_CHANNELS 0
#define AD193X_4_CHANNELS 1
#define AD193X_8_CHANNELS 2
#define AD193X_16_CHANNELS 3
#define AD193X_NUM_REGS 17
#endif

326
sound/soc/codecs/ad1980.c Normal file
View file

@ -0,0 +1,326 @@
/*
* ad1980.c -- ALSA Soc AD1980 codec support
*
* Copyright: Analog Device Inc.
* Author: Roy Huang <roy.huang@analog.com>
* Cliff Cai <cliff.cai@analog.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*/
/*
* WARNING:
*
* Because Analog Devices Inc. discontinued the ad1980 sound chip since
* Sep. 2009, this ad1980 driver is not maintained, tested and supported
* by ADI now.
*/
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/ac97_codec.h>
#include <sound/initval.h>
#include <sound/soc.h>
#include "ad1980.h"
/*
* AD1980 register cache
*/
static const u16 ad1980_reg[] = {
0x0090, 0x8000, 0x8000, 0x8000, /* 0 - 6 */
0x0000, 0x0000, 0x8008, 0x8008, /* 8 - e */
0x8808, 0x8808, 0x0000, 0x8808, /* 10 - 16 */
0x8808, 0x0000, 0x8000, 0x0000, /* 18 - 1e */
0x0000, 0x0000, 0x0000, 0x0000, /* 20 - 26 */
0x03c7, 0x0000, 0xbb80, 0xbb80, /* 28 - 2e */
0xbb80, 0xbb80, 0x0000, 0x8080, /* 30 - 36 */
0x8080, 0x2000, 0x0000, 0x0000, /* 38 - 3e */
0x0000, 0x0000, 0x0000, 0x0000, /* reserved */
0x0000, 0x0000, 0x0000, 0x0000, /* reserved */
0x0000, 0x0000, 0x0000, 0x0000, /* reserved */
0x0000, 0x0000, 0x0000, 0x0000, /* reserved */
0x8080, 0x0000, 0x0000, 0x0000, /* 60 - 66 */
0x0000, 0x0000, 0x0000, 0x0000, /* reserved */
0x0000, 0x0000, 0x1001, 0x0000, /* 70 - 76 */
0x0000, 0x0000, 0x4144, 0x5370 /* 78 - 7e */
};
static const char *ad1980_rec_sel[] = {"Mic", "CD", "NC", "AUX", "Line",
"Stereo Mix", "Mono Mix", "Phone"};
static SOC_ENUM_DOUBLE_DECL(ad1980_cap_src,
AC97_REC_SEL, 8, 0, ad1980_rec_sel);
static const struct snd_kcontrol_new ad1980_snd_ac97_controls[] = {
SOC_DOUBLE("Master Playback Volume", AC97_MASTER, 8, 0, 31, 1),
SOC_SINGLE("Master Playback Switch", AC97_MASTER, 15, 1, 1),
SOC_DOUBLE("Headphone Playback Volume", AC97_HEADPHONE, 8, 0, 31, 1),
SOC_SINGLE("Headphone Playback Switch", AC97_HEADPHONE, 15, 1, 1),
SOC_DOUBLE("PCM Playback Volume", AC97_PCM, 8, 0, 31, 1),
SOC_SINGLE("PCM Playback Switch", AC97_PCM, 15, 1, 1),
SOC_DOUBLE("PCM Capture Volume", AC97_REC_GAIN, 8, 0, 31, 0),
SOC_SINGLE("PCM Capture Switch", AC97_REC_GAIN, 15, 1, 1),
SOC_SINGLE("Mono Playback Volume", AC97_MASTER_MONO, 0, 31, 1),
SOC_SINGLE("Mono Playback Switch", AC97_MASTER_MONO, 15, 1, 1),
SOC_SINGLE("Phone Capture Volume", AC97_PHONE, 0, 31, 1),
SOC_SINGLE("Phone Capture Switch", AC97_PHONE, 15, 1, 1),
SOC_SINGLE("Mic Volume", AC97_MIC, 0, 31, 1),
SOC_SINGLE("Mic Switch", AC97_MIC, 15, 1, 1),
SOC_SINGLE("Stereo Mic Switch", AC97_AD_MISC, 6, 1, 0),
SOC_DOUBLE("Line HP Swap Switch", AC97_AD_MISC, 10, 5, 1, 0),
SOC_DOUBLE("Surround Playback Volume", AC97_SURROUND_MASTER, 8, 0, 31, 1),
SOC_DOUBLE("Surround Playback Switch", AC97_SURROUND_MASTER, 15, 7, 1, 1),
SOC_DOUBLE("Center/LFE Playback Volume", AC97_CENTER_LFE_MASTER, 8, 0, 31, 1),
SOC_DOUBLE("Center/LFE Playback Switch", AC97_CENTER_LFE_MASTER, 15, 7, 1, 1),
SOC_ENUM("Capture Source", ad1980_cap_src),
SOC_SINGLE("Mic Boost Switch", AC97_MIC, 6, 1, 0),
};
static const struct snd_soc_dapm_widget ad1980_dapm_widgets[] = {
SND_SOC_DAPM_INPUT("MIC1"),
SND_SOC_DAPM_INPUT("MIC2"),
SND_SOC_DAPM_INPUT("CD_L"),
SND_SOC_DAPM_INPUT("CD_R"),
SND_SOC_DAPM_INPUT("AUX_L"),
SND_SOC_DAPM_INPUT("AUX_R"),
SND_SOC_DAPM_INPUT("LINE_IN_L"),
SND_SOC_DAPM_INPUT("LINE_IN_R"),
SND_SOC_DAPM_OUTPUT("LFE_OUT"),
SND_SOC_DAPM_OUTPUT("CENTER_OUT"),
SND_SOC_DAPM_OUTPUT("LINE_OUT_L"),
SND_SOC_DAPM_OUTPUT("LINE_OUT_R"),
SND_SOC_DAPM_OUTPUT("MONO_OUT"),
SND_SOC_DAPM_OUTPUT("HP_OUT_L"),
SND_SOC_DAPM_OUTPUT("HP_OUT_R"),
};
static const struct snd_soc_dapm_route ad1980_dapm_routes[] = {
{ "Capture", NULL, "MIC1" },
{ "Capture", NULL, "MIC2" },
{ "Capture", NULL, "CD_L" },
{ "Capture", NULL, "CD_R" },
{ "Capture", NULL, "AUX_L" },
{ "Capture", NULL, "AUX_R" },
{ "Capture", NULL, "LINE_IN_L" },
{ "Capture", NULL, "LINE_IN_R" },
{ "LFE_OUT", NULL, "Playback" },
{ "CENTER_OUT", NULL, "Playback" },
{ "LINE_OUT_L", NULL, "Playback" },
{ "LINE_OUT_R", NULL, "Playback" },
{ "MONO_OUT", NULL, "Playback" },
{ "HP_OUT_L", NULL, "Playback" },
{ "HP_OUT_R", NULL, "Playback" },
};
static unsigned int ac97_read(struct snd_soc_codec *codec,
unsigned int reg)
{
u16 *cache = codec->reg_cache;
switch (reg) {
case AC97_RESET:
case AC97_INT_PAGING:
case AC97_POWERDOWN:
case AC97_EXTENDED_STATUS:
case AC97_VENDOR_ID1:
case AC97_VENDOR_ID2:
return soc_ac97_ops->read(codec->ac97, reg);
default:
reg = reg >> 1;
if (reg >= ARRAY_SIZE(ad1980_reg))
return -EINVAL;
return cache[reg];
}
}
static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
unsigned int val)
{
u16 *cache = codec->reg_cache;
soc_ac97_ops->write(codec->ac97, reg, val);
reg = reg >> 1;
if (reg < ARRAY_SIZE(ad1980_reg))
cache[reg] = val;
return 0;
}
static struct snd_soc_dai_driver ad1980_dai = {
.name = "ad1980-hifi",
.ac97_control = 1,
.playback = {
.stream_name = "Playback",
.channels_min = 2,
.channels_max = 6,
.rates = SNDRV_PCM_RATE_48000,
.formats = SND_SOC_STD_AC97_FMTS, },
.capture = {
.stream_name = "Capture",
.channels_min = 2,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_48000,
.formats = SND_SOC_STD_AC97_FMTS, },
};
static int ad1980_reset(struct snd_soc_codec *codec, int try_warm)
{
unsigned int retry_cnt = 0;
do {
if (try_warm && soc_ac97_ops->warm_reset) {
soc_ac97_ops->warm_reset(codec->ac97);
if (ac97_read(codec, AC97_RESET) == 0x0090)
return 1;
}
soc_ac97_ops->reset(codec->ac97);
/*
* Set bit 16slot in register 74h, then every slot will has only
* 16 bits. This command is sent out in 20bit mode, in which
* case the first nibble of data is eaten by the addr. (Tag is
* always 16 bit)
*/
ac97_write(codec, AC97_AD_SERIAL_CFG, 0x9900);
if (ac97_read(codec, AC97_RESET) == 0x0090)
return 0;
} while (retry_cnt++ < 10);
printk(KERN_ERR "AD1980 AC97 reset failed\n");
return -EIO;
}
static int ad1980_soc_probe(struct snd_soc_codec *codec)
{
int ret;
u16 vendor_id2;
u16 ext_status;
printk(KERN_INFO "AD1980 SoC Audio Codec\n");
ret = snd_soc_new_ac97_codec(codec, soc_ac97_ops, 0);
if (ret < 0) {
printk(KERN_ERR "ad1980: failed to register AC97 codec\n");
return ret;
}
ret = ad1980_reset(codec, 0);
if (ret < 0) {
printk(KERN_ERR "Failed to reset AD1980: AC97 link error\n");
goto reset_err;
}
/* Read out vendor ID to make sure it is ad1980 */
if (ac97_read(codec, AC97_VENDOR_ID1) != 0x4144) {
ret = -ENODEV;
goto reset_err;
}
vendor_id2 = ac97_read(codec, AC97_VENDOR_ID2);
if (vendor_id2 != 0x5370) {
if (vendor_id2 != 0x5374) {
ret = -ENODEV;
goto reset_err;
} else {
printk(KERN_WARNING "ad1980: "
"Found AD1981 - only 2/2 IN/OUT Channels "
"supported\n");
}
}
/* unmute captures and playbacks volume */
ac97_write(codec, AC97_MASTER, 0x0000);
ac97_write(codec, AC97_PCM, 0x0000);
ac97_write(codec, AC97_REC_GAIN, 0x0000);
ac97_write(codec, AC97_CENTER_LFE_MASTER, 0x0000);
ac97_write(codec, AC97_SURROUND_MASTER, 0x0000);
/*power on LFE/CENTER/Surround DACs*/
ext_status = ac97_read(codec, AC97_EXTENDED_STATUS);
ac97_write(codec, AC97_EXTENDED_STATUS, ext_status&~0x3800);
snd_soc_add_codec_controls(codec, ad1980_snd_ac97_controls,
ARRAY_SIZE(ad1980_snd_ac97_controls));
return 0;
reset_err:
snd_soc_free_ac97_codec(codec);
return ret;
}
static int ad1980_soc_remove(struct snd_soc_codec *codec)
{
snd_soc_free_ac97_codec(codec);
return 0;
}
static struct snd_soc_codec_driver soc_codec_dev_ad1980 = {
.probe = ad1980_soc_probe,
.remove = ad1980_soc_remove,
.reg_cache_size = ARRAY_SIZE(ad1980_reg),
.reg_word_size = sizeof(u16),
.reg_cache_default = ad1980_reg,
.reg_cache_step = 2,
.write = ac97_write,
.read = ac97_read,
.dapm_widgets = ad1980_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(ad1980_dapm_widgets),
.dapm_routes = ad1980_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(ad1980_dapm_routes),
};
static int ad1980_probe(struct platform_device *pdev)
{
return snd_soc_register_codec(&pdev->dev,
&soc_codec_dev_ad1980, &ad1980_dai, 1);
}
static int ad1980_remove(struct platform_device *pdev)
{
snd_soc_unregister_codec(&pdev->dev);
return 0;
}
static struct platform_driver ad1980_codec_driver = {
.driver = {
.name = "ad1980",
.owner = THIS_MODULE,
},
.probe = ad1980_probe,
.remove = ad1980_remove,
};
module_platform_driver(ad1980_codec_driver);
MODULE_DESCRIPTION("ASoC ad1980 driver (Obsolete)");
MODULE_AUTHOR("Roy Huang, Cliff Cai");
MODULE_LICENSE("GPL");

26
sound/soc/codecs/ad1980.h Normal file
View file

@ -0,0 +1,26 @@
/*
* ad1980.h -- ad1980 Soc Audio driver
*
* WARNING:
*
* Because Analog Devices Inc. discontinued the ad1980 sound chip since
* Sep. 2009, this ad1980 driver is not maintained, tested and supported
* by ADI now.
*/
#ifndef _AD1980_H
#define _AD1980_H
/* Bit definition of Power-Down Control/Status Register */
#define ADC 0x0001
#define DAC 0x0002
#define ANL 0x0004
#define REF 0x0008
#define PR0 0x0100
#define PR1 0x0200
#define PR2 0x0400
#define PR3 0x0800
#define PR4 0x1000
#define PR5 0x2000
#define PR6 0x4000
#endif

View file

@ -0,0 +1,90 @@
/*
* ad73311.c -- ALSA Soc AD73311 codec support
*
* Copyright: Analog Device Inc.
* Author: Cliff Cai <cliff.cai@analog.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*/
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/ac97_codec.h>
#include <sound/initval.h>
#include <sound/soc.h>
#include "ad73311.h"
static const struct snd_soc_dapm_widget ad73311_dapm_widgets[] = {
SND_SOC_DAPM_INPUT("VINP"),
SND_SOC_DAPM_INPUT("VINN"),
SND_SOC_DAPM_OUTPUT("VOUTN"),
SND_SOC_DAPM_OUTPUT("VOUTP"),
};
static const struct snd_soc_dapm_route ad73311_dapm_routes[] = {
{ "Capture", NULL, "VINP" },
{ "Capture", NULL, "VINN" },
{ "VOUTN", NULL, "Playback" },
{ "VOUTP", NULL, "Playback" },
};
static struct snd_soc_dai_driver ad73311_dai = {
.name = "ad73311-hifi",
.playback = {
.stream_name = "Playback",
.channels_min = 1,
.channels_max = 1,
.rates = SNDRV_PCM_RATE_8000,
.formats = SNDRV_PCM_FMTBIT_S16_LE, },
.capture = {
.stream_name = "Capture",
.channels_min = 1,
.channels_max = 1,
.rates = SNDRV_PCM_RATE_8000,
.formats = SNDRV_PCM_FMTBIT_S16_LE, },
};
static struct snd_soc_codec_driver soc_codec_dev_ad73311 = {
.dapm_widgets = ad73311_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(ad73311_dapm_widgets),
.dapm_routes = ad73311_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(ad73311_dapm_routes),
};
static int ad73311_probe(struct platform_device *pdev)
{
return snd_soc_register_codec(&pdev->dev,
&soc_codec_dev_ad73311, &ad73311_dai, 1);
}
static int ad73311_remove(struct platform_device *pdev)
{
snd_soc_unregister_codec(&pdev->dev);
return 0;
}
static struct platform_driver ad73311_codec_driver = {
.driver = {
.name = "ad73311",
.owner = THIS_MODULE,
},
.probe = ad73311_probe,
.remove = ad73311_remove,
};
module_platform_driver(ad73311_codec_driver);
MODULE_DESCRIPTION("ASoC ad73311 driver");
MODULE_AUTHOR("Cliff Cai ");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,88 @@
/*
* File: sound/soc/codec/ad73311.h
* Based on:
* Author: Cliff Cai <cliff.cai@analog.com>
*
* Created: Thur Sep 25, 2008
* Description: definitions for AD73311 registers
*
*
* Modified:
* Copyright 2006 Analog Devices Inc.
*
* Bugs: Enter bugs at http://blackfin.uclinux.org/
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see the file COPYING, or write
* to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __AD73311_H__
#define __AD73311_H__
#define AD_CONTROL 0x8000
#define AD_DATA 0x0000
#define AD_READ 0x4000
#define AD_WRITE 0x0000
/* Control register A */
#define CTRL_REG_A (0 << 8)
#define REGA_MODE_PRO 0x00
#define REGA_MODE_DATA 0x01
#define REGA_MODE_MIXED 0x03
#define REGA_DLB 0x04
#define REGA_SLB 0x08
#define REGA_DEVC(x) ((x & 0x7) << 4)
#define REGA_RESET 0x80
/* Control register B */
#define CTRL_REG_B (1 << 8)
#define REGB_DIRATE(x) (x & 0x3)
#define REGB_SCDIV(x) ((x & 0x3) << 2)
#define REGB_MCDIV(x) ((x & 0x7) << 4)
#define REGB_CEE (1 << 7)
/* Control register C */
#define CTRL_REG_C (2 << 8)
#define REGC_PUDEV (1 << 0)
#define REGC_PUADC (1 << 3)
#define REGC_PUDAC (1 << 4)
#define REGC_PUREF (1 << 5)
#define REGC_REFUSE (1 << 6)
/* Control register D */
#define CTRL_REG_D (3 << 8)
#define REGD_IGS(x) (x & 0x7)
#define REGD_RMOD (1 << 3)
#define REGD_OGS(x) ((x & 0x7) << 4)
#define REGD_MUTE (1 << 7)
/* Control register E */
#define CTRL_REG_E (4 << 8)
#define REGE_DA(x) (x & 0x1f)
#define REGE_IBYP (1 << 5)
/* Control register F */
#define CTRL_REG_F (5 << 8)
#define REGF_SEEN (1 << 5)
#define REGF_INV (1 << 6)
#define REGF_ALB (1 << 7)
#endif

1549
sound/soc/codecs/adau1373.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,29 @@
#ifndef __ADAU1373_H__
#define __ADAU1373_H__
enum adau1373_pll_src {
ADAU1373_PLL_SRC_MCLK1 = 0,
ADAU1373_PLL_SRC_BCLK1 = 1,
ADAU1373_PLL_SRC_BCLK2 = 2,
ADAU1373_PLL_SRC_BCLK3 = 3,
ADAU1373_PLL_SRC_LRCLK1 = 4,
ADAU1373_PLL_SRC_LRCLK2 = 5,
ADAU1373_PLL_SRC_LRCLK3 = 6,
ADAU1373_PLL_SRC_GPIO1 = 7,
ADAU1373_PLL_SRC_GPIO2 = 8,
ADAU1373_PLL_SRC_GPIO3 = 9,
ADAU1373_PLL_SRC_GPIO4 = 10,
ADAU1373_PLL_SRC_MCLK2 = 11,
};
enum adau1373_pll {
ADAU1373_PLL1 = 0,
ADAU1373_PLL2 = 1,
};
enum adau1373_clk_src {
ADAU1373_CLK_SRC_PLL1 = 0,
ADAU1373_CLK_SRC_PLL2 = 1,
};
#endif

761
sound/soc/codecs/adau1701.c Normal file
View file

@ -0,0 +1,761 @@
/*
* Driver for ADAU1701 SigmaDSP processor
*
* Copyright 2011 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
* based on an inital version by Cliff Cai <cliff.cai@analog.com>
*
* Licensed under the GPL-2 or later.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/of_device.h>
#include <linux/regmap.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include "sigmadsp.h"
#include "adau1701.h"
#define ADAU1701_DSPCTRL 0x081c
#define ADAU1701_SEROCTL 0x081e
#define ADAU1701_SERICTL 0x081f
#define ADAU1701_AUXNPOW 0x0822
#define ADAU1701_PINCONF_0 0x0820
#define ADAU1701_PINCONF_1 0x0821
#define ADAU1701_AUXNPOW 0x0822
#define ADAU1701_OSCIPOW 0x0826
#define ADAU1701_DACSET 0x0827
#define ADAU1701_MAX_REGISTER 0x0828
#define ADAU1701_DSPCTRL_CR (1 << 2)
#define ADAU1701_DSPCTRL_DAM (1 << 3)
#define ADAU1701_DSPCTRL_ADM (1 << 4)
#define ADAU1701_DSPCTRL_SR_48 0x00
#define ADAU1701_DSPCTRL_SR_96 0x01
#define ADAU1701_DSPCTRL_SR_192 0x02
#define ADAU1701_DSPCTRL_SR_MASK 0x03
#define ADAU1701_SEROCTL_INV_LRCLK 0x2000
#define ADAU1701_SEROCTL_INV_BCLK 0x1000
#define ADAU1701_SEROCTL_MASTER 0x0800
#define ADAU1701_SEROCTL_OBF16 0x0000
#define ADAU1701_SEROCTL_OBF8 0x0200
#define ADAU1701_SEROCTL_OBF4 0x0400
#define ADAU1701_SEROCTL_OBF2 0x0600
#define ADAU1701_SEROCTL_OBF_MASK 0x0600
#define ADAU1701_SEROCTL_OLF1024 0x0000
#define ADAU1701_SEROCTL_OLF512 0x0080
#define ADAU1701_SEROCTL_OLF256 0x0100
#define ADAU1701_SEROCTL_OLF_MASK 0x0180
#define ADAU1701_SEROCTL_MSB_DEALY1 0x0000
#define ADAU1701_SEROCTL_MSB_DEALY0 0x0004
#define ADAU1701_SEROCTL_MSB_DEALY8 0x0008
#define ADAU1701_SEROCTL_MSB_DEALY12 0x000c
#define ADAU1701_SEROCTL_MSB_DEALY16 0x0010
#define ADAU1701_SEROCTL_MSB_DEALY_MASK 0x001c
#define ADAU1701_SEROCTL_WORD_LEN_24 0x0000
#define ADAU1701_SEROCTL_WORD_LEN_20 0x0001
#define ADAU1701_SEROCTL_WORD_LEN_16 0x0002
#define ADAU1701_SEROCTL_WORD_LEN_MASK 0x0003
#define ADAU1701_AUXNPOW_VBPD 0x40
#define ADAU1701_AUXNPOW_VRPD 0x20
#define ADAU1701_SERICTL_I2S 0
#define ADAU1701_SERICTL_LEFTJ 1
#define ADAU1701_SERICTL_TDM 2
#define ADAU1701_SERICTL_RIGHTJ_24 3
#define ADAU1701_SERICTL_RIGHTJ_20 4
#define ADAU1701_SERICTL_RIGHTJ_18 5
#define ADAU1701_SERICTL_RIGHTJ_16 6
#define ADAU1701_SERICTL_MODE_MASK 7
#define ADAU1701_SERICTL_INV_BCLK BIT(3)
#define ADAU1701_SERICTL_INV_LRCLK BIT(4)
#define ADAU1701_OSCIPOW_OPD 0x04
#define ADAU1701_DACSET_DACINIT 1
#define ADAU1707_CLKDIV_UNSET (-1U)
#define ADAU1701_FIRMWARE "adau1701.bin"
struct adau1701 {
int gpio_nreset;
int gpio_pll_mode[2];
unsigned int dai_fmt;
unsigned int pll_clkdiv;
unsigned int sysclk;
struct regmap *regmap;
u8 pin_config[12];
};
static const struct snd_kcontrol_new adau1701_controls[] = {
SOC_SINGLE("Master Capture Switch", ADAU1701_DSPCTRL, 4, 1, 0),
};
static const struct snd_soc_dapm_widget adau1701_dapm_widgets[] = {
SND_SOC_DAPM_DAC("DAC0", "Playback", ADAU1701_AUXNPOW, 3, 1),
SND_SOC_DAPM_DAC("DAC1", "Playback", ADAU1701_AUXNPOW, 2, 1),
SND_SOC_DAPM_DAC("DAC2", "Playback", ADAU1701_AUXNPOW, 1, 1),
SND_SOC_DAPM_DAC("DAC3", "Playback", ADAU1701_AUXNPOW, 0, 1),
SND_SOC_DAPM_ADC("ADC", "Capture", ADAU1701_AUXNPOW, 7, 1),
SND_SOC_DAPM_OUTPUT("OUT0"),
SND_SOC_DAPM_OUTPUT("OUT1"),
SND_SOC_DAPM_OUTPUT("OUT2"),
SND_SOC_DAPM_OUTPUT("OUT3"),
SND_SOC_DAPM_INPUT("IN0"),
SND_SOC_DAPM_INPUT("IN1"),
};
static const struct snd_soc_dapm_route adau1701_dapm_routes[] = {
{ "OUT0", NULL, "DAC0" },
{ "OUT1", NULL, "DAC1" },
{ "OUT2", NULL, "DAC2" },
{ "OUT3", NULL, "DAC3" },
{ "ADC", NULL, "IN0" },
{ "ADC", NULL, "IN1" },
};
static unsigned int adau1701_register_size(struct device *dev,
unsigned int reg)
{
switch (reg) {
case ADAU1701_PINCONF_0:
case ADAU1701_PINCONF_1:
return 3;
case ADAU1701_DSPCTRL:
case ADAU1701_SEROCTL:
case ADAU1701_AUXNPOW:
case ADAU1701_OSCIPOW:
case ADAU1701_DACSET:
return 2;
case ADAU1701_SERICTL:
return 1;
}
dev_err(dev, "Unsupported register address: %d\n", reg);
return 0;
}
static bool adau1701_volatile_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case ADAU1701_DACSET:
return true;
default:
return false;
}
}
static int adau1701_reg_write(void *context, unsigned int reg,
unsigned int value)
{
struct i2c_client *client = context;
unsigned int i;
unsigned int size;
uint8_t buf[5];
int ret;
size = adau1701_register_size(&client->dev, reg);
if (size == 0)
return -EINVAL;
buf[0] = reg >> 8;
buf[1] = reg & 0xff;
for (i = size + 1; i >= 2; --i) {
buf[i] = value;
value >>= 8;
}
ret = i2c_master_send(client, buf, size + 2);
if (ret == size + 2)
return 0;
else if (ret < 0)
return ret;
else
return -EIO;
}
static int adau1701_reg_read(void *context, unsigned int reg,
unsigned int *value)
{
int ret;
unsigned int i;
unsigned int size;
uint8_t send_buf[2], recv_buf[3];
struct i2c_client *client = context;
struct i2c_msg msgs[2];
size = adau1701_register_size(&client->dev, reg);
if (size == 0)
return -EINVAL;
send_buf[0] = reg >> 8;
send_buf[1] = reg & 0xff;
msgs[0].addr = client->addr;
msgs[0].len = sizeof(send_buf);
msgs[0].buf = send_buf;
msgs[0].flags = 0;
msgs[1].addr = client->addr;
msgs[1].len = size;
msgs[1].buf = recv_buf;
msgs[1].flags = I2C_M_RD;
ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
if (ret < 0)
return ret;
else if (ret != ARRAY_SIZE(msgs))
return -EIO;
*value = 0;
for (i = 0; i < size; i++) {
*value <<= 8;
*value |= recv_buf[i];
}
return 0;
}
static int adau1701_reset(struct snd_soc_codec *codec, unsigned int clkdiv)
{
struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
struct i2c_client *client = to_i2c_client(codec->dev);
int ret;
if (clkdiv != ADAU1707_CLKDIV_UNSET &&
gpio_is_valid(adau1701->gpio_pll_mode[0]) &&
gpio_is_valid(adau1701->gpio_pll_mode[1])) {
switch (clkdiv) {
case 64:
gpio_set_value_cansleep(adau1701->gpio_pll_mode[0], 0);
gpio_set_value_cansleep(adau1701->gpio_pll_mode[1], 0);
break;
case 256:
gpio_set_value_cansleep(adau1701->gpio_pll_mode[0], 0);
gpio_set_value_cansleep(adau1701->gpio_pll_mode[1], 1);
break;
case 384:
gpio_set_value_cansleep(adau1701->gpio_pll_mode[0], 1);
gpio_set_value_cansleep(adau1701->gpio_pll_mode[1], 0);
break;
case 0: /* fallback */
case 512:
gpio_set_value_cansleep(adau1701->gpio_pll_mode[0], 1);
gpio_set_value_cansleep(adau1701->gpio_pll_mode[1], 1);
break;
}
}
adau1701->pll_clkdiv = clkdiv;
if (gpio_is_valid(adau1701->gpio_nreset)) {
gpio_set_value_cansleep(adau1701->gpio_nreset, 0);
/* minimum reset time is 20ns */
udelay(1);
gpio_set_value_cansleep(adau1701->gpio_nreset, 1);
/* power-up time may be as long as 85ms */
mdelay(85);
}
/*
* Postpone the firmware download to a point in time when we
* know the correct PLL setup
*/
if (clkdiv != ADAU1707_CLKDIV_UNSET) {
ret = process_sigma_firmware(client, ADAU1701_FIRMWARE);
if (ret) {
dev_warn(codec->dev, "Failed to load firmware\n");
return ret;
}
}
regmap_write(adau1701->regmap, ADAU1701_DACSET, ADAU1701_DACSET_DACINIT);
regmap_write(adau1701->regmap, ADAU1701_DSPCTRL, ADAU1701_DSPCTRL_CR);
regcache_mark_dirty(adau1701->regmap);
regcache_sync(adau1701->regmap);
return 0;
}
static int adau1701_set_capture_pcm_format(struct snd_soc_codec *codec,
struct snd_pcm_hw_params *params)
{
struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
unsigned int mask = ADAU1701_SEROCTL_WORD_LEN_MASK;
unsigned int val;
switch (params_width(params)) {
case 16:
val = ADAU1701_SEROCTL_WORD_LEN_16;
break;
case 20:
val = ADAU1701_SEROCTL_WORD_LEN_20;
break;
case 24:
val = ADAU1701_SEROCTL_WORD_LEN_24;
break;
default:
return -EINVAL;
}
if (adau1701->dai_fmt == SND_SOC_DAIFMT_RIGHT_J) {
switch (params_width(params)) {
case 16:
val |= ADAU1701_SEROCTL_MSB_DEALY16;
break;
case 20:
val |= ADAU1701_SEROCTL_MSB_DEALY12;
break;
case 24:
val |= ADAU1701_SEROCTL_MSB_DEALY8;
break;
}
mask |= ADAU1701_SEROCTL_MSB_DEALY_MASK;
}
regmap_update_bits(adau1701->regmap, ADAU1701_SEROCTL, mask, val);
return 0;
}
static int adau1701_set_playback_pcm_format(struct snd_soc_codec *codec,
struct snd_pcm_hw_params *params)
{
struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
unsigned int val;
if (adau1701->dai_fmt != SND_SOC_DAIFMT_RIGHT_J)
return 0;
switch (params_width(params)) {
case 16:
val = ADAU1701_SERICTL_RIGHTJ_16;
break;
case 20:
val = ADAU1701_SERICTL_RIGHTJ_20;
break;
case 24:
val = ADAU1701_SERICTL_RIGHTJ_24;
break;
default:
return -EINVAL;
}
regmap_update_bits(adau1701->regmap, ADAU1701_SERICTL,
ADAU1701_SERICTL_MODE_MASK, val);
return 0;
}
static int adau1701_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
unsigned int clkdiv = adau1701->sysclk / params_rate(params);
unsigned int val;
int ret;
/*
* If the mclk/lrclk ratio changes, the chip needs updated PLL
* mode GPIO settings, and a full reset cycle, including a new
* firmware upload.
*/
if (clkdiv != adau1701->pll_clkdiv) {
ret = adau1701_reset(codec, clkdiv);
if (ret < 0)
return ret;
}
switch (params_rate(params)) {
case 192000:
val = ADAU1701_DSPCTRL_SR_192;
break;
case 96000:
val = ADAU1701_DSPCTRL_SR_96;
break;
case 48000:
val = ADAU1701_DSPCTRL_SR_48;
break;
default:
return -EINVAL;
}
regmap_update_bits(adau1701->regmap, ADAU1701_DSPCTRL,
ADAU1701_DSPCTRL_SR_MASK, val);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
return adau1701_set_playback_pcm_format(codec, params);
else
return adau1701_set_capture_pcm_format(codec, params);
}
static int adau1701_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
unsigned int serictl = 0x00, seroctl = 0x00;
bool invert_lrclk;
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBM_CFM:
/* master, 64-bits per sample, 1 frame per sample */
seroctl |= ADAU1701_SEROCTL_MASTER | ADAU1701_SEROCTL_OBF16
| ADAU1701_SEROCTL_OLF1024;
break;
case SND_SOC_DAIFMT_CBS_CFS:
break;
default:
return -EINVAL;
}
/* clock inversion */
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
invert_lrclk = false;
break;
case SND_SOC_DAIFMT_NB_IF:
invert_lrclk = true;
break;
case SND_SOC_DAIFMT_IB_NF:
invert_lrclk = false;
serictl |= ADAU1701_SERICTL_INV_BCLK;
seroctl |= ADAU1701_SEROCTL_INV_BCLK;
break;
case SND_SOC_DAIFMT_IB_IF:
invert_lrclk = true;
serictl |= ADAU1701_SERICTL_INV_BCLK;
seroctl |= ADAU1701_SEROCTL_INV_BCLK;
break;
default:
return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
break;
case SND_SOC_DAIFMT_LEFT_J:
serictl |= ADAU1701_SERICTL_LEFTJ;
seroctl |= ADAU1701_SEROCTL_MSB_DEALY0;
invert_lrclk = !invert_lrclk;
break;
case SND_SOC_DAIFMT_RIGHT_J:
serictl |= ADAU1701_SERICTL_RIGHTJ_24;
seroctl |= ADAU1701_SEROCTL_MSB_DEALY8;
invert_lrclk = !invert_lrclk;
break;
default:
return -EINVAL;
}
if (invert_lrclk) {
seroctl |= ADAU1701_SEROCTL_INV_LRCLK;
serictl |= ADAU1701_SERICTL_INV_LRCLK;
}
adau1701->dai_fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
regmap_write(adau1701->regmap, ADAU1701_SERICTL, serictl);
regmap_update_bits(adau1701->regmap, ADAU1701_SEROCTL,
~ADAU1701_SEROCTL_WORD_LEN_MASK, seroctl);
return 0;
}
static int adau1701_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
unsigned int mask = ADAU1701_AUXNPOW_VBPD | ADAU1701_AUXNPOW_VRPD;
struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
switch (level) {
case SND_SOC_BIAS_ON:
break;
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
/* Enable VREF and VREF buffer */
regmap_update_bits(adau1701->regmap,
ADAU1701_AUXNPOW, mask, 0x00);
break;
case SND_SOC_BIAS_OFF:
/* Disable VREF and VREF buffer */
regmap_update_bits(adau1701->regmap,
ADAU1701_AUXNPOW, mask, mask);
break;
}
codec->dapm.bias_level = level;
return 0;
}
static int adau1701_digital_mute(struct snd_soc_dai *dai, int mute)
{
struct snd_soc_codec *codec = dai->codec;
unsigned int mask = ADAU1701_DSPCTRL_DAM;
struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
unsigned int val;
if (mute)
val = 0;
else
val = mask;
regmap_update_bits(adau1701->regmap, ADAU1701_DSPCTRL, mask, val);
return 0;
}
static int adau1701_set_sysclk(struct snd_soc_codec *codec, int clk_id,
int source, unsigned int freq, int dir)
{
unsigned int val;
struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
switch (clk_id) {
case ADAU1701_CLK_SRC_OSC:
val = 0x0;
break;
case ADAU1701_CLK_SRC_MCLK:
val = ADAU1701_OSCIPOW_OPD;
break;
default:
return -EINVAL;
}
regmap_update_bits(adau1701->regmap, ADAU1701_OSCIPOW,
ADAU1701_OSCIPOW_OPD, val);
adau1701->sysclk = freq;
return 0;
}
#define ADAU1701_RATES (SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | \
SNDRV_PCM_RATE_192000)
#define ADAU1701_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE)
static const struct snd_soc_dai_ops adau1701_dai_ops = {
.set_fmt = adau1701_set_dai_fmt,
.hw_params = adau1701_hw_params,
.digital_mute = adau1701_digital_mute,
};
static struct snd_soc_dai_driver adau1701_dai = {
.name = "adau1701",
.playback = {
.stream_name = "Playback",
.channels_min = 2,
.channels_max = 8,
.rates = ADAU1701_RATES,
.formats = ADAU1701_FORMATS,
},
.capture = {
.stream_name = "Capture",
.channels_min = 2,
.channels_max = 8,
.rates = ADAU1701_RATES,
.formats = ADAU1701_FORMATS,
},
.ops = &adau1701_dai_ops,
.symmetric_rates = 1,
};
#ifdef CONFIG_OF
static const struct of_device_id adau1701_dt_ids[] = {
{ .compatible = "adi,adau1701", },
{ }
};
MODULE_DEVICE_TABLE(of, adau1701_dt_ids);
#endif
static int adau1701_probe(struct snd_soc_codec *codec)
{
int i, ret;
unsigned int val;
struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
/*
* Let the pll_clkdiv variable default to something that won't happen
* at runtime. That way, we can postpone the firmware download from
* adau1701_reset() to a point in time when we know the correct PLL
* mode parameters.
*/
adau1701->pll_clkdiv = ADAU1707_CLKDIV_UNSET;
/* initalize with pre-configured pll mode settings */
ret = adau1701_reset(codec, adau1701->pll_clkdiv);
if (ret < 0)
return ret;
/* set up pin config */
val = 0;
for (i = 0; i < 6; i++)
val |= adau1701->pin_config[i] << (i * 4);
regmap_write(adau1701->regmap, ADAU1701_PINCONF_0, val);
val = 0;
for (i = 0; i < 6; i++)
val |= adau1701->pin_config[i + 6] << (i * 4);
regmap_write(adau1701->regmap, ADAU1701_PINCONF_1, val);
return 0;
}
static struct snd_soc_codec_driver adau1701_codec_drv = {
.probe = adau1701_probe,
.set_bias_level = adau1701_set_bias_level,
.idle_bias_off = true,
.controls = adau1701_controls,
.num_controls = ARRAY_SIZE(adau1701_controls),
.dapm_widgets = adau1701_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(adau1701_dapm_widgets),
.dapm_routes = adau1701_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(adau1701_dapm_routes),
.set_sysclk = adau1701_set_sysclk,
};
static const struct regmap_config adau1701_regmap = {
.reg_bits = 16,
.val_bits = 32,
.max_register = ADAU1701_MAX_REGISTER,
.cache_type = REGCACHE_RBTREE,
.volatile_reg = adau1701_volatile_reg,
.reg_write = adau1701_reg_write,
.reg_read = adau1701_reg_read,
};
static int adau1701_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct adau1701 *adau1701;
struct device *dev = &client->dev;
int gpio_nreset = -EINVAL;
int gpio_pll_mode[2] = { -EINVAL, -EINVAL };
int ret;
adau1701 = devm_kzalloc(dev, sizeof(*adau1701), GFP_KERNEL);
if (!adau1701)
return -ENOMEM;
adau1701->regmap = devm_regmap_init(dev, NULL, client,
&adau1701_regmap);
if (IS_ERR(adau1701->regmap))
return PTR_ERR(adau1701->regmap);
if (dev->of_node) {
gpio_nreset = of_get_named_gpio(dev->of_node, "reset-gpio", 0);
if (gpio_nreset < 0 && gpio_nreset != -ENOENT)
return gpio_nreset;
gpio_pll_mode[0] = of_get_named_gpio(dev->of_node,
"adi,pll-mode-gpios", 0);
if (gpio_pll_mode[0] < 0 && gpio_pll_mode[0] != -ENOENT)
return gpio_pll_mode[0];
gpio_pll_mode[1] = of_get_named_gpio(dev->of_node,
"adi,pll-mode-gpios", 1);
if (gpio_pll_mode[1] < 0 && gpio_pll_mode[1] != -ENOENT)
return gpio_pll_mode[1];
of_property_read_u32(dev->of_node, "adi,pll-clkdiv",
&adau1701->pll_clkdiv);
of_property_read_u8_array(dev->of_node, "adi,pin-config",
adau1701->pin_config,
ARRAY_SIZE(adau1701->pin_config));
}
if (gpio_is_valid(gpio_nreset)) {
ret = devm_gpio_request_one(dev, gpio_nreset, GPIOF_OUT_INIT_LOW,
"ADAU1701 Reset");
if (ret < 0)
return ret;
}
if (gpio_is_valid(gpio_pll_mode[0]) &&
gpio_is_valid(gpio_pll_mode[1])) {
ret = devm_gpio_request_one(dev, gpio_pll_mode[0],
GPIOF_OUT_INIT_LOW,
"ADAU1701 PLL mode 0");
if (ret < 0)
return ret;
ret = devm_gpio_request_one(dev, gpio_pll_mode[1],
GPIOF_OUT_INIT_LOW,
"ADAU1701 PLL mode 1");
if (ret < 0)
return ret;
}
adau1701->gpio_nreset = gpio_nreset;
adau1701->gpio_pll_mode[0] = gpio_pll_mode[0];
adau1701->gpio_pll_mode[1] = gpio_pll_mode[1];
i2c_set_clientdata(client, adau1701);
ret = snd_soc_register_codec(&client->dev, &adau1701_codec_drv,
&adau1701_dai, 1);
return ret;
}
static int adau1701_i2c_remove(struct i2c_client *client)
{
snd_soc_unregister_codec(&client->dev);
return 0;
}
static const struct i2c_device_id adau1701_i2c_id[] = {
{ "adau1401", 0 },
{ "adau1401a", 0 },
{ "adau1701", 0 },
{ "adau1702", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, adau1701_i2c_id);
static struct i2c_driver adau1701_i2c_driver = {
.driver = {
.name = "adau1701",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(adau1701_dt_ids),
},
.probe = adau1701_i2c_probe,
.remove = adau1701_i2c_remove,
.id_table = adau1701_i2c_id,
};
module_i2c_driver(adau1701_i2c_driver);
MODULE_DESCRIPTION("ASoC ADAU1701 SigmaDSP driver");
MODULE_AUTHOR("Cliff Cai <cliff.cai@analog.com>");
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,17 @@
/*
* header file for ADAU1701 SigmaDSP processor
*
* Copyright 2011 Analog Devices Inc.
*
* Licensed under the GPL-2 or later.
*/
#ifndef _ADAU1701_H
#define _ADAU1701_H
enum adau1701_clk_src {
ADAU1701_CLK_SRC_OSC,
ADAU1701_CLK_SRC_MCLK,
};
#endif

View file

@ -0,0 +1,60 @@
/*
* Driver for ADAU1761/ADAU1461/ADAU1761/ADAU1961 codec
*
* Copyright 2014 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
*
* Licensed under the GPL-2.
*/
#include <linux/i2c.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <sound/soc.h>
#include "adau1761.h"
static int adau1761_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct regmap_config config;
config = adau1761_regmap_config;
config.val_bits = 8;
config.reg_bits = 16;
return adau1761_probe(&client->dev,
devm_regmap_init_i2c(client, &config),
id->driver_data, NULL);
}
static int adau1761_i2c_remove(struct i2c_client *client)
{
snd_soc_unregister_codec(&client->dev);
return 0;
}
static const struct i2c_device_id adau1761_i2c_ids[] = {
{ "adau1361", ADAU1361 },
{ "adau1461", ADAU1761 },
{ "adau1761", ADAU1761 },
{ "adau1961", ADAU1361 },
{ }
};
MODULE_DEVICE_TABLE(i2c, adau1761_i2c_ids);
static struct i2c_driver adau1761_i2c_driver = {
.driver = {
.name = "adau1761",
.owner = THIS_MODULE,
},
.probe = adau1761_i2c_probe,
.remove = adau1761_i2c_remove,
.id_table = adau1761_i2c_ids,
};
module_i2c_driver(adau1761_i2c_driver);
MODULE_DESCRIPTION("ASoC ADAU1361/ADAU1461/ADAU1761/ADAU1961 CODEC I2C driver");
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,77 @@
/*
* Driver for ADAU1761/ADAU1461/ADAU1761/ADAU1961 codec
*
* Copyright 2014 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
*
* Licensed under the GPL-2.
*/
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/spi/spi.h>
#include <sound/soc.h>
#include "adau1761.h"
static void adau1761_spi_switch_mode(struct device *dev)
{
struct spi_device *spi = to_spi_device(dev);
/*
* To get the device into SPI mode CLATCH has to be pulled low three
* times. Do this by issuing three dummy reads.
*/
spi_w8r8(spi, 0x00);
spi_w8r8(spi, 0x00);
spi_w8r8(spi, 0x00);
}
static int adau1761_spi_probe(struct spi_device *spi)
{
const struct spi_device_id *id = spi_get_device_id(spi);
struct regmap_config config;
if (!id)
return -EINVAL;
config = adau1761_regmap_config;
config.val_bits = 8;
config.reg_bits = 24;
config.read_flag_mask = 0x1;
return adau1761_probe(&spi->dev,
devm_regmap_init_spi(spi, &config),
id->driver_data, adau1761_spi_switch_mode);
}
static int adau1761_spi_remove(struct spi_device *spi)
{
snd_soc_unregister_codec(&spi->dev);
return 0;
}
static const struct spi_device_id adau1761_spi_id[] = {
{ "adau1361", ADAU1361 },
{ "adau1461", ADAU1761 },
{ "adau1761", ADAU1761 },
{ "adau1961", ADAU1361 },
{ }
};
MODULE_DEVICE_TABLE(spi, adau1761_spi_id);
static struct spi_driver adau1761_spi_driver = {
.driver = {
.name = "adau1761",
.owner = THIS_MODULE,
},
.probe = adau1761_spi_probe,
.remove = adau1761_spi_remove,
.id_table = adau1761_spi_id,
};
module_spi_driver(adau1761_spi_driver);
MODULE_DESCRIPTION("ASoC ADAU1361/ADAU1461/ADAU1761/ADAU1961 CODEC SPI driver");
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
MODULE_LICENSE("GPL");

807
sound/soc/codecs/adau1761.c Normal file
View file

@ -0,0 +1,807 @@
/*
* Driver for ADAU1761/ADAU1461/ADAU1761/ADAU1961 codec
*
* Copyright 2011-2013 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
*
* Licensed under the GPL-2 or later.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/spi/spi.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/tlv.h>
#include <linux/platform_data/adau17x1.h>
#include "adau17x1.h"
#include "adau1761.h"
#define ADAU1761_DIGMIC_JACKDETECT 0x4008
#define ADAU1761_REC_MIXER_LEFT0 0x400a
#define ADAU1761_REC_MIXER_LEFT1 0x400b
#define ADAU1761_REC_MIXER_RIGHT0 0x400c
#define ADAU1761_REC_MIXER_RIGHT1 0x400d
#define ADAU1761_LEFT_DIFF_INPUT_VOL 0x400e
#define ADAU1761_RIGHT_DIFF_INPUT_VOL 0x400f
#define ADAU1761_PLAY_LR_MIXER_LEFT 0x4020
#define ADAU1761_PLAY_MIXER_LEFT0 0x401c
#define ADAU1761_PLAY_MIXER_LEFT1 0x401d
#define ADAU1761_PLAY_MIXER_RIGHT0 0x401e
#define ADAU1761_PLAY_MIXER_RIGHT1 0x401f
#define ADAU1761_PLAY_LR_MIXER_RIGHT 0x4021
#define ADAU1761_PLAY_MIXER_MONO 0x4022
#define ADAU1761_PLAY_HP_LEFT_VOL 0x4023
#define ADAU1761_PLAY_HP_RIGHT_VOL 0x4024
#define ADAU1761_PLAY_LINE_LEFT_VOL 0x4025
#define ADAU1761_PLAY_LINE_RIGHT_VOL 0x4026
#define ADAU1761_PLAY_MONO_OUTPUT_VOL 0x4027
#define ADAU1761_POP_CLICK_SUPPRESS 0x4028
#define ADAU1761_JACK_DETECT_PIN 0x4031
#define ADAU1761_DEJITTER 0x4036
#define ADAU1761_CLK_ENABLE0 0x40f9
#define ADAU1761_CLK_ENABLE1 0x40fa
#define ADAU1761_DIGMIC_JACKDETECT_ACTIVE_LOW BIT(0)
#define ADAU1761_DIGMIC_JACKDETECT_DIGMIC BIT(5)
#define ADAU1761_DIFF_INPUT_VOL_LDEN BIT(0)
#define ADAU1761_PLAY_MONO_OUTPUT_VOL_MODE_HP BIT(0)
#define ADAU1761_PLAY_MONO_OUTPUT_VOL_UNMUTE BIT(1)
#define ADAU1761_PLAY_HP_RIGHT_VOL_MODE_HP BIT(0)
#define ADAU1761_PLAY_LINE_LEFT_VOL_MODE_HP BIT(0)
#define ADAU1761_PLAY_LINE_RIGHT_VOL_MODE_HP BIT(0)
#define ADAU1761_FIRMWARE "adau1761.bin"
static const struct reg_default adau1761_reg_defaults[] = {
{ ADAU1761_DEJITTER, 0x03 },
{ ADAU1761_DIGMIC_JACKDETECT, 0x00 },
{ ADAU1761_REC_MIXER_LEFT0, 0x00 },
{ ADAU1761_REC_MIXER_LEFT1, 0x00 },
{ ADAU1761_REC_MIXER_RIGHT0, 0x00 },
{ ADAU1761_REC_MIXER_RIGHT1, 0x00 },
{ ADAU1761_LEFT_DIFF_INPUT_VOL, 0x00 },
{ ADAU1761_RIGHT_DIFF_INPUT_VOL, 0x00 },
{ ADAU1761_PLAY_LR_MIXER_LEFT, 0x00 },
{ ADAU1761_PLAY_MIXER_LEFT0, 0x00 },
{ ADAU1761_PLAY_MIXER_LEFT1, 0x00 },
{ ADAU1761_PLAY_MIXER_RIGHT0, 0x00 },
{ ADAU1761_PLAY_MIXER_RIGHT1, 0x00 },
{ ADAU1761_PLAY_LR_MIXER_RIGHT, 0x00 },
{ ADAU1761_PLAY_MIXER_MONO, 0x00 },
{ ADAU1761_PLAY_HP_LEFT_VOL, 0x00 },
{ ADAU1761_PLAY_HP_RIGHT_VOL, 0x00 },
{ ADAU1761_PLAY_LINE_LEFT_VOL, 0x00 },
{ ADAU1761_PLAY_LINE_RIGHT_VOL, 0x00 },
{ ADAU1761_PLAY_MONO_OUTPUT_VOL, 0x00 },
{ ADAU1761_POP_CLICK_SUPPRESS, 0x00 },
{ ADAU1761_JACK_DETECT_PIN, 0x00 },
{ ADAU1761_CLK_ENABLE0, 0x00 },
{ ADAU1761_CLK_ENABLE1, 0x00 },
{ ADAU17X1_CLOCK_CONTROL, 0x00 },
{ ADAU17X1_PLL_CONTROL, 0x00 },
{ ADAU17X1_REC_POWER_MGMT, 0x00 },
{ ADAU17X1_MICBIAS, 0x00 },
{ ADAU17X1_SERIAL_PORT0, 0x00 },
{ ADAU17X1_SERIAL_PORT1, 0x00 },
{ ADAU17X1_CONVERTER0, 0x00 },
{ ADAU17X1_CONVERTER1, 0x00 },
{ ADAU17X1_LEFT_INPUT_DIGITAL_VOL, 0x00 },
{ ADAU17X1_RIGHT_INPUT_DIGITAL_VOL, 0x00 },
{ ADAU17X1_ADC_CONTROL, 0x00 },
{ ADAU17X1_PLAY_POWER_MGMT, 0x00 },
{ ADAU17X1_DAC_CONTROL0, 0x00 },
{ ADAU17X1_DAC_CONTROL1, 0x00 },
{ ADAU17X1_DAC_CONTROL2, 0x00 },
{ ADAU17X1_SERIAL_PORT_PAD, 0xaa },
{ ADAU17X1_CONTROL_PORT_PAD0, 0xaa },
{ ADAU17X1_CONTROL_PORT_PAD1, 0x00 },
{ ADAU17X1_DSP_SAMPLING_RATE, 0x01 },
{ ADAU17X1_SERIAL_INPUT_ROUTE, 0x00 },
{ ADAU17X1_SERIAL_OUTPUT_ROUTE, 0x00 },
{ ADAU17X1_DSP_ENABLE, 0x00 },
{ ADAU17X1_DSP_RUN, 0x00 },
{ ADAU17X1_SERIAL_SAMPLING_RATE, 0x00 },
};
static const DECLARE_TLV_DB_SCALE(adau1761_sing_in_tlv, -1500, 300, 1);
static const DECLARE_TLV_DB_SCALE(adau1761_diff_in_tlv, -1200, 75, 0);
static const DECLARE_TLV_DB_SCALE(adau1761_out_tlv, -5700, 100, 0);
static const DECLARE_TLV_DB_SCALE(adau1761_sidetone_tlv, -1800, 300, 1);
static const DECLARE_TLV_DB_SCALE(adau1761_boost_tlv, -600, 600, 1);
static const DECLARE_TLV_DB_SCALE(adau1761_pga_boost_tlv, -2000, 2000, 1);
static const unsigned int adau1761_bias_select_values[] = {
0, 2, 3,
};
static const char * const adau1761_bias_select_text[] = {
"Normal operation", "Enhanced performance", "Power saving",
};
static const char * const adau1761_bias_select_extreme_text[] = {
"Normal operation", "Extreme power saving", "Enhanced performance",
"Power saving",
};
static SOC_ENUM_SINGLE_DECL(adau1761_adc_bias_enum,
ADAU17X1_REC_POWER_MGMT, 3, adau1761_bias_select_extreme_text);
static SOC_ENUM_SINGLE_DECL(adau1761_hp_bias_enum,
ADAU17X1_PLAY_POWER_MGMT, 6, adau1761_bias_select_extreme_text);
static SOC_ENUM_SINGLE_DECL(adau1761_dac_bias_enum,
ADAU17X1_PLAY_POWER_MGMT, 4, adau1761_bias_select_extreme_text);
static SOC_VALUE_ENUM_SINGLE_DECL(adau1761_playback_bias_enum,
ADAU17X1_PLAY_POWER_MGMT, 2, 0x3, adau1761_bias_select_text,
adau1761_bias_select_values);
static SOC_VALUE_ENUM_SINGLE_DECL(adau1761_capture_bias_enum,
ADAU17X1_REC_POWER_MGMT, 1, 0x3, adau1761_bias_select_text,
adau1761_bias_select_values);
static const struct snd_kcontrol_new adau1761_jack_detect_controls[] = {
SOC_SINGLE("Speaker Auto-mute Switch", ADAU1761_DIGMIC_JACKDETECT,
4, 1, 0),
};
static const struct snd_kcontrol_new adau1761_differential_mode_controls[] = {
SOC_DOUBLE_R_TLV("Capture Volume", ADAU1761_LEFT_DIFF_INPUT_VOL,
ADAU1761_RIGHT_DIFF_INPUT_VOL, 2, 0x3f, 0,
adau1761_diff_in_tlv),
SOC_DOUBLE_R("Capture Switch", ADAU1761_LEFT_DIFF_INPUT_VOL,
ADAU1761_RIGHT_DIFF_INPUT_VOL, 1, 1, 0),
SOC_DOUBLE_R_TLV("PGA Boost Capture Volume", ADAU1761_REC_MIXER_LEFT1,
ADAU1761_REC_MIXER_RIGHT1, 3, 2, 0, adau1761_pga_boost_tlv),
};
static const struct snd_kcontrol_new adau1761_single_mode_controls[] = {
SOC_SINGLE_TLV("Input 1 Capture Volume", ADAU1761_REC_MIXER_LEFT0,
4, 7, 0, adau1761_sing_in_tlv),
SOC_SINGLE_TLV("Input 2 Capture Volume", ADAU1761_REC_MIXER_LEFT0,
1, 7, 0, adau1761_sing_in_tlv),
SOC_SINGLE_TLV("Input 3 Capture Volume", ADAU1761_REC_MIXER_RIGHT0,
4, 7, 0, adau1761_sing_in_tlv),
SOC_SINGLE_TLV("Input 4 Capture Volume", ADAU1761_REC_MIXER_RIGHT0,
1, 7, 0, adau1761_sing_in_tlv),
};
static const struct snd_kcontrol_new adau1761_controls[] = {
SOC_DOUBLE_R_TLV("Aux Capture Volume", ADAU1761_REC_MIXER_LEFT1,
ADAU1761_REC_MIXER_RIGHT1, 0, 7, 0, adau1761_sing_in_tlv),
SOC_DOUBLE_R_TLV("Headphone Playback Volume", ADAU1761_PLAY_HP_LEFT_VOL,
ADAU1761_PLAY_HP_RIGHT_VOL, 2, 0x3f, 0, adau1761_out_tlv),
SOC_DOUBLE_R("Headphone Playback Switch", ADAU1761_PLAY_HP_LEFT_VOL,
ADAU1761_PLAY_HP_RIGHT_VOL, 1, 1, 0),
SOC_DOUBLE_R_TLV("Lineout Playback Volume", ADAU1761_PLAY_LINE_LEFT_VOL,
ADAU1761_PLAY_LINE_RIGHT_VOL, 2, 0x3f, 0, adau1761_out_tlv),
SOC_DOUBLE_R("Lineout Playback Switch", ADAU1761_PLAY_LINE_LEFT_VOL,
ADAU1761_PLAY_LINE_RIGHT_VOL, 1, 1, 0),
SOC_ENUM("ADC Bias", adau1761_adc_bias_enum),
SOC_ENUM("DAC Bias", adau1761_dac_bias_enum),
SOC_ENUM("Capture Bias", adau1761_capture_bias_enum),
SOC_ENUM("Playback Bias", adau1761_playback_bias_enum),
SOC_ENUM("Headphone Bias", adau1761_hp_bias_enum),
};
static const struct snd_kcontrol_new adau1761_mono_controls[] = {
SOC_SINGLE_TLV("Mono Playback Volume", ADAU1761_PLAY_MONO_OUTPUT_VOL,
2, 0x3f, 0, adau1761_out_tlv),
SOC_SINGLE("Mono Playback Switch", ADAU1761_PLAY_MONO_OUTPUT_VOL,
1, 1, 0),
};
static const struct snd_kcontrol_new adau1761_left_mixer_controls[] = {
SOC_DAPM_SINGLE_AUTODISABLE("Left DAC Switch",
ADAU1761_PLAY_MIXER_LEFT0, 5, 1, 0),
SOC_DAPM_SINGLE_AUTODISABLE("Right DAC Switch",
ADAU1761_PLAY_MIXER_LEFT0, 6, 1, 0),
SOC_DAPM_SINGLE_TLV("Aux Bypass Volume",
ADAU1761_PLAY_MIXER_LEFT0, 1, 8, 0, adau1761_sidetone_tlv),
SOC_DAPM_SINGLE_TLV("Right Bypass Volume",
ADAU1761_PLAY_MIXER_LEFT1, 4, 8, 0, adau1761_sidetone_tlv),
SOC_DAPM_SINGLE_TLV("Left Bypass Volume",
ADAU1761_PLAY_MIXER_LEFT1, 0, 8, 0, adau1761_sidetone_tlv),
};
static const struct snd_kcontrol_new adau1761_right_mixer_controls[] = {
SOC_DAPM_SINGLE_AUTODISABLE("Left DAC Switch",
ADAU1761_PLAY_MIXER_RIGHT0, 5, 1, 0),
SOC_DAPM_SINGLE_AUTODISABLE("Right DAC Switch",
ADAU1761_PLAY_MIXER_RIGHT0, 6, 1, 0),
SOC_DAPM_SINGLE_TLV("Aux Bypass Volume",
ADAU1761_PLAY_MIXER_RIGHT0, 1, 8, 0, adau1761_sidetone_tlv),
SOC_DAPM_SINGLE_TLV("Right Bypass Volume",
ADAU1761_PLAY_MIXER_RIGHT1, 4, 8, 0, adau1761_sidetone_tlv),
SOC_DAPM_SINGLE_TLV("Left Bypass Volume",
ADAU1761_PLAY_MIXER_RIGHT1, 0, 8, 0, adau1761_sidetone_tlv),
};
static const struct snd_kcontrol_new adau1761_left_lr_mixer_controls[] = {
SOC_DAPM_SINGLE_TLV("Left Volume",
ADAU1761_PLAY_LR_MIXER_LEFT, 1, 2, 0, adau1761_boost_tlv),
SOC_DAPM_SINGLE_TLV("Right Volume",
ADAU1761_PLAY_LR_MIXER_LEFT, 3, 2, 0, adau1761_boost_tlv),
};
static const struct snd_kcontrol_new adau1761_right_lr_mixer_controls[] = {
SOC_DAPM_SINGLE_TLV("Left Volume",
ADAU1761_PLAY_LR_MIXER_RIGHT, 1, 2, 0, adau1761_boost_tlv),
SOC_DAPM_SINGLE_TLV("Right Volume",
ADAU1761_PLAY_LR_MIXER_RIGHT, 3, 2, 0, adau1761_boost_tlv),
};
static const char * const adau1761_input_mux_text[] = {
"ADC", "DMIC",
};
static SOC_ENUM_SINGLE_DECL(adau1761_input_mux_enum,
ADAU17X1_ADC_CONTROL, 2, adau1761_input_mux_text);
static const struct snd_kcontrol_new adau1761_input_mux_control =
SOC_DAPM_ENUM("Input Select", adau1761_input_mux_enum);
static int adau1761_dejitter_fixup(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct adau *adau = snd_soc_codec_get_drvdata(w->codec);
/* After any power changes have been made the dejitter circuit
* has to be reinitialized. */
regmap_write(adau->regmap, ADAU1761_DEJITTER, 0);
if (!adau->master)
regmap_write(adau->regmap, ADAU1761_DEJITTER, 3);
return 0;
}
static const struct snd_soc_dapm_widget adau1x61_dapm_widgets[] = {
SND_SOC_DAPM_MIXER("Left Input Mixer", ADAU1761_REC_MIXER_LEFT0, 0, 0,
NULL, 0),
SND_SOC_DAPM_MIXER("Right Input Mixer", ADAU1761_REC_MIXER_RIGHT0, 0, 0,
NULL, 0),
SOC_MIXER_ARRAY("Left Playback Mixer", ADAU1761_PLAY_MIXER_LEFT0,
0, 0, adau1761_left_mixer_controls),
SOC_MIXER_ARRAY("Right Playback Mixer", ADAU1761_PLAY_MIXER_RIGHT0,
0, 0, adau1761_right_mixer_controls),
SOC_MIXER_ARRAY("Left LR Playback Mixer", ADAU1761_PLAY_LR_MIXER_LEFT,
0, 0, adau1761_left_lr_mixer_controls),
SOC_MIXER_ARRAY("Right LR Playback Mixer", ADAU1761_PLAY_LR_MIXER_RIGHT,
0, 0, adau1761_right_lr_mixer_controls),
SND_SOC_DAPM_SUPPLY("Headphone", ADAU1761_PLAY_HP_LEFT_VOL,
0, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("SYSCLK", 2, SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_POST("Dejitter fixup", adau1761_dejitter_fixup),
SND_SOC_DAPM_INPUT("LAUX"),
SND_SOC_DAPM_INPUT("RAUX"),
SND_SOC_DAPM_INPUT("LINP"),
SND_SOC_DAPM_INPUT("LINN"),
SND_SOC_DAPM_INPUT("RINP"),
SND_SOC_DAPM_INPUT("RINN"),
SND_SOC_DAPM_OUTPUT("LOUT"),
SND_SOC_DAPM_OUTPUT("ROUT"),
SND_SOC_DAPM_OUTPUT("LHP"),
SND_SOC_DAPM_OUTPUT("RHP"),
};
static const struct snd_soc_dapm_widget adau1761_mono_dapm_widgets[] = {
SND_SOC_DAPM_MIXER("Mono Playback Mixer", ADAU1761_PLAY_MIXER_MONO,
0, 0, NULL, 0),
SND_SOC_DAPM_OUTPUT("MONOOUT"),
};
static const struct snd_soc_dapm_widget adau1761_capless_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY_S("Headphone VGND", 1, ADAU1761_PLAY_MIXER_MONO,
0, 0, NULL, 0),
};
static const struct snd_soc_dapm_route adau1x61_dapm_routes[] = {
{ "Left Input Mixer", NULL, "LINP" },
{ "Left Input Mixer", NULL, "LINN" },
{ "Left Input Mixer", NULL, "LAUX" },
{ "Right Input Mixer", NULL, "RINP" },
{ "Right Input Mixer", NULL, "RINN" },
{ "Right Input Mixer", NULL, "RAUX" },
{ "Left Playback Mixer", NULL, "Left Playback Enable"},
{ "Right Playback Mixer", NULL, "Right Playback Enable"},
{ "Left LR Playback Mixer", NULL, "Left Playback Enable"},
{ "Right LR Playback Mixer", NULL, "Right Playback Enable"},
{ "Left Playback Mixer", "Left DAC Switch", "Left DAC" },
{ "Left Playback Mixer", "Right DAC Switch", "Right DAC" },
{ "Right Playback Mixer", "Left DAC Switch", "Left DAC" },
{ "Right Playback Mixer", "Right DAC Switch", "Right DAC" },
{ "Left LR Playback Mixer", "Left Volume", "Left Playback Mixer" },
{ "Left LR Playback Mixer", "Right Volume", "Right Playback Mixer" },
{ "Right LR Playback Mixer", "Left Volume", "Left Playback Mixer" },
{ "Right LR Playback Mixer", "Right Volume", "Right Playback Mixer" },
{ "LHP", NULL, "Left Playback Mixer" },
{ "RHP", NULL, "Right Playback Mixer" },
{ "LHP", NULL, "Headphone" },
{ "RHP", NULL, "Headphone" },
{ "LOUT", NULL, "Left LR Playback Mixer" },
{ "ROUT", NULL, "Right LR Playback Mixer" },
{ "Left Playback Mixer", "Aux Bypass Volume", "LAUX" },
{ "Left Playback Mixer", "Left Bypass Volume", "Left Input Mixer" },
{ "Left Playback Mixer", "Right Bypass Volume", "Right Input Mixer" },
{ "Right Playback Mixer", "Aux Bypass Volume", "RAUX" },
{ "Right Playback Mixer", "Left Bypass Volume", "Left Input Mixer" },
{ "Right Playback Mixer", "Right Bypass Volume", "Right Input Mixer" },
};
static const struct snd_soc_dapm_route adau1761_mono_dapm_routes[] = {
{ "Mono Playback Mixer", NULL, "Left Playback Mixer" },
{ "Mono Playback Mixer", NULL, "Right Playback Mixer" },
{ "MONOOUT", NULL, "Mono Playback Mixer" },
};
static const struct snd_soc_dapm_route adau1761_capless_dapm_routes[] = {
{ "Headphone", NULL, "Headphone VGND" },
};
static const struct snd_soc_dapm_widget adau1761_dmic_widgets[] = {
SND_SOC_DAPM_MUX("Left Decimator Mux", SND_SOC_NOPM, 0, 0,
&adau1761_input_mux_control),
SND_SOC_DAPM_MUX("Right Decimator Mux", SND_SOC_NOPM, 0, 0,
&adau1761_input_mux_control),
SND_SOC_DAPM_INPUT("DMIC"),
};
static const struct snd_soc_dapm_route adau1761_dmic_routes[] = {
{ "Left Decimator Mux", "ADC", "Left Input Mixer" },
{ "Left Decimator Mux", "DMIC", "DMIC" },
{ "Right Decimator Mux", "ADC", "Right Input Mixer" },
{ "Right Decimator Mux", "DMIC", "DMIC" },
{ "Left Decimator", NULL, "Left Decimator Mux" },
{ "Right Decimator", NULL, "Right Decimator Mux" },
};
static const struct snd_soc_dapm_route adau1761_no_dmic_routes[] = {
{ "Left Decimator", NULL, "Left Input Mixer" },
{ "Right Decimator", NULL, "Right Input Mixer" },
};
static const struct snd_soc_dapm_widget adau1761_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("Serial Port Clock", ADAU1761_CLK_ENABLE0,
0, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("Serial Input Routing Clock", ADAU1761_CLK_ENABLE0,
1, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("Serial Output Routing Clock", ADAU1761_CLK_ENABLE0,
3, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("Decimator Resync Clock", ADAU1761_CLK_ENABLE0,
4, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("Interpolator Resync Clock", ADAU1761_CLK_ENABLE0,
2, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("Slew Clock", ADAU1761_CLK_ENABLE0, 6, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("ALC Clock", ADAU1761_CLK_ENABLE0, 5, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("Digital Clock 0", 1, ADAU1761_CLK_ENABLE1,
0, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("Digital Clock 1", 1, ADAU1761_CLK_ENABLE1,
1, 0, NULL, 0),
};
static const struct snd_soc_dapm_route adau1761_dapm_routes[] = {
{ "Left Decimator", NULL, "Digital Clock 0", },
{ "Right Decimator", NULL, "Digital Clock 0", },
{ "Left DAC", NULL, "Digital Clock 0", },
{ "Right DAC", NULL, "Digital Clock 0", },
{ "AIFCLK", NULL, "Digital Clock 1" },
{ "Playback", NULL, "Serial Port Clock" },
{ "Capture", NULL, "Serial Port Clock" },
{ "Playback", NULL, "Serial Input Routing Clock" },
{ "Capture", NULL, "Serial Output Routing Clock" },
{ "Left Decimator", NULL, "Decimator Resync Clock" },
{ "Right Decimator", NULL, "Decimator Resync Clock" },
{ "Left DAC", NULL, "Interpolator Resync Clock" },
{ "Right DAC", NULL, "Interpolator Resync Clock" },
{ "DSP", NULL, "Digital Clock 0" },
{ "Slew Clock", NULL, "Digital Clock 0" },
{ "Right Playback Mixer", NULL, "Slew Clock" },
{ "Left Playback Mixer", NULL, "Slew Clock" },
{ "Left Input Mixer", NULL, "ALC Clock" },
{ "Right Input Mixer", NULL, "ALC Clock" },
{ "Digital Clock 0", NULL, "SYSCLK" },
{ "Digital Clock 1", NULL, "SYSCLK" },
};
static int adau1761_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
struct adau *adau = snd_soc_codec_get_drvdata(codec);
switch (level) {
case SND_SOC_BIAS_ON:
break;
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
regmap_update_bits(adau->regmap, ADAU17X1_CLOCK_CONTROL,
ADAU17X1_CLOCK_CONTROL_SYSCLK_EN,
ADAU17X1_CLOCK_CONTROL_SYSCLK_EN);
break;
case SND_SOC_BIAS_OFF:
regmap_update_bits(adau->regmap, ADAU17X1_CLOCK_CONTROL,
ADAU17X1_CLOCK_CONTROL_SYSCLK_EN, 0);
break;
}
codec->dapm.bias_level = level;
return 0;
}
static enum adau1761_output_mode adau1761_get_lineout_mode(
struct snd_soc_codec *codec)
{
struct adau1761_platform_data *pdata = codec->dev->platform_data;
if (pdata)
return pdata->lineout_mode;
return ADAU1761_OUTPUT_MODE_LINE;
}
static int adau1761_setup_digmic_jackdetect(struct snd_soc_codec *codec)
{
struct adau1761_platform_data *pdata = codec->dev->platform_data;
struct adau *adau = snd_soc_codec_get_drvdata(codec);
enum adau1761_digmic_jackdet_pin_mode mode;
unsigned int val = 0;
int ret;
if (pdata)
mode = pdata->digmic_jackdetect_pin_mode;
else
mode = ADAU1761_DIGMIC_JACKDET_PIN_MODE_NONE;
switch (mode) {
case ADAU1761_DIGMIC_JACKDET_PIN_MODE_JACKDETECT:
switch (pdata->jackdetect_debounce_time) {
case ADAU1761_JACKDETECT_DEBOUNCE_5MS:
case ADAU1761_JACKDETECT_DEBOUNCE_10MS:
case ADAU1761_JACKDETECT_DEBOUNCE_20MS:
case ADAU1761_JACKDETECT_DEBOUNCE_40MS:
val |= pdata->jackdetect_debounce_time << 6;
break;
default:
return -EINVAL;
}
if (pdata->jackdetect_active_low)
val |= ADAU1761_DIGMIC_JACKDETECT_ACTIVE_LOW;
ret = snd_soc_add_codec_controls(codec,
adau1761_jack_detect_controls,
ARRAY_SIZE(adau1761_jack_detect_controls));
if (ret)
return ret;
case ADAU1761_DIGMIC_JACKDET_PIN_MODE_NONE: /* fallthrough */
ret = snd_soc_dapm_add_routes(&codec->dapm,
adau1761_no_dmic_routes,
ARRAY_SIZE(adau1761_no_dmic_routes));
if (ret)
return ret;
break;
case ADAU1761_DIGMIC_JACKDET_PIN_MODE_DIGMIC:
ret = snd_soc_dapm_new_controls(&codec->dapm,
adau1761_dmic_widgets,
ARRAY_SIZE(adau1761_dmic_widgets));
if (ret)
return ret;
ret = snd_soc_dapm_add_routes(&codec->dapm,
adau1761_dmic_routes,
ARRAY_SIZE(adau1761_dmic_routes));
if (ret)
return ret;
val |= ADAU1761_DIGMIC_JACKDETECT_DIGMIC;
break;
default:
return -EINVAL;
}
regmap_write(adau->regmap, ADAU1761_DIGMIC_JACKDETECT, val);
return 0;
}
static int adau1761_setup_headphone_mode(struct snd_soc_codec *codec)
{
struct adau *adau = snd_soc_codec_get_drvdata(codec);
struct adau1761_platform_data *pdata = codec->dev->platform_data;
enum adau1761_output_mode mode;
int ret;
if (pdata)
mode = pdata->headphone_mode;
else
mode = ADAU1761_OUTPUT_MODE_HEADPHONE;
switch (mode) {
case ADAU1761_OUTPUT_MODE_LINE:
break;
case ADAU1761_OUTPUT_MODE_HEADPHONE_CAPLESS:
regmap_update_bits(adau->regmap, ADAU1761_PLAY_MONO_OUTPUT_VOL,
ADAU1761_PLAY_MONO_OUTPUT_VOL_MODE_HP |
ADAU1761_PLAY_MONO_OUTPUT_VOL_UNMUTE,
ADAU1761_PLAY_MONO_OUTPUT_VOL_MODE_HP |
ADAU1761_PLAY_MONO_OUTPUT_VOL_UNMUTE);
/* fallthrough */
case ADAU1761_OUTPUT_MODE_HEADPHONE:
regmap_update_bits(adau->regmap, ADAU1761_PLAY_HP_RIGHT_VOL,
ADAU1761_PLAY_HP_RIGHT_VOL_MODE_HP,
ADAU1761_PLAY_HP_RIGHT_VOL_MODE_HP);
break;
default:
return -EINVAL;
}
if (mode == ADAU1761_OUTPUT_MODE_HEADPHONE_CAPLESS) {
ret = snd_soc_dapm_new_controls(&codec->dapm,
adau1761_capless_dapm_widgets,
ARRAY_SIZE(adau1761_capless_dapm_widgets));
if (ret)
return ret;
ret = snd_soc_dapm_add_routes(&codec->dapm,
adau1761_capless_dapm_routes,
ARRAY_SIZE(adau1761_capless_dapm_routes));
} else {
ret = snd_soc_add_codec_controls(codec, adau1761_mono_controls,
ARRAY_SIZE(adau1761_mono_controls));
if (ret)
return ret;
ret = snd_soc_dapm_new_controls(&codec->dapm,
adau1761_mono_dapm_widgets,
ARRAY_SIZE(adau1761_mono_dapm_widgets));
if (ret)
return ret;
ret = snd_soc_dapm_add_routes(&codec->dapm,
adau1761_mono_dapm_routes,
ARRAY_SIZE(adau1761_mono_dapm_routes));
}
return ret;
}
static bool adau1761_readable_register(struct device *dev, unsigned int reg)
{
switch (reg) {
case ADAU1761_DIGMIC_JACKDETECT:
case ADAU1761_REC_MIXER_LEFT0:
case ADAU1761_REC_MIXER_LEFT1:
case ADAU1761_REC_MIXER_RIGHT0:
case ADAU1761_REC_MIXER_RIGHT1:
case ADAU1761_LEFT_DIFF_INPUT_VOL:
case ADAU1761_RIGHT_DIFF_INPUT_VOL:
case ADAU1761_PLAY_LR_MIXER_LEFT:
case ADAU1761_PLAY_MIXER_LEFT0:
case ADAU1761_PLAY_MIXER_LEFT1:
case ADAU1761_PLAY_MIXER_RIGHT0:
case ADAU1761_PLAY_MIXER_RIGHT1:
case ADAU1761_PLAY_LR_MIXER_RIGHT:
case ADAU1761_PLAY_MIXER_MONO:
case ADAU1761_PLAY_HP_LEFT_VOL:
case ADAU1761_PLAY_HP_RIGHT_VOL:
case ADAU1761_PLAY_LINE_LEFT_VOL:
case ADAU1761_PLAY_LINE_RIGHT_VOL:
case ADAU1761_PLAY_MONO_OUTPUT_VOL:
case ADAU1761_POP_CLICK_SUPPRESS:
case ADAU1761_JACK_DETECT_PIN:
case ADAU1761_DEJITTER:
case ADAU1761_CLK_ENABLE0:
case ADAU1761_CLK_ENABLE1:
return true;
default:
break;
}
return adau17x1_readable_register(dev, reg);
}
static int adau1761_codec_probe(struct snd_soc_codec *codec)
{
struct adau1761_platform_data *pdata = codec->dev->platform_data;
struct adau *adau = snd_soc_codec_get_drvdata(codec);
int ret;
ret = adau17x1_add_widgets(codec);
if (ret < 0)
return ret;
if (pdata && pdata->input_differential) {
regmap_update_bits(adau->regmap, ADAU1761_LEFT_DIFF_INPUT_VOL,
ADAU1761_DIFF_INPUT_VOL_LDEN,
ADAU1761_DIFF_INPUT_VOL_LDEN);
regmap_update_bits(adau->regmap, ADAU1761_RIGHT_DIFF_INPUT_VOL,
ADAU1761_DIFF_INPUT_VOL_LDEN,
ADAU1761_DIFF_INPUT_VOL_LDEN);
ret = snd_soc_add_codec_controls(codec,
adau1761_differential_mode_controls,
ARRAY_SIZE(adau1761_differential_mode_controls));
if (ret)
return ret;
} else {
ret = snd_soc_add_codec_controls(codec,
adau1761_single_mode_controls,
ARRAY_SIZE(adau1761_single_mode_controls));
if (ret)
return ret;
}
switch (adau1761_get_lineout_mode(codec)) {
case ADAU1761_OUTPUT_MODE_LINE:
break;
case ADAU1761_OUTPUT_MODE_HEADPHONE:
regmap_update_bits(adau->regmap, ADAU1761_PLAY_LINE_LEFT_VOL,
ADAU1761_PLAY_LINE_LEFT_VOL_MODE_HP,
ADAU1761_PLAY_LINE_LEFT_VOL_MODE_HP);
regmap_update_bits(adau->regmap, ADAU1761_PLAY_LINE_RIGHT_VOL,
ADAU1761_PLAY_LINE_RIGHT_VOL_MODE_HP,
ADAU1761_PLAY_LINE_RIGHT_VOL_MODE_HP);
break;
default:
return -EINVAL;
}
ret = adau1761_setup_headphone_mode(codec);
if (ret)
return ret;
ret = adau1761_setup_digmic_jackdetect(codec);
if (ret)
return ret;
if (adau->type == ADAU1761) {
ret = snd_soc_dapm_new_controls(&codec->dapm,
adau1761_dapm_widgets,
ARRAY_SIZE(adau1761_dapm_widgets));
if (ret)
return ret;
ret = snd_soc_dapm_add_routes(&codec->dapm,
adau1761_dapm_routes,
ARRAY_SIZE(adau1761_dapm_routes));
if (ret)
return ret;
ret = adau17x1_load_firmware(adau, codec->dev,
ADAU1761_FIRMWARE);
if (ret)
dev_warn(codec->dev, "Failed to firmware\n");
}
ret = adau17x1_add_routes(codec);
if (ret < 0)
return ret;
return 0;
}
static const struct snd_soc_codec_driver adau1761_codec_driver = {
.probe = adau1761_codec_probe,
.resume = adau17x1_resume,
.set_bias_level = adau1761_set_bias_level,
.suspend_bias_off = true,
.controls = adau1761_controls,
.num_controls = ARRAY_SIZE(adau1761_controls),
.dapm_widgets = adau1x61_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(adau1x61_dapm_widgets),
.dapm_routes = adau1x61_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(adau1x61_dapm_routes),
};
#define ADAU1761_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \
SNDRV_PCM_FMTBIT_S32_LE)
static struct snd_soc_dai_driver adau1361_dai_driver = {
.name = "adau-hifi",
.playback = {
.stream_name = "Playback",
.channels_min = 2,
.channels_max = 4,
.rates = SNDRV_PCM_RATE_8000_96000,
.formats = ADAU1761_FORMATS,
},
.capture = {
.stream_name = "Capture",
.channels_min = 2,
.channels_max = 4,
.rates = SNDRV_PCM_RATE_8000_96000,
.formats = ADAU1761_FORMATS,
},
.ops = &adau17x1_dai_ops,
};
static struct snd_soc_dai_driver adau1761_dai_driver = {
.name = "adau-hifi",
.playback = {
.stream_name = "Playback",
.channels_min = 2,
.channels_max = 8,
.rates = SNDRV_PCM_RATE_8000_96000,
.formats = ADAU1761_FORMATS,
},
.capture = {
.stream_name = "Capture",
.channels_min = 2,
.channels_max = 8,
.rates = SNDRV_PCM_RATE_8000_96000,
.formats = ADAU1761_FORMATS,
},
.ops = &adau17x1_dai_ops,
};
int adau1761_probe(struct device *dev, struct regmap *regmap,
enum adau17x1_type type, void (*switch_mode)(struct device *dev))
{
struct snd_soc_dai_driver *dai_drv;
int ret;
ret = adau17x1_probe(dev, regmap, type, switch_mode);
if (ret)
return ret;
if (type == ADAU1361)
dai_drv = &adau1361_dai_driver;
else
dai_drv = &adau1761_dai_driver;
return snd_soc_register_codec(dev, &adau1761_codec_driver, dai_drv, 1);
}
EXPORT_SYMBOL_GPL(adau1761_probe);
const struct regmap_config adau1761_regmap_config = {
.val_bits = 8,
.reg_bits = 16,
.max_register = 0x40fa,
.reg_defaults = adau1761_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(adau1761_reg_defaults),
.readable_reg = adau1761_readable_register,
.volatile_reg = adau17x1_volatile_register,
.cache_type = REGCACHE_RBTREE,
};
EXPORT_SYMBOL_GPL(adau1761_regmap_config);
MODULE_DESCRIPTION("ASoC ADAU1361/ADAU1461/ADAU1761/ADAU1961 CODEC driver");
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,23 @@
/*
* ADAU1361/ADAU1461/ADAU1761/ADAU1961 driver
*
* Copyright 2014 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
*
* Licensed under the GPL-2.
*/
#ifndef __SOUND_SOC_CODECS_ADAU1761_H__
#define __SOUND_SOC_CODECS_ADAU1761_H__
#include <linux/regmap.h>
#include "adau17x1.h"
struct device;
int adau1761_probe(struct device *dev, struct regmap *regmap,
enum adau17x1_type type, void (*switch_mode)(struct device *dev));
extern const struct regmap_config adau1761_regmap_config;
#endif

View file

@ -0,0 +1,58 @@
/*
* Driver for ADAU1381/ADAU1781 CODEC
*
* Copyright 2014 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
*
* Licensed under the GPL-2.
*/
#include <linux/i2c.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <sound/soc.h>
#include "adau1781.h"
static int adau1781_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct regmap_config config;
config = adau1781_regmap_config;
config.val_bits = 8;
config.reg_bits = 16;
return adau1781_probe(&client->dev,
devm_regmap_init_i2c(client, &config),
id->driver_data, NULL);
}
static int adau1781_i2c_remove(struct i2c_client *client)
{
snd_soc_unregister_codec(&client->dev);
return 0;
}
static const struct i2c_device_id adau1781_i2c_ids[] = {
{ "adau1381", ADAU1381 },
{ "adau1781", ADAU1781 },
{ }
};
MODULE_DEVICE_TABLE(i2c, adau1781_i2c_ids);
static struct i2c_driver adau1781_i2c_driver = {
.driver = {
.name = "adau1781",
.owner = THIS_MODULE,
},
.probe = adau1781_i2c_probe,
.remove = adau1781_i2c_remove,
.id_table = adau1781_i2c_ids,
};
module_i2c_driver(adau1781_i2c_driver);
MODULE_DESCRIPTION("ASoC ADAU1381/ADAU1781 CODEC I2C driver");
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,75 @@
/*
* Driver for ADAU1381/ADAU1781 CODEC
*
* Copyright 2014 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
*
* Licensed under the GPL-2.
*/
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/spi/spi.h>
#include <sound/soc.h>
#include "adau1781.h"
static void adau1781_spi_switch_mode(struct device *dev)
{
struct spi_device *spi = to_spi_device(dev);
/*
* To get the device into SPI mode CLATCH has to be pulled low three
* times. Do this by issuing three dummy reads.
*/
spi_w8r8(spi, 0x00);
spi_w8r8(spi, 0x00);
spi_w8r8(spi, 0x00);
}
static int adau1781_spi_probe(struct spi_device *spi)
{
const struct spi_device_id *id = spi_get_device_id(spi);
struct regmap_config config;
if (!id)
return -EINVAL;
config = adau1781_regmap_config;
config.val_bits = 8;
config.reg_bits = 24;
config.read_flag_mask = 0x1;
return adau1781_probe(&spi->dev,
devm_regmap_init_spi(spi, &config),
id->driver_data, adau1781_spi_switch_mode);
}
static int adau1781_spi_remove(struct spi_device *spi)
{
snd_soc_unregister_codec(&spi->dev);
return 0;
}
static const struct spi_device_id adau1781_spi_id[] = {
{ "adau1381", ADAU1381 },
{ "adau1781", ADAU1781 },
{ }
};
MODULE_DEVICE_TABLE(spi, adau1781_spi_id);
static struct spi_driver adau1781_spi_driver = {
.driver = {
.name = "adau1781",
.owner = THIS_MODULE,
},
.probe = adau1781_spi_probe,
.remove = adau1781_spi_remove,
.id_table = adau1781_spi_id,
};
module_spi_driver(adau1781_spi_driver);
MODULE_DESCRIPTION("ASoC ADAU1381/ADAU1781 CODEC SPI driver");
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
MODULE_LICENSE("GPL");

511
sound/soc/codecs/adau1781.c Normal file
View file

@ -0,0 +1,511 @@
/*
* Driver for ADAU1781/ADAU1781 codec
*
* Copyright 2011-2013 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
*
* Licensed under the GPL-2 or later.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/spi/spi.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/tlv.h>
#include <linux/platform_data/adau17x1.h>
#include "adau17x1.h"
#include "adau1781.h"
#define ADAU1781_DMIC_BEEP_CTRL 0x4008
#define ADAU1781_LEFT_PGA 0x400e
#define ADAU1781_RIGHT_PGA 0x400f
#define ADAU1781_LEFT_PLAYBACK_MIXER 0x401c
#define ADAU1781_RIGHT_PLAYBACK_MIXER 0x401e
#define ADAU1781_MONO_PLAYBACK_MIXER 0x401f
#define ADAU1781_LEFT_LINEOUT 0x4025
#define ADAU1781_RIGHT_LINEOUT 0x4026
#define ADAU1781_SPEAKER 0x4027
#define ADAU1781_BEEP_ZC 0x4028
#define ADAU1781_DEJITTER 0x4032
#define ADAU1781_DIG_PWDN0 0x4080
#define ADAU1781_DIG_PWDN1 0x4081
#define ADAU1781_INPUT_DIFFERNTIAL BIT(3)
#define ADAU1381_FIRMWARE "adau1381.bin"
#define ADAU1781_FIRMWARE "adau1781.bin"
static const struct reg_default adau1781_reg_defaults[] = {
{ ADAU1781_DMIC_BEEP_CTRL, 0x00 },
{ ADAU1781_LEFT_PGA, 0xc7 },
{ ADAU1781_RIGHT_PGA, 0xc7 },
{ ADAU1781_LEFT_PLAYBACK_MIXER, 0x00 },
{ ADAU1781_RIGHT_PLAYBACK_MIXER, 0x00 },
{ ADAU1781_MONO_PLAYBACK_MIXER, 0x00 },
{ ADAU1781_LEFT_LINEOUT, 0x00 },
{ ADAU1781_RIGHT_LINEOUT, 0x00 },
{ ADAU1781_SPEAKER, 0x00 },
{ ADAU1781_BEEP_ZC, 0x19 },
{ ADAU1781_DEJITTER, 0x60 },
{ ADAU1781_DIG_PWDN1, 0x0c },
{ ADAU1781_DIG_PWDN1, 0x00 },
{ ADAU17X1_CLOCK_CONTROL, 0x00 },
{ ADAU17X1_PLL_CONTROL, 0x00 },
{ ADAU17X1_REC_POWER_MGMT, 0x00 },
{ ADAU17X1_MICBIAS, 0x04 },
{ ADAU17X1_SERIAL_PORT0, 0x00 },
{ ADAU17X1_SERIAL_PORT1, 0x00 },
{ ADAU17X1_CONVERTER0, 0x00 },
{ ADAU17X1_CONVERTER1, 0x00 },
{ ADAU17X1_LEFT_INPUT_DIGITAL_VOL, 0x00 },
{ ADAU17X1_RIGHT_INPUT_DIGITAL_VOL, 0x00 },
{ ADAU17X1_ADC_CONTROL, 0x00 },
{ ADAU17X1_PLAY_POWER_MGMT, 0x00 },
{ ADAU17X1_DAC_CONTROL0, 0x00 },
{ ADAU17X1_DAC_CONTROL1, 0x00 },
{ ADAU17X1_DAC_CONTROL2, 0x00 },
{ ADAU17X1_SERIAL_PORT_PAD, 0x00 },
{ ADAU17X1_CONTROL_PORT_PAD0, 0x00 },
{ ADAU17X1_CONTROL_PORT_PAD1, 0x00 },
{ ADAU17X1_DSP_SAMPLING_RATE, 0x01 },
{ ADAU17X1_SERIAL_INPUT_ROUTE, 0x00 },
{ ADAU17X1_SERIAL_OUTPUT_ROUTE, 0x00 },
{ ADAU17X1_DSP_ENABLE, 0x00 },
{ ADAU17X1_DSP_RUN, 0x00 },
{ ADAU17X1_SERIAL_SAMPLING_RATE, 0x00 },
};
static const DECLARE_TLV_DB_SCALE(adau1781_speaker_tlv, 0, 200, 0);
static const DECLARE_TLV_DB_RANGE(adau1781_pga_tlv,
0, 1, TLV_DB_SCALE_ITEM(0, 600, 0),
2, 3, TLV_DB_SCALE_ITEM(1000, 400, 0),
4, 4, TLV_DB_SCALE_ITEM(1700, 0, 0),
5, 7, TLV_DB_SCALE_ITEM(2000, 600, 0)
);
static const DECLARE_TLV_DB_RANGE(adau1781_beep_tlv,
0, 1, TLV_DB_SCALE_ITEM(0, 600, 0),
2, 3, TLV_DB_SCALE_ITEM(1000, 400, 0),
4, 4, TLV_DB_SCALE_ITEM(-2300, 0, 0),
5, 7, TLV_DB_SCALE_ITEM(2000, 600, 0)
);
static const DECLARE_TLV_DB_SCALE(adau1781_sidetone_tlv, -1800, 300, 1);
static const char * const adau1781_speaker_bias_select_text[] = {
"Normal operation", "Power saving", "Enhanced performance",
};
static const char * const adau1781_bias_select_text[] = {
"Normal operation", "Extreme power saving", "Power saving",
"Enhanced performance",
};
static SOC_ENUM_SINGLE_DECL(adau1781_adc_bias_enum,
ADAU17X1_REC_POWER_MGMT, 3, adau1781_bias_select_text);
static SOC_ENUM_SINGLE_DECL(adau1781_speaker_bias_enum,
ADAU17X1_PLAY_POWER_MGMT, 6, adau1781_speaker_bias_select_text);
static SOC_ENUM_SINGLE_DECL(adau1781_dac_bias_enum,
ADAU17X1_PLAY_POWER_MGMT, 4, adau1781_bias_select_text);
static SOC_ENUM_SINGLE_DECL(adau1781_playback_bias_enum,
ADAU17X1_PLAY_POWER_MGMT, 2, adau1781_bias_select_text);
static SOC_ENUM_SINGLE_DECL(adau1781_capture_bias_enum,
ADAU17X1_REC_POWER_MGMT, 1, adau1781_bias_select_text);
static const struct snd_kcontrol_new adau1781_controls[] = {
SOC_SINGLE_TLV("Beep Capture Volume", ADAU1781_DMIC_BEEP_CTRL, 0, 7, 0,
adau1781_beep_tlv),
SOC_DOUBLE_R_TLV("PGA Capture Volume", ADAU1781_LEFT_PGA,
ADAU1781_RIGHT_PGA, 5, 7, 0, adau1781_pga_tlv),
SOC_DOUBLE_R("PGA Capture Switch", ADAU1781_LEFT_PGA,
ADAU1781_RIGHT_PGA, 1, 1, 0),
SOC_DOUBLE_R("Lineout Playback Switch", ADAU1781_LEFT_LINEOUT,
ADAU1781_RIGHT_LINEOUT, 1, 1, 0),
SOC_SINGLE("Beep ZC Switch", ADAU1781_BEEP_ZC, 0, 1, 0),
SOC_SINGLE("Mono Playback Switch", ADAU1781_MONO_PLAYBACK_MIXER,
0, 1, 0),
SOC_SINGLE_TLV("Mono Playback Volume", ADAU1781_SPEAKER, 6, 3, 0,
adau1781_speaker_tlv),
SOC_ENUM("ADC Bias", adau1781_adc_bias_enum),
SOC_ENUM("DAC Bias", adau1781_dac_bias_enum),
SOC_ENUM("Capture Bias", adau1781_capture_bias_enum),
SOC_ENUM("Playback Bias", adau1781_playback_bias_enum),
SOC_ENUM("Speaker Bias", adau1781_speaker_bias_enum),
};
static const struct snd_kcontrol_new adau1781_beep_mixer_controls[] = {
SOC_DAPM_SINGLE("Beep Capture Switch", ADAU1781_DMIC_BEEP_CTRL,
3, 1, 0),
};
static const struct snd_kcontrol_new adau1781_left_mixer_controls[] = {
SOC_DAPM_SINGLE_AUTODISABLE("Switch",
ADAU1781_LEFT_PLAYBACK_MIXER, 5, 1, 0),
SOC_DAPM_SINGLE_TLV("Beep Playback Volume",
ADAU1781_LEFT_PLAYBACK_MIXER, 1, 8, 0, adau1781_sidetone_tlv),
};
static const struct snd_kcontrol_new adau1781_right_mixer_controls[] = {
SOC_DAPM_SINGLE_AUTODISABLE("Switch",
ADAU1781_RIGHT_PLAYBACK_MIXER, 6, 1, 0),
SOC_DAPM_SINGLE_TLV("Beep Playback Volume",
ADAU1781_LEFT_PLAYBACK_MIXER, 1, 8, 0, adau1781_sidetone_tlv),
};
static const struct snd_kcontrol_new adau1781_mono_mixer_controls[] = {
SOC_DAPM_SINGLE_AUTODISABLE("Left Switch",
ADAU1781_MONO_PLAYBACK_MIXER, 7, 1, 0),
SOC_DAPM_SINGLE_AUTODISABLE("Right Switch",
ADAU1781_MONO_PLAYBACK_MIXER, 6, 1, 0),
SOC_DAPM_SINGLE_TLV("Beep Playback Volume",
ADAU1781_MONO_PLAYBACK_MIXER, 2, 8, 0, adau1781_sidetone_tlv),
};
static int adau1781_dejitter_fixup(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = w->codec;
struct adau *adau = snd_soc_codec_get_drvdata(codec);
/* After any power changes have been made the dejitter circuit
* has to be reinitialized. */
regmap_write(adau->regmap, ADAU1781_DEJITTER, 0);
if (!adau->master)
regmap_write(adau->regmap, ADAU1781_DEJITTER, 5);
return 0;
}
static const struct snd_soc_dapm_widget adau1781_dapm_widgets[] = {
SND_SOC_DAPM_PGA("Left PGA", ADAU1781_LEFT_PGA, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("Right PGA", ADAU1781_RIGHT_PGA, 0, 0, NULL, 0),
SND_SOC_DAPM_OUT_DRV("Speaker", ADAU1781_SPEAKER, 0, 0, NULL, 0),
SOC_MIXER_NAMED_CTL_ARRAY("Beep Mixer", ADAU17X1_MICBIAS, 4, 0,
adau1781_beep_mixer_controls),
SOC_MIXER_ARRAY("Left Lineout Mixer", SND_SOC_NOPM, 0, 0,
adau1781_left_mixer_controls),
SOC_MIXER_ARRAY("Right Lineout Mixer", SND_SOC_NOPM, 0, 0,
adau1781_right_mixer_controls),
SOC_MIXER_ARRAY("Mono Mixer", SND_SOC_NOPM, 0, 0,
adau1781_mono_mixer_controls),
SND_SOC_DAPM_SUPPLY("Serial Input Routing", ADAU1781_DIG_PWDN0,
2, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("Serial Output Routing", ADAU1781_DIG_PWDN0,
3, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("Clock Domain Transfer", ADAU1781_DIG_PWDN0,
5, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("Serial Ports", ADAU1781_DIG_PWDN0, 4, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("ADC Engine", ADAU1781_DIG_PWDN0, 7, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("DAC Engine", ADAU1781_DIG_PWDN1, 0, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("Digital Mic", ADAU1781_DIG_PWDN1, 1, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("Sound Engine", ADAU1781_DIG_PWDN0, 0, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("SYSCLK", 1, ADAU1781_DIG_PWDN0, 1, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("Zero Crossing Detector", ADAU1781_DIG_PWDN1, 2, 0,
NULL, 0),
SND_SOC_DAPM_POST("Dejitter fixup", adau1781_dejitter_fixup),
SND_SOC_DAPM_INPUT("BEEP"),
SND_SOC_DAPM_OUTPUT("AOUTL"),
SND_SOC_DAPM_OUTPUT("AOUTR"),
SND_SOC_DAPM_OUTPUT("SP"),
SND_SOC_DAPM_INPUT("LMIC"),
SND_SOC_DAPM_INPUT("RMIC"),
};
static const struct snd_soc_dapm_route adau1781_dapm_routes[] = {
{ "Left Lineout Mixer", NULL, "Left Playback Enable" },
{ "Right Lineout Mixer", NULL, "Right Playback Enable" },
{ "Left Lineout Mixer", "Beep Playback Volume", "Beep Mixer" },
{ "Left Lineout Mixer", "Switch", "Left DAC" },
{ "Right Lineout Mixer", "Beep Playback Volume", "Beep Mixer" },
{ "Right Lineout Mixer", "Switch", "Right DAC" },
{ "Mono Mixer", "Beep Playback Volume", "Beep Mixer" },
{ "Mono Mixer", "Right Switch", "Right DAC" },
{ "Mono Mixer", "Left Switch", "Left DAC" },
{ "Speaker", NULL, "Mono Mixer" },
{ "Mono Mixer", NULL, "SYSCLK" },
{ "Left Lineout Mixer", NULL, "SYSCLK" },
{ "Left Lineout Mixer", NULL, "SYSCLK" },
{ "Beep Mixer", "Beep Capture Switch", "BEEP" },
{ "Beep Mixer", NULL, "Zero Crossing Detector" },
{ "Left DAC", NULL, "DAC Engine" },
{ "Right DAC", NULL, "DAC Engine" },
{ "Sound Engine", NULL, "SYSCLK" },
{ "DSP", NULL, "Sound Engine" },
{ "Left Decimator", NULL, "ADC Engine" },
{ "Right Decimator", NULL, "ADC Engine" },
{ "AIFCLK", NULL, "SYSCLK" },
{ "Playback", NULL, "Serial Input Routing" },
{ "Playback", NULL, "Serial Ports" },
{ "Playback", NULL, "Clock Domain Transfer" },
{ "Capture", NULL, "Serial Output Routing" },
{ "Capture", NULL, "Serial Ports" },
{ "Capture", NULL, "Clock Domain Transfer" },
{ "AOUTL", NULL, "Left Lineout Mixer" },
{ "AOUTR", NULL, "Right Lineout Mixer" },
{ "SP", NULL, "Speaker" },
};
static const struct snd_soc_dapm_route adau1781_adc_dapm_routes[] = {
{ "Left PGA", NULL, "LMIC" },
{ "Right PGA", NULL, "RMIC" },
{ "Left Decimator", NULL, "Left PGA" },
{ "Right Decimator", NULL, "Right PGA" },
};
static const char * const adau1781_dmic_select_text[] = {
"DMIC1", "DMIC2",
};
static SOC_ENUM_SINGLE_VIRT_DECL(adau1781_dmic_select_enum,
adau1781_dmic_select_text);
static const struct snd_kcontrol_new adau1781_dmic_mux =
SOC_DAPM_ENUM("DMIC Select", adau1781_dmic_select_enum);
static const struct snd_soc_dapm_widget adau1781_dmic_dapm_widgets[] = {
SND_SOC_DAPM_MUX("DMIC Select", SND_SOC_NOPM, 0, 0, &adau1781_dmic_mux),
SND_SOC_DAPM_ADC("DMIC1", NULL, ADAU1781_DMIC_BEEP_CTRL, 4, 0),
SND_SOC_DAPM_ADC("DMIC2", NULL, ADAU1781_DMIC_BEEP_CTRL, 5, 0),
};
static const struct snd_soc_dapm_route adau1781_dmic_dapm_routes[] = {
{ "DMIC1", NULL, "LMIC" },
{ "DMIC2", NULL, "RMIC" },
{ "DMIC1", NULL, "Digital Mic" },
{ "DMIC2", NULL, "Digital Mic" },
{ "DMIC Select", "DMIC1", "DMIC1" },
{ "DMIC Select", "DMIC2", "DMIC2" },
{ "Left Decimator", NULL, "DMIC Select" },
{ "Right Decimator", NULL, "DMIC Select" },
};
static int adau1781_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
struct adau *adau = snd_soc_codec_get_drvdata(codec);
switch (level) {
case SND_SOC_BIAS_ON:
break;
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
regmap_update_bits(adau->regmap, ADAU17X1_CLOCK_CONTROL,
ADAU17X1_CLOCK_CONTROL_SYSCLK_EN,
ADAU17X1_CLOCK_CONTROL_SYSCLK_EN);
/* Precharge */
regmap_update_bits(adau->regmap, ADAU1781_DIG_PWDN1, 0x8, 0x8);
break;
case SND_SOC_BIAS_OFF:
regmap_update_bits(adau->regmap, ADAU1781_DIG_PWDN1, 0xc, 0x0);
regmap_update_bits(adau->regmap, ADAU17X1_CLOCK_CONTROL,
ADAU17X1_CLOCK_CONTROL_SYSCLK_EN, 0);
break;
}
codec->dapm.bias_level = level;
return 0;
}
static bool adau1781_readable_register(struct device *dev, unsigned int reg)
{
switch (reg) {
case ADAU1781_DMIC_BEEP_CTRL:
case ADAU1781_LEFT_PGA:
case ADAU1781_RIGHT_PGA:
case ADAU1781_LEFT_PLAYBACK_MIXER:
case ADAU1781_RIGHT_PLAYBACK_MIXER:
case ADAU1781_MONO_PLAYBACK_MIXER:
case ADAU1781_LEFT_LINEOUT:
case ADAU1781_RIGHT_LINEOUT:
case ADAU1781_SPEAKER:
case ADAU1781_BEEP_ZC:
case ADAU1781_DEJITTER:
case ADAU1781_DIG_PWDN0:
case ADAU1781_DIG_PWDN1:
return true;
default:
break;
}
return adau17x1_readable_register(dev, reg);
}
static int adau1781_set_input_mode(struct adau *adau, unsigned int reg,
bool differential)
{
unsigned int val;
if (differential)
val = ADAU1781_INPUT_DIFFERNTIAL;
else
val = 0;
return regmap_update_bits(adau->regmap, reg,
ADAU1781_INPUT_DIFFERNTIAL, val);
}
static int adau1781_codec_probe(struct snd_soc_codec *codec)
{
struct adau1781_platform_data *pdata = dev_get_platdata(codec->dev);
struct adau *adau = snd_soc_codec_get_drvdata(codec);
const char *firmware;
int ret;
ret = adau17x1_add_widgets(codec);
if (ret)
return ret;
if (pdata) {
ret = adau1781_set_input_mode(adau, ADAU1781_LEFT_PGA,
pdata->left_input_differential);
if (ret)
return ret;
ret = adau1781_set_input_mode(adau, ADAU1781_RIGHT_PGA,
pdata->right_input_differential);
if (ret)
return ret;
}
if (pdata && pdata->use_dmic) {
ret = snd_soc_dapm_new_controls(&codec->dapm,
adau1781_dmic_dapm_widgets,
ARRAY_SIZE(adau1781_dmic_dapm_widgets));
if (ret)
return ret;
ret = snd_soc_dapm_add_routes(&codec->dapm,
adau1781_dmic_dapm_routes,
ARRAY_SIZE(adau1781_dmic_dapm_routes));
if (ret)
return ret;
} else {
ret = snd_soc_dapm_add_routes(&codec->dapm,
adau1781_adc_dapm_routes,
ARRAY_SIZE(adau1781_adc_dapm_routes));
if (ret)
return ret;
}
switch (adau->type) {
case ADAU1381:
firmware = ADAU1381_FIRMWARE;
break;
case ADAU1781:
firmware = ADAU1781_FIRMWARE;
break;
default:
return -EINVAL;
}
ret = adau17x1_add_routes(codec);
if (ret < 0)
return ret;
ret = adau17x1_load_firmware(adau, codec->dev, firmware);
if (ret)
dev_warn(codec->dev, "Failed to load firmware\n");
return 0;
}
static const struct snd_soc_codec_driver adau1781_codec_driver = {
.probe = adau1781_codec_probe,
.resume = adau17x1_resume,
.set_bias_level = adau1781_set_bias_level,
.suspend_bias_off = true,
.controls = adau1781_controls,
.num_controls = ARRAY_SIZE(adau1781_controls),
.dapm_widgets = adau1781_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(adau1781_dapm_widgets),
.dapm_routes = adau1781_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(adau1781_dapm_routes),
};
#define ADAU1781_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \
SNDRV_PCM_FMTBIT_S32_LE)
static struct snd_soc_dai_driver adau1781_dai_driver = {
.name = "adau-hifi",
.playback = {
.stream_name = "Playback",
.channels_min = 2,
.channels_max = 8,
.rates = SNDRV_PCM_RATE_8000_96000,
.formats = ADAU1781_FORMATS,
},
.capture = {
.stream_name = "Capture",
.channels_min = 2,
.channels_max = 8,
.rates = SNDRV_PCM_RATE_8000_96000,
.formats = ADAU1781_FORMATS,
},
.ops = &adau17x1_dai_ops,
};
const struct regmap_config adau1781_regmap_config = {
.val_bits = 8,
.reg_bits = 16,
.max_register = 0x40f8,
.reg_defaults = adau1781_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(adau1781_reg_defaults),
.readable_reg = adau1781_readable_register,
.volatile_reg = adau17x1_volatile_register,
.cache_type = REGCACHE_RBTREE,
};
EXPORT_SYMBOL_GPL(adau1781_regmap_config);
int adau1781_probe(struct device *dev, struct regmap *regmap,
enum adau17x1_type type, void (*switch_mode)(struct device *dev))
{
int ret;
ret = adau17x1_probe(dev, regmap, type, switch_mode);
if (ret)
return ret;
return snd_soc_register_codec(dev, &adau1781_codec_driver,
&adau1781_dai_driver, 1);
}
EXPORT_SYMBOL_GPL(adau1781_probe);
MODULE_DESCRIPTION("ASoC ADAU1381/ADAU1781 driver");
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,23 @@
/*
* ADAU1381/ADAU1781 driver
*
* Copyright 2014 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
*
* Licensed under the GPL-2.
*/
#ifndef __SOUND_SOC_CODECS_ADAU1781_H__
#define __SOUND_SOC_CODECS_ADAU1781_H__
#include <linux/regmap.h>
#include "adau17x1.h"
struct device;
int adau1781_probe(struct device *dev, struct regmap *regmap,
enum adau17x1_type type, void (*switch_mode)(struct device *dev));
extern const struct regmap_config adau1781_regmap_config;
#endif

858
sound/soc/codecs/adau17x1.c Normal file
View file

@ -0,0 +1,858 @@
/*
* Common code for ADAU1X61 and ADAU1X81 codecs
*
* Copyright 2011-2014 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
*
* Licensed under the GPL-2 or later.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/tlv.h>
#include <linux/gcd.h>
#include <linux/i2c.h>
#include <linux/spi/spi.h>
#include <linux/regmap.h>
#include "sigmadsp.h"
#include "adau17x1.h"
static const char * const adau17x1_capture_mixer_boost_text[] = {
"Normal operation", "Boost Level 1", "Boost Level 2", "Boost Level 3",
};
static SOC_ENUM_SINGLE_DECL(adau17x1_capture_boost_enum,
ADAU17X1_REC_POWER_MGMT, 5, adau17x1_capture_mixer_boost_text);
static const char * const adau17x1_mic_bias_mode_text[] = {
"Normal operation", "High performance",
};
static SOC_ENUM_SINGLE_DECL(adau17x1_mic_bias_mode_enum,
ADAU17X1_MICBIAS, 3, adau17x1_mic_bias_mode_text);
static const DECLARE_TLV_DB_MINMAX(adau17x1_digital_tlv, -9563, 0);
static const struct snd_kcontrol_new adau17x1_controls[] = {
SOC_DOUBLE_R_TLV("Digital Capture Volume",
ADAU17X1_LEFT_INPUT_DIGITAL_VOL,
ADAU17X1_RIGHT_INPUT_DIGITAL_VOL,
0, 0xff, 1, adau17x1_digital_tlv),
SOC_DOUBLE_R_TLV("Digital Playback Volume", ADAU17X1_DAC_CONTROL1,
ADAU17X1_DAC_CONTROL2, 0, 0xff, 1, adau17x1_digital_tlv),
SOC_SINGLE("ADC High Pass Filter Switch", ADAU17X1_ADC_CONTROL,
5, 1, 0),
SOC_SINGLE("Playback De-emphasis Switch", ADAU17X1_DAC_CONTROL0,
2, 1, 0),
SOC_ENUM("Capture Boost", adau17x1_capture_boost_enum),
SOC_ENUM("Mic Bias Mode", adau17x1_mic_bias_mode_enum),
};
static int adau17x1_pll_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct adau *adau = snd_soc_codec_get_drvdata(w->codec);
int ret;
if (SND_SOC_DAPM_EVENT_ON(event)) {
adau->pll_regs[5] = 1;
} else {
adau->pll_regs[5] = 0;
/* Bypass the PLL when disabled, otherwise registers will become
* inaccessible. */
regmap_update_bits(adau->regmap, ADAU17X1_CLOCK_CONTROL,
ADAU17X1_CLOCK_CONTROL_CORECLK_SRC_PLL, 0);
}
/* The PLL register is 6 bytes long and can only be written at once. */
ret = regmap_raw_write(adau->regmap, ADAU17X1_PLL_CONTROL,
adau->pll_regs, ARRAY_SIZE(adau->pll_regs));
if (SND_SOC_DAPM_EVENT_ON(event)) {
mdelay(5);
regmap_update_bits(adau->regmap, ADAU17X1_CLOCK_CONTROL,
ADAU17X1_CLOCK_CONTROL_CORECLK_SRC_PLL,
ADAU17X1_CLOCK_CONTROL_CORECLK_SRC_PLL);
}
return 0;
}
static const char * const adau17x1_mono_stereo_text[] = {
"Stereo",
"Mono Left Channel (L+R)",
"Mono Right Channel (L+R)",
"Mono (L+R)",
};
static SOC_ENUM_SINGLE_DECL(adau17x1_dac_mode_enum,
ADAU17X1_DAC_CONTROL0, 6, adau17x1_mono_stereo_text);
static const struct snd_kcontrol_new adau17x1_dac_mode_mux =
SOC_DAPM_ENUM("DAC Mono-Stereo-Mode", adau17x1_dac_mode_enum);
static const struct snd_soc_dapm_widget adau17x1_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY_S("PLL", 3, SND_SOC_NOPM, 0, 0, adau17x1_pll_event,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_SUPPLY("AIFCLK", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("MICBIAS", ADAU17X1_MICBIAS, 0, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("Left Playback Enable", ADAU17X1_PLAY_POWER_MGMT,
0, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("Right Playback Enable", ADAU17X1_PLAY_POWER_MGMT,
1, 0, NULL, 0),
SND_SOC_DAPM_MUX("Left DAC Mode Mux", SND_SOC_NOPM, 0, 0,
&adau17x1_dac_mode_mux),
SND_SOC_DAPM_MUX("Right DAC Mode Mux", SND_SOC_NOPM, 0, 0,
&adau17x1_dac_mode_mux),
SND_SOC_DAPM_ADC("Left Decimator", NULL, ADAU17X1_ADC_CONTROL, 0, 0),
SND_SOC_DAPM_ADC("Right Decimator", NULL, ADAU17X1_ADC_CONTROL, 1, 0),
SND_SOC_DAPM_DAC("Left DAC", NULL, ADAU17X1_DAC_CONTROL0, 0, 0),
SND_SOC_DAPM_DAC("Right DAC", NULL, ADAU17X1_DAC_CONTROL0, 1, 0),
};
static const struct snd_soc_dapm_route adau17x1_dapm_routes[] = {
{ "Left Decimator", NULL, "SYSCLK" },
{ "Right Decimator", NULL, "SYSCLK" },
{ "Left DAC", NULL, "SYSCLK" },
{ "Right DAC", NULL, "SYSCLK" },
{ "Capture", NULL, "SYSCLK" },
{ "Playback", NULL, "SYSCLK" },
{ "Left DAC", NULL, "Left DAC Mode Mux" },
{ "Right DAC", NULL, "Right DAC Mode Mux" },
{ "Capture", NULL, "AIFCLK" },
{ "Playback", NULL, "AIFCLK" },
};
static const struct snd_soc_dapm_route adau17x1_dapm_pll_route = {
"SYSCLK", NULL, "PLL",
};
/*
* The MUX register for the Capture and Playback MUXs selects either DSP as
* source/destination or one of the TDM slots. The TDM slot is selected via
* snd_soc_dai_set_tdm_slot(), so we only expose whether to go to the DSP or
* directly to the DAI interface with this control.
*/
static int adau17x1_dsp_mux_enum_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
struct adau *adau = snd_soc_codec_get_drvdata(codec);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
struct snd_soc_dapm_update update;
unsigned int stream = e->shift_l;
unsigned int val, change;
int reg;
if (ucontrol->value.enumerated.item[0] >= e->items)
return -EINVAL;
switch (ucontrol->value.enumerated.item[0]) {
case 0:
val = 0;
adau->dsp_bypass[stream] = false;
break;
default:
val = (adau->tdm_slot[stream] * 2) + 1;
adau->dsp_bypass[stream] = true;
break;
}
if (stream == SNDRV_PCM_STREAM_PLAYBACK)
reg = ADAU17X1_SERIAL_INPUT_ROUTE;
else
reg = ADAU17X1_SERIAL_OUTPUT_ROUTE;
change = snd_soc_test_bits(codec, reg, 0xff, val);
if (change) {
update.kcontrol = kcontrol;
update.mask = 0xff;
update.reg = reg;
update.val = val;
snd_soc_dapm_mux_update_power(&codec->dapm, kcontrol,
ucontrol->value.enumerated.item[0], e, &update);
}
return change;
}
static int adau17x1_dsp_mux_enum_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
struct adau *adau = snd_soc_codec_get_drvdata(codec);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned int stream = e->shift_l;
unsigned int reg, val;
int ret;
if (stream == SNDRV_PCM_STREAM_PLAYBACK)
reg = ADAU17X1_SERIAL_INPUT_ROUTE;
else
reg = ADAU17X1_SERIAL_OUTPUT_ROUTE;
ret = regmap_read(adau->regmap, reg, &val);
if (ret)
return ret;
if (val != 0)
val = 1;
ucontrol->value.enumerated.item[0] = val;
return 0;
}
#define DECLARE_ADAU17X1_DSP_MUX_CTRL(_name, _label, _stream, _text) \
const struct snd_kcontrol_new _name = \
SOC_DAPM_ENUM_EXT(_label, (const struct soc_enum)\
SOC_ENUM_SINGLE(SND_SOC_NOPM, _stream, \
ARRAY_SIZE(_text), _text), \
adau17x1_dsp_mux_enum_get, adau17x1_dsp_mux_enum_put)
static const char * const adau17x1_dac_mux_text[] = {
"DSP",
"AIFIN",
};
static const char * const adau17x1_capture_mux_text[] = {
"DSP",
"Decimator",
};
static DECLARE_ADAU17X1_DSP_MUX_CTRL(adau17x1_dac_mux, "DAC Playback Mux",
SNDRV_PCM_STREAM_PLAYBACK, adau17x1_dac_mux_text);
static DECLARE_ADAU17X1_DSP_MUX_CTRL(adau17x1_capture_mux, "Capture Mux",
SNDRV_PCM_STREAM_CAPTURE, adau17x1_capture_mux_text);
static const struct snd_soc_dapm_widget adau17x1_dsp_dapm_widgets[] = {
SND_SOC_DAPM_PGA("DSP", ADAU17X1_DSP_RUN, 0, 0, NULL, 0),
SND_SOC_DAPM_SIGGEN("DSP Siggen"),
SND_SOC_DAPM_MUX("DAC Playback Mux", SND_SOC_NOPM, 0, 0,
&adau17x1_dac_mux),
SND_SOC_DAPM_MUX("Capture Mux", SND_SOC_NOPM, 0, 0,
&adau17x1_capture_mux),
};
static const struct snd_soc_dapm_route adau17x1_dsp_dapm_routes[] = {
{ "DAC Playback Mux", "DSP", "DSP" },
{ "DAC Playback Mux", "AIFIN", "Playback" },
{ "Left DAC Mode Mux", "Stereo", "DAC Playback Mux" },
{ "Left DAC Mode Mux", "Mono (L+R)", "DAC Playback Mux" },
{ "Left DAC Mode Mux", "Mono Left Channel (L+R)", "DAC Playback Mux" },
{ "Right DAC Mode Mux", "Stereo", "DAC Playback Mux" },
{ "Right DAC Mode Mux", "Mono (L+R)", "DAC Playback Mux" },
{ "Right DAC Mode Mux", "Mono Right Channel (L+R)", "DAC Playback Mux" },
{ "Capture Mux", "DSP", "DSP" },
{ "Capture Mux", "Decimator", "Left Decimator" },
{ "Capture Mux", "Decimator", "Right Decimator" },
{ "Capture", NULL, "Capture Mux" },
{ "DSP", NULL, "DSP Siggen" },
{ "DSP", NULL, "Left Decimator" },
{ "DSP", NULL, "Right Decimator" },
};
static const struct snd_soc_dapm_route adau17x1_no_dsp_dapm_routes[] = {
{ "Left DAC Mode Mux", "Stereo", "Playback" },
{ "Left DAC Mode Mux", "Mono (L+R)", "Playback" },
{ "Left DAC Mode Mux", "Mono Left Channel (L+R)", "Playback" },
{ "Right DAC Mode Mux", "Stereo", "Playback" },
{ "Right DAC Mode Mux", "Mono (L+R)", "Playback" },
{ "Right DAC Mode Mux", "Mono Right Channel (L+R)", "Playback" },
{ "Capture", NULL, "Left Decimator" },
{ "Capture", NULL, "Right Decimator" },
};
bool adau17x1_has_dsp(struct adau *adau)
{
switch (adau->type) {
case ADAU1761:
case ADAU1381:
case ADAU1781:
return true;
default:
return false;
}
}
EXPORT_SYMBOL_GPL(adau17x1_has_dsp);
static int adau17x1_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
struct adau *adau = snd_soc_codec_get_drvdata(codec);
unsigned int val, div, dsp_div;
unsigned int freq;
if (adau->clk_src == ADAU17X1_CLK_SRC_PLL)
freq = adau->pll_freq;
else
freq = adau->sysclk;
if (freq % params_rate(params) != 0)
return -EINVAL;
switch (freq / params_rate(params)) {
case 1024: /* fs */
div = 0;
dsp_div = 1;
break;
case 6144: /* fs / 6 */
div = 1;
dsp_div = 6;
break;
case 4096: /* fs / 4 */
div = 2;
dsp_div = 5;
break;
case 3072: /* fs / 3 */
div = 3;
dsp_div = 4;
break;
case 2048: /* fs / 2 */
div = 4;
dsp_div = 3;
break;
case 1536: /* fs / 1.5 */
div = 5;
dsp_div = 2;
break;
case 512: /* fs / 0.5 */
div = 6;
dsp_div = 0;
break;
default:
return -EINVAL;
}
regmap_update_bits(adau->regmap, ADAU17X1_CONVERTER0,
ADAU17X1_CONVERTER0_CONVSR_MASK, div);
if (adau17x1_has_dsp(adau)) {
regmap_write(adau->regmap, ADAU17X1_SERIAL_SAMPLING_RATE, div);
regmap_write(adau->regmap, ADAU17X1_DSP_SAMPLING_RATE, dsp_div);
}
if (adau->dai_fmt != SND_SOC_DAIFMT_RIGHT_J)
return 0;
switch (params_width(params)) {
case 16:
val = ADAU17X1_SERIAL_PORT1_DELAY16;
break;
case 24:
val = ADAU17X1_SERIAL_PORT1_DELAY8;
break;
case 32:
val = ADAU17X1_SERIAL_PORT1_DELAY0;
break;
default:
return -EINVAL;
}
return regmap_update_bits(adau->regmap, ADAU17X1_SERIAL_PORT1,
ADAU17X1_SERIAL_PORT1_DELAY_MASK, val);
}
static int adau17x1_set_dai_pll(struct snd_soc_dai *dai, int pll_id,
int source, unsigned int freq_in, unsigned int freq_out)
{
struct snd_soc_codec *codec = dai->codec;
struct adau *adau = snd_soc_codec_get_drvdata(codec);
unsigned int r, n, m, i, j;
unsigned int div;
int ret;
if (freq_in < 8000000 || freq_in > 27000000)
return -EINVAL;
if (!freq_out) {
r = 0;
n = 0;
m = 0;
div = 0;
} else {
if (freq_out % freq_in != 0) {
div = DIV_ROUND_UP(freq_in, 13500000);
freq_in /= div;
r = freq_out / freq_in;
i = freq_out % freq_in;
j = gcd(i, freq_in);
n = i / j;
m = freq_in / j;
div--;
} else {
r = freq_out / freq_in;
n = 0;
m = 0;
div = 0;
}
if (n > 0xffff || m > 0xffff || div > 3 || r > 8 || r < 2)
return -EINVAL;
}
adau->pll_regs[0] = m >> 8;
adau->pll_regs[1] = m & 0xff;
adau->pll_regs[2] = n >> 8;
adau->pll_regs[3] = n & 0xff;
adau->pll_regs[4] = (r << 3) | (div << 1);
if (m != 0)
adau->pll_regs[4] |= 1; /* Fractional mode */
/* The PLL register is 6 bytes long and can only be written at once. */
ret = regmap_raw_write(adau->regmap, ADAU17X1_PLL_CONTROL,
adau->pll_regs, ARRAY_SIZE(adau->pll_regs));
if (ret)
return ret;
adau->pll_freq = freq_out;
return 0;
}
static int adau17x1_set_dai_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
struct adau *adau = snd_soc_codec_get_drvdata(dai->codec);
struct snd_soc_dapm_context *dapm = &dai->codec->dapm;
switch (clk_id) {
case ADAU17X1_CLK_SRC_MCLK:
case ADAU17X1_CLK_SRC_PLL:
break;
default:
return -EINVAL;
}
adau->sysclk = freq;
if (adau->clk_src != clk_id) {
if (clk_id == ADAU17X1_CLK_SRC_PLL) {
snd_soc_dapm_add_routes(dapm,
&adau17x1_dapm_pll_route, 1);
} else {
snd_soc_dapm_del_routes(dapm,
&adau17x1_dapm_pll_route, 1);
}
}
adau->clk_src = clk_id;
return 0;
}
static int adau17x1_set_dai_fmt(struct snd_soc_dai *dai,
unsigned int fmt)
{
struct adau *adau = snd_soc_codec_get_drvdata(dai->codec);
unsigned int ctrl0, ctrl1;
int lrclk_pol;
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBM_CFM:
ctrl0 = ADAU17X1_SERIAL_PORT0_MASTER;
adau->master = true;
break;
case SND_SOC_DAIFMT_CBS_CFS:
ctrl0 = 0;
adau->master = false;
break;
default:
return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
lrclk_pol = 0;
ctrl1 = ADAU17X1_SERIAL_PORT1_DELAY1;
break;
case SND_SOC_DAIFMT_LEFT_J:
case SND_SOC_DAIFMT_RIGHT_J:
lrclk_pol = 1;
ctrl1 = ADAU17X1_SERIAL_PORT1_DELAY0;
break;
case SND_SOC_DAIFMT_DSP_A:
lrclk_pol = 1;
ctrl0 |= ADAU17X1_SERIAL_PORT0_PULSE_MODE;
ctrl1 = ADAU17X1_SERIAL_PORT1_DELAY1;
break;
case SND_SOC_DAIFMT_DSP_B:
lrclk_pol = 1;
ctrl0 |= ADAU17X1_SERIAL_PORT0_PULSE_MODE;
ctrl1 = ADAU17X1_SERIAL_PORT1_DELAY0;
break;
default:
return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
break;
case SND_SOC_DAIFMT_IB_NF:
ctrl0 |= ADAU17X1_SERIAL_PORT0_BCLK_POL;
break;
case SND_SOC_DAIFMT_NB_IF:
lrclk_pol = !lrclk_pol;
break;
case SND_SOC_DAIFMT_IB_IF:
ctrl0 |= ADAU17X1_SERIAL_PORT0_BCLK_POL;
lrclk_pol = !lrclk_pol;
break;
default:
return -EINVAL;
}
if (lrclk_pol)
ctrl0 |= ADAU17X1_SERIAL_PORT0_LRCLK_POL;
regmap_write(adau->regmap, ADAU17X1_SERIAL_PORT0, ctrl0);
regmap_write(adau->regmap, ADAU17X1_SERIAL_PORT1, ctrl1);
adau->dai_fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
return 0;
}
static int adau17x1_set_dai_tdm_slot(struct snd_soc_dai *dai,
unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width)
{
struct adau *adau = snd_soc_codec_get_drvdata(dai->codec);
unsigned int ser_ctrl0, ser_ctrl1;
unsigned int conv_ctrl0, conv_ctrl1;
/* I2S mode */
if (slots == 0) {
slots = 2;
rx_mask = 3;
tx_mask = 3;
slot_width = 32;
}
switch (slots) {
case 2:
ser_ctrl0 = ADAU17X1_SERIAL_PORT0_STEREO;
break;
case 4:
ser_ctrl0 = ADAU17X1_SERIAL_PORT0_TDM4;
break;
case 8:
if (adau->type == ADAU1361)
return -EINVAL;
ser_ctrl0 = ADAU17X1_SERIAL_PORT0_TDM8;
break;
default:
return -EINVAL;
}
switch (slot_width * slots) {
case 32:
if (adau->type == ADAU1761)
return -EINVAL;
ser_ctrl1 = ADAU17X1_SERIAL_PORT1_BCLK32;
break;
case 64:
ser_ctrl1 = ADAU17X1_SERIAL_PORT1_BCLK64;
break;
case 48:
ser_ctrl1 = ADAU17X1_SERIAL_PORT1_BCLK48;
break;
case 128:
ser_ctrl1 = ADAU17X1_SERIAL_PORT1_BCLK128;
break;
case 256:
if (adau->type == ADAU1361)
return -EINVAL;
ser_ctrl1 = ADAU17X1_SERIAL_PORT1_BCLK256;
break;
default:
return -EINVAL;
}
switch (rx_mask) {
case 0x03:
conv_ctrl1 = ADAU17X1_CONVERTER1_ADC_PAIR(1);
adau->tdm_slot[SNDRV_PCM_STREAM_CAPTURE] = 0;
break;
case 0x0c:
conv_ctrl1 = ADAU17X1_CONVERTER1_ADC_PAIR(2);
adau->tdm_slot[SNDRV_PCM_STREAM_CAPTURE] = 1;
break;
case 0x30:
conv_ctrl1 = ADAU17X1_CONVERTER1_ADC_PAIR(3);
adau->tdm_slot[SNDRV_PCM_STREAM_CAPTURE] = 2;
break;
case 0xc0:
conv_ctrl1 = ADAU17X1_CONVERTER1_ADC_PAIR(4);
adau->tdm_slot[SNDRV_PCM_STREAM_CAPTURE] = 3;
break;
default:
return -EINVAL;
}
switch (tx_mask) {
case 0x03:
conv_ctrl0 = ADAU17X1_CONVERTER0_DAC_PAIR(1);
adau->tdm_slot[SNDRV_PCM_STREAM_PLAYBACK] = 0;
break;
case 0x0c:
conv_ctrl0 = ADAU17X1_CONVERTER0_DAC_PAIR(2);
adau->tdm_slot[SNDRV_PCM_STREAM_PLAYBACK] = 1;
break;
case 0x30:
conv_ctrl0 = ADAU17X1_CONVERTER0_DAC_PAIR(3);
adau->tdm_slot[SNDRV_PCM_STREAM_PLAYBACK] = 2;
break;
case 0xc0:
conv_ctrl0 = ADAU17X1_CONVERTER0_DAC_PAIR(4);
adau->tdm_slot[SNDRV_PCM_STREAM_PLAYBACK] = 3;
break;
default:
return -EINVAL;
}
regmap_update_bits(adau->regmap, ADAU17X1_CONVERTER0,
ADAU17X1_CONVERTER0_DAC_PAIR_MASK, conv_ctrl0);
regmap_update_bits(adau->regmap, ADAU17X1_CONVERTER1,
ADAU17X1_CONVERTER1_ADC_PAIR_MASK, conv_ctrl1);
regmap_update_bits(adau->regmap, ADAU17X1_SERIAL_PORT0,
ADAU17X1_SERIAL_PORT0_TDM_MASK, ser_ctrl0);
regmap_update_bits(adau->regmap, ADAU17X1_SERIAL_PORT1,
ADAU17X1_SERIAL_PORT1_BCLK_MASK, ser_ctrl1);
if (!adau17x1_has_dsp(adau))
return 0;
if (adau->dsp_bypass[SNDRV_PCM_STREAM_PLAYBACK]) {
regmap_write(adau->regmap, ADAU17X1_SERIAL_INPUT_ROUTE,
(adau->tdm_slot[SNDRV_PCM_STREAM_PLAYBACK] * 2) + 1);
}
if (adau->dsp_bypass[SNDRV_PCM_STREAM_CAPTURE]) {
regmap_write(adau->regmap, ADAU17X1_SERIAL_OUTPUT_ROUTE,
(adau->tdm_slot[SNDRV_PCM_STREAM_CAPTURE] * 2) + 1);
}
return 0;
}
const struct snd_soc_dai_ops adau17x1_dai_ops = {
.hw_params = adau17x1_hw_params,
.set_sysclk = adau17x1_set_dai_sysclk,
.set_fmt = adau17x1_set_dai_fmt,
.set_pll = adau17x1_set_dai_pll,
.set_tdm_slot = adau17x1_set_dai_tdm_slot,
};
EXPORT_SYMBOL_GPL(adau17x1_dai_ops);
int adau17x1_set_micbias_voltage(struct snd_soc_codec *codec,
enum adau17x1_micbias_voltage micbias)
{
struct adau *adau = snd_soc_codec_get_drvdata(codec);
switch (micbias) {
case ADAU17X1_MICBIAS_0_90_AVDD:
case ADAU17X1_MICBIAS_0_65_AVDD:
break;
default:
return -EINVAL;
}
return regmap_write(adau->regmap, ADAU17X1_MICBIAS, micbias << 2);
}
EXPORT_SYMBOL_GPL(adau17x1_set_micbias_voltage);
bool adau17x1_readable_register(struct device *dev, unsigned int reg)
{
switch (reg) {
case ADAU17X1_CLOCK_CONTROL:
case ADAU17X1_PLL_CONTROL:
case ADAU17X1_REC_POWER_MGMT:
case ADAU17X1_MICBIAS:
case ADAU17X1_SERIAL_PORT0:
case ADAU17X1_SERIAL_PORT1:
case ADAU17X1_CONVERTER0:
case ADAU17X1_CONVERTER1:
case ADAU17X1_LEFT_INPUT_DIGITAL_VOL:
case ADAU17X1_RIGHT_INPUT_DIGITAL_VOL:
case ADAU17X1_ADC_CONTROL:
case ADAU17X1_PLAY_POWER_MGMT:
case ADAU17X1_DAC_CONTROL0:
case ADAU17X1_DAC_CONTROL1:
case ADAU17X1_DAC_CONTROL2:
case ADAU17X1_SERIAL_PORT_PAD:
case ADAU17X1_CONTROL_PORT_PAD0:
case ADAU17X1_CONTROL_PORT_PAD1:
case ADAU17X1_DSP_SAMPLING_RATE:
case ADAU17X1_SERIAL_INPUT_ROUTE:
case ADAU17X1_SERIAL_OUTPUT_ROUTE:
case ADAU17X1_DSP_ENABLE:
case ADAU17X1_DSP_RUN:
case ADAU17X1_SERIAL_SAMPLING_RATE:
return true;
default:
break;
}
return false;
}
EXPORT_SYMBOL_GPL(adau17x1_readable_register);
bool adau17x1_volatile_register(struct device *dev, unsigned int reg)
{
/* SigmaDSP parameter and program memory */
if (reg < 0x4000)
return true;
switch (reg) {
/* The PLL register is 6 bytes long */
case ADAU17X1_PLL_CONTROL:
case ADAU17X1_PLL_CONTROL + 1:
case ADAU17X1_PLL_CONTROL + 2:
case ADAU17X1_PLL_CONTROL + 3:
case ADAU17X1_PLL_CONTROL + 4:
case ADAU17X1_PLL_CONTROL + 5:
return true;
default:
break;
}
return false;
}
EXPORT_SYMBOL_GPL(adau17x1_volatile_register);
int adau17x1_load_firmware(struct adau *adau, struct device *dev,
const char *firmware)
{
int ret;
int dspsr;
ret = regmap_read(adau->regmap, ADAU17X1_DSP_SAMPLING_RATE, &dspsr);
if (ret)
return ret;
regmap_write(adau->regmap, ADAU17X1_DSP_ENABLE, 1);
regmap_write(adau->regmap, ADAU17X1_DSP_SAMPLING_RATE, 0xf);
ret = process_sigma_firmware_regmap(dev, adau->regmap, firmware);
if (ret) {
regmap_write(adau->regmap, ADAU17X1_DSP_ENABLE, 0);
return ret;
}
regmap_write(adau->regmap, ADAU17X1_DSP_SAMPLING_RATE, dspsr);
return 0;
}
EXPORT_SYMBOL_GPL(adau17x1_load_firmware);
int adau17x1_add_widgets(struct snd_soc_codec *codec)
{
struct adau *adau = snd_soc_codec_get_drvdata(codec);
int ret;
ret = snd_soc_add_codec_controls(codec, adau17x1_controls,
ARRAY_SIZE(adau17x1_controls));
if (ret)
return ret;
ret = snd_soc_dapm_new_controls(&codec->dapm, adau17x1_dapm_widgets,
ARRAY_SIZE(adau17x1_dapm_widgets));
if (ret)
return ret;
if (adau17x1_has_dsp(adau)) {
ret = snd_soc_dapm_new_controls(&codec->dapm,
adau17x1_dsp_dapm_widgets,
ARRAY_SIZE(adau17x1_dsp_dapm_widgets));
}
return ret;
}
EXPORT_SYMBOL_GPL(adau17x1_add_widgets);
int adau17x1_add_routes(struct snd_soc_codec *codec)
{
struct adau *adau = snd_soc_codec_get_drvdata(codec);
int ret;
ret = snd_soc_dapm_add_routes(&codec->dapm, adau17x1_dapm_routes,
ARRAY_SIZE(adau17x1_dapm_routes));
if (ret)
return ret;
if (adau17x1_has_dsp(adau)) {
ret = snd_soc_dapm_add_routes(&codec->dapm,
adau17x1_dsp_dapm_routes,
ARRAY_SIZE(adau17x1_dsp_dapm_routes));
} else {
ret = snd_soc_dapm_add_routes(&codec->dapm,
adau17x1_no_dsp_dapm_routes,
ARRAY_SIZE(adau17x1_no_dsp_dapm_routes));
}
return ret;
}
EXPORT_SYMBOL_GPL(adau17x1_add_routes);
int adau17x1_resume(struct snd_soc_codec *codec)
{
struct adau *adau = snd_soc_codec_get_drvdata(codec);
if (adau->switch_mode)
adau->switch_mode(codec->dev);
regcache_sync(adau->regmap);
return 0;
}
EXPORT_SYMBOL_GPL(adau17x1_resume);
int adau17x1_probe(struct device *dev, struct regmap *regmap,
enum adau17x1_type type, void (*switch_mode)(struct device *dev))
{
struct adau *adau;
if (IS_ERR(regmap))
return PTR_ERR(regmap);
adau = devm_kzalloc(dev, sizeof(*adau), GFP_KERNEL);
if (!adau)
return -ENOMEM;
adau->regmap = regmap;
adau->switch_mode = switch_mode;
adau->type = type;
dev_set_drvdata(dev, adau);
if (switch_mode)
switch_mode(dev);
return 0;
}
EXPORT_SYMBOL_GPL(adau17x1_probe);
MODULE_DESCRIPTION("ASoC ADAU1X61/ADAU1X81 common code");
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
MODULE_LICENSE("GPL");

123
sound/soc/codecs/adau17x1.h Normal file
View file

@ -0,0 +1,123 @@
#ifndef __ADAU17X1_H__
#define __ADAU17X1_H__
#include <linux/regmap.h>
#include <linux/platform_data/adau17x1.h>
enum adau17x1_type {
ADAU1361,
ADAU1761,
ADAU1381,
ADAU1781,
};
enum adau17x1_pll {
ADAU17X1_PLL,
};
enum adau17x1_pll_src {
ADAU17X1_PLL_SRC_MCLK,
};
enum adau17x1_clk_src {
ADAU17X1_CLK_SRC_MCLK,
ADAU17X1_CLK_SRC_PLL,
};
struct adau {
unsigned int sysclk;
unsigned int pll_freq;
enum adau17x1_clk_src clk_src;
enum adau17x1_type type;
void (*switch_mode)(struct device *dev);
unsigned int dai_fmt;
uint8_t pll_regs[6];
bool master;
unsigned int tdm_slot[2];
bool dsp_bypass[2];
struct regmap *regmap;
};
int adau17x1_add_widgets(struct snd_soc_codec *codec);
int adau17x1_add_routes(struct snd_soc_codec *codec);
int adau17x1_probe(struct device *dev, struct regmap *regmap,
enum adau17x1_type type, void (*switch_mode)(struct device *dev));
int adau17x1_set_micbias_voltage(struct snd_soc_codec *codec,
enum adau17x1_micbias_voltage micbias);
bool adau17x1_readable_register(struct device *dev, unsigned int reg);
bool adau17x1_volatile_register(struct device *dev, unsigned int reg);
int adau17x1_resume(struct snd_soc_codec *codec);
extern const struct snd_soc_dai_ops adau17x1_dai_ops;
int adau17x1_load_firmware(struct adau *adau, struct device *dev,
const char *firmware);
bool adau17x1_has_dsp(struct adau *adau);
#define ADAU17X1_CLOCK_CONTROL 0x4000
#define ADAU17X1_PLL_CONTROL 0x4002
#define ADAU17X1_REC_POWER_MGMT 0x4009
#define ADAU17X1_MICBIAS 0x4010
#define ADAU17X1_SERIAL_PORT0 0x4015
#define ADAU17X1_SERIAL_PORT1 0x4016
#define ADAU17X1_CONVERTER0 0x4017
#define ADAU17X1_CONVERTER1 0x4018
#define ADAU17X1_LEFT_INPUT_DIGITAL_VOL 0x401a
#define ADAU17X1_RIGHT_INPUT_DIGITAL_VOL 0x401b
#define ADAU17X1_ADC_CONTROL 0x4019
#define ADAU17X1_PLAY_POWER_MGMT 0x4029
#define ADAU17X1_DAC_CONTROL0 0x402a
#define ADAU17X1_DAC_CONTROL1 0x402b
#define ADAU17X1_DAC_CONTROL2 0x402c
#define ADAU17X1_SERIAL_PORT_PAD 0x402d
#define ADAU17X1_CONTROL_PORT_PAD0 0x402f
#define ADAU17X1_CONTROL_PORT_PAD1 0x4030
#define ADAU17X1_DSP_SAMPLING_RATE 0x40eb
#define ADAU17X1_SERIAL_INPUT_ROUTE 0x40f2
#define ADAU17X1_SERIAL_OUTPUT_ROUTE 0x40f3
#define ADAU17X1_DSP_ENABLE 0x40f5
#define ADAU17X1_DSP_RUN 0x40f6
#define ADAU17X1_SERIAL_SAMPLING_RATE 0x40f8
#define ADAU17X1_SERIAL_PORT0_BCLK_POL BIT(4)
#define ADAU17X1_SERIAL_PORT0_LRCLK_POL BIT(3)
#define ADAU17X1_SERIAL_PORT0_MASTER BIT(0)
#define ADAU17X1_SERIAL_PORT1_DELAY1 0x00
#define ADAU17X1_SERIAL_PORT1_DELAY0 0x01
#define ADAU17X1_SERIAL_PORT1_DELAY8 0x02
#define ADAU17X1_SERIAL_PORT1_DELAY16 0x03
#define ADAU17X1_SERIAL_PORT1_DELAY_MASK 0x03
#define ADAU17X1_CLOCK_CONTROL_INFREQ_MASK 0x6
#define ADAU17X1_CLOCK_CONTROL_CORECLK_SRC_PLL BIT(3)
#define ADAU17X1_CLOCK_CONTROL_SYSCLK_EN BIT(0)
#define ADAU17X1_SERIAL_PORT1_BCLK32 (0x0 << 5)
#define ADAU17X1_SERIAL_PORT1_BCLK48 (0x1 << 5)
#define ADAU17X1_SERIAL_PORT1_BCLK64 (0x2 << 5)
#define ADAU17X1_SERIAL_PORT1_BCLK128 (0x3 << 5)
#define ADAU17X1_SERIAL_PORT1_BCLK256 (0x4 << 5)
#define ADAU17X1_SERIAL_PORT1_BCLK_MASK (0x7 << 5)
#define ADAU17X1_SERIAL_PORT0_STEREO (0x0 << 1)
#define ADAU17X1_SERIAL_PORT0_TDM4 (0x1 << 1)
#define ADAU17X1_SERIAL_PORT0_TDM8 (0x2 << 1)
#define ADAU17X1_SERIAL_PORT0_TDM_MASK (0x3 << 1)
#define ADAU17X1_SERIAL_PORT0_PULSE_MODE BIT(5)
#define ADAU17X1_CONVERTER0_DAC_PAIR(x) (((x) - 1) << 5)
#define ADAU17X1_CONVERTER0_DAC_PAIR_MASK (0x3 << 5)
#define ADAU17X1_CONVERTER1_ADC_PAIR(x) ((x) - 1)
#define ADAU17X1_CONVERTER1_ADC_PAIR_MASK 0x3
#define ADAU17X1_CONVERTER0_CONVSR_MASK 0x7
#endif

View file

@ -0,0 +1,59 @@
/*
* ADAU1977/ADAU1978/ADAU1979 driver
*
* Copyright 2014 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
*
* Licensed under the GPL-2.
*/
#include <linux/i2c.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <sound/soc.h>
#include "adau1977.h"
static int adau1977_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct regmap_config config;
config = adau1977_regmap_config;
config.val_bits = 8;
config.reg_bits = 8;
return adau1977_probe(&client->dev,
devm_regmap_init_i2c(client, &config),
id->driver_data, NULL);
}
static int adau1977_i2c_remove(struct i2c_client *client)
{
snd_soc_unregister_codec(&client->dev);
return 0;
}
static const struct i2c_device_id adau1977_i2c_ids[] = {
{ "adau1977", ADAU1977 },
{ "adau1978", ADAU1978 },
{ "adau1979", ADAU1978 },
{ }
};
MODULE_DEVICE_TABLE(i2c, adau1977_i2c_ids);
static struct i2c_driver adau1977_i2c_driver = {
.driver = {
.name = "adau1977",
.owner = THIS_MODULE,
},
.probe = adau1977_i2c_probe,
.remove = adau1977_i2c_remove,
.id_table = adau1977_i2c_ids,
};
module_i2c_driver(adau1977_i2c_driver);
MODULE_DESCRIPTION("ASoC ADAU1977/ADAU1978/ADAU1979 driver");
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,76 @@
/*
* ADAU1977/ADAU1978/ADAU1979 driver
*
* Copyright 2014 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
*
* Licensed under the GPL-2.
*/
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/spi/spi.h>
#include <sound/soc.h>
#include "adau1977.h"
static void adau1977_spi_switch_mode(struct device *dev)
{
struct spi_device *spi = to_spi_device(dev);
/*
* To get the device into SPI mode CLATCH has to be pulled low three
* times. Do this by issuing three dummy reads.
*/
spi_w8r8(spi, 0x00);
spi_w8r8(spi, 0x00);
spi_w8r8(spi, 0x00);
}
static int adau1977_spi_probe(struct spi_device *spi)
{
const struct spi_device_id *id = spi_get_device_id(spi);
struct regmap_config config;
if (!id)
return -EINVAL;
config = adau1977_regmap_config;
config.val_bits = 8;
config.reg_bits = 16;
config.read_flag_mask = 0x1;
return adau1977_probe(&spi->dev,
devm_regmap_init_spi(spi, &config),
id->driver_data, adau1977_spi_switch_mode);
}
static int adau1977_spi_remove(struct spi_device *spi)
{
snd_soc_unregister_codec(&spi->dev);
return 0;
}
static const struct spi_device_id adau1977_spi_ids[] = {
{ "adau1977", ADAU1977 },
{ "adau1978", ADAU1978 },
{ "adau1979", ADAU1978 },
{ }
};
MODULE_DEVICE_TABLE(spi, adau1977_spi_ids);
static struct spi_driver adau1977_spi_driver = {
.driver = {
.name = "adau1977",
.owner = THIS_MODULE,
},
.probe = adau1977_spi_probe,
.remove = adau1977_spi_remove,
.id_table = adau1977_spi_ids,
};
module_spi_driver(adau1977_spi_driver);
MODULE_DESCRIPTION("ASoC ADAU1977/ADAU1978/ADAU1979 driver");
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
MODULE_LICENSE("GPL");

1018
sound/soc/codecs/adau1977.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,37 @@
/*
* ADAU1977/ADAU1978/ADAU1979 driver
*
* Copyright 2014 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
*
* Licensed under the GPL-2.
*/
#ifndef __SOUND_SOC_CODECS_ADAU1977_H__
#define __SOUND_SOC_CODECS_ADAU1977_H__
#include <linux/regmap.h>
struct device;
enum adau1977_type {
ADAU1977,
ADAU1978,
ADAU1979,
};
int adau1977_probe(struct device *dev, struct regmap *regmap,
enum adau1977_type type, void (*switch_mode)(struct device *dev));
extern const struct regmap_config adau1977_regmap_config;
enum adau1977_clk_id {
ADAU1977_SYSCLK,
};
enum adau1977_sysclk_src {
ADAU1977_SYSCLK_SRC_MCLK,
ADAU1977_SYSCLK_SRC_LRCLK,
};
#endif

View file

@ -0,0 +1,53 @@
/*
* ADAV801 audio driver
*
* Copyright 2014 Analog Devices Inc.
*
* Licensed under the GPL-2.
*/
#include <linux/module.h>
#include <linux/spi/spi.h>
#include <linux/regmap.h>
#include <sound/soc.h>
#include "adav80x.h"
static const struct spi_device_id adav80x_spi_id[] = {
{ "adav801", 0 },
{ }
};
MODULE_DEVICE_TABLE(spi, adav80x_spi_id);
static int adav80x_spi_probe(struct spi_device *spi)
{
struct regmap_config config;
config = adav80x_regmap_config;
config.read_flag_mask = 0x01;
return adav80x_bus_probe(&spi->dev, devm_regmap_init_spi(spi, &config));
}
static int adav80x_spi_remove(struct spi_device *spi)
{
snd_soc_unregister_codec(&spi->dev);
return 0;
}
static struct spi_driver adav80x_spi_driver = {
.driver = {
.name = "adav801",
.owner = THIS_MODULE,
},
.probe = adav80x_spi_probe,
.remove = adav80x_spi_remove,
.id_table = adav80x_spi_id,
};
module_spi_driver(adav80x_spi_driver);
MODULE_DESCRIPTION("ASoC ADAV801 driver");
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
MODULE_AUTHOR("Yi Li <yi.li@analog.com>>");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,50 @@
/*
* ADAV803 audio driver
*
* Copyright 2014 Analog Devices Inc.
*
* Licensed under the GPL-2.
*/
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
#include <sound/soc.h>
#include "adav80x.h"
static const struct i2c_device_id adav803_id[] = {
{ "adav803", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, adav803_id);
static int adav803_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
return adav80x_bus_probe(&client->dev,
devm_regmap_init_i2c(client, &adav80x_regmap_config));
}
static int adav803_remove(struct i2c_client *client)
{
snd_soc_unregister_codec(&client->dev);
return 0;
}
static struct i2c_driver adav803_driver = {
.driver = {
.name = "adav803",
.owner = THIS_MODULE,
},
.probe = adav803_probe,
.remove = adav803_remove,
.id_table = adav803_id,
};
module_i2c_driver(adav803_driver);
MODULE_DESCRIPTION("ASoC ADAV803 driver");
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
MODULE_AUTHOR("Yi Li <yi.li@analog.com>>");
MODULE_LICENSE("GPL");

880
sound/soc/codecs/adav80x.c Normal file
View file

@ -0,0 +1,880 @@
/*
* ADAV80X Audio Codec driver supporting ADAV801, ADAV803
*
* Copyright 2011 Analog Devices Inc.
* Author: Yi Li <yi.li@analog.com>
* Author: Lars-Peter Clausen <lars@metafoo.de>
*
* Licensed under the GPL-2 or later.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/tlv.h>
#include "adav80x.h"
#define ADAV80X_PLAYBACK_CTRL 0x04
#define ADAV80X_AUX_IN_CTRL 0x05
#define ADAV80X_REC_CTRL 0x06
#define ADAV80X_AUX_OUT_CTRL 0x07
#define ADAV80X_DPATH_CTRL1 0x62
#define ADAV80X_DPATH_CTRL2 0x63
#define ADAV80X_DAC_CTRL1 0x64
#define ADAV80X_DAC_CTRL2 0x65
#define ADAV80X_DAC_CTRL3 0x66
#define ADAV80X_DAC_L_VOL 0x68
#define ADAV80X_DAC_R_VOL 0x69
#define ADAV80X_PGA_L_VOL 0x6c
#define ADAV80X_PGA_R_VOL 0x6d
#define ADAV80X_ADC_CTRL1 0x6e
#define ADAV80X_ADC_CTRL2 0x6f
#define ADAV80X_ADC_L_VOL 0x70
#define ADAV80X_ADC_R_VOL 0x71
#define ADAV80X_PLL_CTRL1 0x74
#define ADAV80X_PLL_CTRL2 0x75
#define ADAV80X_ICLK_CTRL1 0x76
#define ADAV80X_ICLK_CTRL2 0x77
#define ADAV80X_PLL_CLK_SRC 0x78
#define ADAV80X_PLL_OUTE 0x7a
#define ADAV80X_PLL_CLK_SRC_PLL_XIN(pll) 0x00
#define ADAV80X_PLL_CLK_SRC_PLL_MCLKI(pll) (0x40 << (pll))
#define ADAV80X_PLL_CLK_SRC_PLL_MASK(pll) (0x40 << (pll))
#define ADAV80X_ICLK_CTRL1_DAC_SRC(src) ((src) << 5)
#define ADAV80X_ICLK_CTRL1_ADC_SRC(src) ((src) << 2)
#define ADAV80X_ICLK_CTRL1_ICLK2_SRC(src) (src)
#define ADAV80X_ICLK_CTRL2_ICLK1_SRC(src) ((src) << 3)
#define ADAV80X_PLL_CTRL1_PLLDIV 0x10
#define ADAV80X_PLL_CTRL1_PLLPD(pll) (0x04 << (pll))
#define ADAV80X_PLL_CTRL1_XTLPD 0x02
#define ADAV80X_PLL_CTRL2_FIELD(pll, x) ((x) << ((pll) * 4))
#define ADAV80X_PLL_CTRL2_FS_48(pll) ADAV80X_PLL_CTRL2_FIELD((pll), 0x00)
#define ADAV80X_PLL_CTRL2_FS_32(pll) ADAV80X_PLL_CTRL2_FIELD((pll), 0x08)
#define ADAV80X_PLL_CTRL2_FS_44(pll) ADAV80X_PLL_CTRL2_FIELD((pll), 0x0c)
#define ADAV80X_PLL_CTRL2_SEL(pll) ADAV80X_PLL_CTRL2_FIELD((pll), 0x02)
#define ADAV80X_PLL_CTRL2_DOUB(pll) ADAV80X_PLL_CTRL2_FIELD((pll), 0x01)
#define ADAV80X_PLL_CTRL2_PLL_MASK(pll) ADAV80X_PLL_CTRL2_FIELD((pll), 0x0f)
#define ADAV80X_ADC_CTRL1_MODULATOR_MASK 0x80
#define ADAV80X_ADC_CTRL1_MODULATOR_128FS 0x00
#define ADAV80X_ADC_CTRL1_MODULATOR_64FS 0x80
#define ADAV80X_DAC_CTRL1_PD 0x80
#define ADAV80X_DAC_CTRL2_DIV1 0x00
#define ADAV80X_DAC_CTRL2_DIV1_5 0x10
#define ADAV80X_DAC_CTRL2_DIV2 0x20
#define ADAV80X_DAC_CTRL2_DIV3 0x30
#define ADAV80X_DAC_CTRL2_DIV_MASK 0x30
#define ADAV80X_DAC_CTRL2_INTERPOL_256FS 0x00
#define ADAV80X_DAC_CTRL2_INTERPOL_128FS 0x40
#define ADAV80X_DAC_CTRL2_INTERPOL_64FS 0x80
#define ADAV80X_DAC_CTRL2_INTERPOL_MASK 0xc0
#define ADAV80X_DAC_CTRL2_DEEMPH_NONE 0x00
#define ADAV80X_DAC_CTRL2_DEEMPH_44 0x01
#define ADAV80X_DAC_CTRL2_DEEMPH_32 0x02
#define ADAV80X_DAC_CTRL2_DEEMPH_48 0x03
#define ADAV80X_DAC_CTRL2_DEEMPH_MASK 0x01
#define ADAV80X_CAPTURE_MODE_MASTER 0x20
#define ADAV80X_CAPTURE_WORD_LEN24 0x00
#define ADAV80X_CAPTURE_WORD_LEN20 0x04
#define ADAV80X_CAPTRUE_WORD_LEN18 0x08
#define ADAV80X_CAPTURE_WORD_LEN16 0x0c
#define ADAV80X_CAPTURE_WORD_LEN_MASK 0x0c
#define ADAV80X_CAPTURE_MODE_LEFT_J 0x00
#define ADAV80X_CAPTURE_MODE_I2S 0x01
#define ADAV80X_CAPTURE_MODE_RIGHT_J 0x03
#define ADAV80X_CAPTURE_MODE_MASK 0x03
#define ADAV80X_PLAYBACK_MODE_MASTER 0x10
#define ADAV80X_PLAYBACK_MODE_LEFT_J 0x00
#define ADAV80X_PLAYBACK_MODE_I2S 0x01
#define ADAV80X_PLAYBACK_MODE_RIGHT_J_24 0x04
#define ADAV80X_PLAYBACK_MODE_RIGHT_J_20 0x05
#define ADAV80X_PLAYBACK_MODE_RIGHT_J_18 0x06
#define ADAV80X_PLAYBACK_MODE_RIGHT_J_16 0x07
#define ADAV80X_PLAYBACK_MODE_MASK 0x07
#define ADAV80X_PLL_OUTE_SYSCLKPD(x) BIT(2 - (x))
static struct reg_default adav80x_reg_defaults[] = {
{ ADAV80X_PLAYBACK_CTRL, 0x01 },
{ ADAV80X_AUX_IN_CTRL, 0x01 },
{ ADAV80X_REC_CTRL, 0x02 },
{ ADAV80X_AUX_OUT_CTRL, 0x01 },
{ ADAV80X_DPATH_CTRL1, 0xc0 },
{ ADAV80X_DPATH_CTRL2, 0x11 },
{ ADAV80X_DAC_CTRL1, 0x00 },
{ ADAV80X_DAC_CTRL2, 0x00 },
{ ADAV80X_DAC_CTRL3, 0x00 },
{ ADAV80X_DAC_L_VOL, 0xff },
{ ADAV80X_DAC_R_VOL, 0xff },
{ ADAV80X_PGA_L_VOL, 0x00 },
{ ADAV80X_PGA_R_VOL, 0x00 },
{ ADAV80X_ADC_CTRL1, 0x00 },
{ ADAV80X_ADC_CTRL2, 0x00 },
{ ADAV80X_ADC_L_VOL, 0xff },
{ ADAV80X_ADC_R_VOL, 0xff },
{ ADAV80X_PLL_CTRL1, 0x00 },
{ ADAV80X_PLL_CTRL2, 0x00 },
{ ADAV80X_ICLK_CTRL1, 0x00 },
{ ADAV80X_ICLK_CTRL2, 0x00 },
{ ADAV80X_PLL_CLK_SRC, 0x00 },
{ ADAV80X_PLL_OUTE, 0x00 },
};
struct adav80x {
struct regmap *regmap;
enum adav80x_clk_src clk_src;
unsigned int sysclk;
enum adav80x_pll_src pll_src;
unsigned int dai_fmt[2];
unsigned int rate;
bool deemph;
bool sysclk_pd[3];
};
static const char *adav80x_mux_text[] = {
"ADC",
"Playback",
"Aux Playback",
};
static const unsigned int adav80x_mux_values[] = {
0, 2, 3,
};
#define ADAV80X_MUX_ENUM_DECL(name, reg, shift) \
SOC_VALUE_ENUM_DOUBLE_DECL(name, reg, shift, 7, \
ARRAY_SIZE(adav80x_mux_text), adav80x_mux_text, \
adav80x_mux_values)
static ADAV80X_MUX_ENUM_DECL(adav80x_aux_capture_enum, ADAV80X_DPATH_CTRL1, 0);
static ADAV80X_MUX_ENUM_DECL(adav80x_capture_enum, ADAV80X_DPATH_CTRL1, 3);
static ADAV80X_MUX_ENUM_DECL(adav80x_dac_enum, ADAV80X_DPATH_CTRL2, 3);
static const struct snd_kcontrol_new adav80x_aux_capture_mux_ctrl =
SOC_DAPM_ENUM("Route", adav80x_aux_capture_enum);
static const struct snd_kcontrol_new adav80x_capture_mux_ctrl =
SOC_DAPM_ENUM("Route", adav80x_capture_enum);
static const struct snd_kcontrol_new adav80x_dac_mux_ctrl =
SOC_DAPM_ENUM("Route", adav80x_dac_enum);
#define ADAV80X_MUX(name, ctrl) \
SND_SOC_DAPM_MUX(name, SND_SOC_NOPM, 0, 0, ctrl)
static const struct snd_soc_dapm_widget adav80x_dapm_widgets[] = {
SND_SOC_DAPM_DAC("DAC", NULL, ADAV80X_DAC_CTRL1, 7, 1),
SND_SOC_DAPM_ADC("ADC", NULL, ADAV80X_ADC_CTRL1, 5, 1),
SND_SOC_DAPM_PGA("Right PGA", ADAV80X_ADC_CTRL1, 0, 1, NULL, 0),
SND_SOC_DAPM_PGA("Left PGA", ADAV80X_ADC_CTRL1, 1, 1, NULL, 0),
SND_SOC_DAPM_AIF_OUT("AIFOUT", "HiFi Capture", 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_IN("AIFIN", "HiFi Playback", 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_OUT("AIFAUXOUT", "Aux Capture", 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_IN("AIFAUXIN", "Aux Playback", 0, SND_SOC_NOPM, 0, 0),
ADAV80X_MUX("Aux Capture Select", &adav80x_aux_capture_mux_ctrl),
ADAV80X_MUX("Capture Select", &adav80x_capture_mux_ctrl),
ADAV80X_MUX("DAC Select", &adav80x_dac_mux_ctrl),
SND_SOC_DAPM_INPUT("VINR"),
SND_SOC_DAPM_INPUT("VINL"),
SND_SOC_DAPM_OUTPUT("VOUTR"),
SND_SOC_DAPM_OUTPUT("VOUTL"),
SND_SOC_DAPM_SUPPLY("SYSCLK", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("PLL1", ADAV80X_PLL_CTRL1, 2, 1, NULL, 0),
SND_SOC_DAPM_SUPPLY("PLL2", ADAV80X_PLL_CTRL1, 3, 1, NULL, 0),
SND_SOC_DAPM_SUPPLY("OSC", ADAV80X_PLL_CTRL1, 1, 1, NULL, 0),
};
static int adav80x_dapm_sysclk_check(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink)
{
struct snd_soc_codec *codec = source->codec;
struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
const char *clk;
switch (adav80x->clk_src) {
case ADAV80X_CLK_PLL1:
clk = "PLL1";
break;
case ADAV80X_CLK_PLL2:
clk = "PLL2";
break;
case ADAV80X_CLK_XTAL:
clk = "OSC";
break;
default:
return 0;
}
return strcmp(source->name, clk) == 0;
}
static int adav80x_dapm_pll_check(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink)
{
struct snd_soc_codec *codec = source->codec;
struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
return adav80x->pll_src == ADAV80X_PLL_SRC_XTAL;
}
static const struct snd_soc_dapm_route adav80x_dapm_routes[] = {
{ "DAC Select", "ADC", "ADC" },
{ "DAC Select", "Playback", "AIFIN" },
{ "DAC Select", "Aux Playback", "AIFAUXIN" },
{ "DAC", NULL, "DAC Select" },
{ "Capture Select", "ADC", "ADC" },
{ "Capture Select", "Playback", "AIFIN" },
{ "Capture Select", "Aux Playback", "AIFAUXIN" },
{ "AIFOUT", NULL, "Capture Select" },
{ "Aux Capture Select", "ADC", "ADC" },
{ "Aux Capture Select", "Playback", "AIFIN" },
{ "Aux Capture Select", "Aux Playback", "AIFAUXIN" },
{ "AIFAUXOUT", NULL, "Aux Capture Select" },
{ "VOUTR", NULL, "DAC" },
{ "VOUTL", NULL, "DAC" },
{ "Left PGA", NULL, "VINL" },
{ "Right PGA", NULL, "VINR" },
{ "ADC", NULL, "Left PGA" },
{ "ADC", NULL, "Right PGA" },
{ "SYSCLK", NULL, "PLL1", adav80x_dapm_sysclk_check },
{ "SYSCLK", NULL, "PLL2", adav80x_dapm_sysclk_check },
{ "SYSCLK", NULL, "OSC", adav80x_dapm_sysclk_check },
{ "PLL1", NULL, "OSC", adav80x_dapm_pll_check },
{ "PLL2", NULL, "OSC", adav80x_dapm_pll_check },
{ "ADC", NULL, "SYSCLK" },
{ "DAC", NULL, "SYSCLK" },
{ "AIFOUT", NULL, "SYSCLK" },
{ "AIFAUXOUT", NULL, "SYSCLK" },
{ "AIFIN", NULL, "SYSCLK" },
{ "AIFAUXIN", NULL, "SYSCLK" },
};
static int adav80x_set_deemph(struct snd_soc_codec *codec)
{
struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
unsigned int val;
if (adav80x->deemph) {
switch (adav80x->rate) {
case 32000:
val = ADAV80X_DAC_CTRL2_DEEMPH_32;
break;
case 44100:
val = ADAV80X_DAC_CTRL2_DEEMPH_44;
break;
case 48000:
case 64000:
case 88200:
case 96000:
val = ADAV80X_DAC_CTRL2_DEEMPH_48;
break;
default:
val = ADAV80X_DAC_CTRL2_DEEMPH_NONE;
break;
}
} else {
val = ADAV80X_DAC_CTRL2_DEEMPH_NONE;
}
return regmap_update_bits(adav80x->regmap, ADAV80X_DAC_CTRL2,
ADAV80X_DAC_CTRL2_DEEMPH_MASK, val);
}
static int adav80x_put_deemph(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
unsigned int deemph = ucontrol->value.integer.value[0];
if (deemph > 1)
return -EINVAL;
adav80x->deemph = deemph;
return adav80x_set_deemph(codec);
}
static int adav80x_get_deemph(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
ucontrol->value.integer.value[0] = adav80x->deemph;
return 0;
};
static const DECLARE_TLV_DB_SCALE(adav80x_inpga_tlv, 0, 50, 0);
static const DECLARE_TLV_DB_MINMAX(adav80x_digital_tlv, -9563, 0);
static const struct snd_kcontrol_new adav80x_controls[] = {
SOC_DOUBLE_R_TLV("Master Playback Volume", ADAV80X_DAC_L_VOL,
ADAV80X_DAC_R_VOL, 0, 0xff, 0, adav80x_digital_tlv),
SOC_DOUBLE_R_TLV("Master Capture Volume", ADAV80X_ADC_L_VOL,
ADAV80X_ADC_R_VOL, 0, 0xff, 0, adav80x_digital_tlv),
SOC_DOUBLE_R_TLV("PGA Capture Volume", ADAV80X_PGA_L_VOL,
ADAV80X_PGA_R_VOL, 0, 0x30, 0, adav80x_inpga_tlv),
SOC_DOUBLE("Master Playback Switch", ADAV80X_DAC_CTRL1, 0, 1, 1, 0),
SOC_DOUBLE("Master Capture Switch", ADAV80X_ADC_CTRL1, 2, 3, 1, 1),
SOC_SINGLE("ADC High Pass Filter Switch", ADAV80X_ADC_CTRL1, 6, 1, 0),
SOC_SINGLE_BOOL_EXT("Playback De-emphasis Switch", 0,
adav80x_get_deemph, adav80x_put_deemph),
};
static unsigned int adav80x_port_ctrl_regs[2][2] = {
{ ADAV80X_REC_CTRL, ADAV80X_PLAYBACK_CTRL, },
{ ADAV80X_AUX_OUT_CTRL, ADAV80X_AUX_IN_CTRL },
};
static int adav80x_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
struct snd_soc_codec *codec = dai->codec;
struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
unsigned int capture = 0x00;
unsigned int playback = 0x00;
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBM_CFM:
capture |= ADAV80X_CAPTURE_MODE_MASTER;
playback |= ADAV80X_PLAYBACK_MODE_MASTER;
case SND_SOC_DAIFMT_CBS_CFS:
break;
default:
return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
capture |= ADAV80X_CAPTURE_MODE_I2S;
playback |= ADAV80X_PLAYBACK_MODE_I2S;
break;
case SND_SOC_DAIFMT_LEFT_J:
capture |= ADAV80X_CAPTURE_MODE_LEFT_J;
playback |= ADAV80X_PLAYBACK_MODE_LEFT_J;
break;
case SND_SOC_DAIFMT_RIGHT_J:
capture |= ADAV80X_CAPTURE_MODE_RIGHT_J;
playback |= ADAV80X_PLAYBACK_MODE_RIGHT_J_24;
break;
default:
return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
break;
default:
return -EINVAL;
}
regmap_update_bits(adav80x->regmap, adav80x_port_ctrl_regs[dai->id][0],
ADAV80X_CAPTURE_MODE_MASK | ADAV80X_CAPTURE_MODE_MASTER,
capture);
regmap_write(adav80x->regmap, adav80x_port_ctrl_regs[dai->id][1],
playback);
adav80x->dai_fmt[dai->id] = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
return 0;
}
static int adav80x_set_adc_clock(struct snd_soc_codec *codec,
unsigned int sample_rate)
{
struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
unsigned int val;
if (sample_rate <= 48000)
val = ADAV80X_ADC_CTRL1_MODULATOR_128FS;
else
val = ADAV80X_ADC_CTRL1_MODULATOR_64FS;
regmap_update_bits(adav80x->regmap, ADAV80X_ADC_CTRL1,
ADAV80X_ADC_CTRL1_MODULATOR_MASK, val);
return 0;
}
static int adav80x_set_dac_clock(struct snd_soc_codec *codec,
unsigned int sample_rate)
{
struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
unsigned int val;
if (sample_rate <= 48000)
val = ADAV80X_DAC_CTRL2_DIV1 | ADAV80X_DAC_CTRL2_INTERPOL_256FS;
else
val = ADAV80X_DAC_CTRL2_DIV2 | ADAV80X_DAC_CTRL2_INTERPOL_128FS;
regmap_update_bits(adav80x->regmap, ADAV80X_DAC_CTRL2,
ADAV80X_DAC_CTRL2_DIV_MASK | ADAV80X_DAC_CTRL2_INTERPOL_MASK,
val);
return 0;
}
static int adav80x_set_capture_pcm_format(struct snd_soc_codec *codec,
struct snd_soc_dai *dai, struct snd_pcm_hw_params *params)
{
struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
unsigned int val;
switch (params_width(params)) {
case 16:
val = ADAV80X_CAPTURE_WORD_LEN16;
break;
case 18:
val = ADAV80X_CAPTRUE_WORD_LEN18;
break;
case 20:
val = ADAV80X_CAPTURE_WORD_LEN20;
break;
case 24:
val = ADAV80X_CAPTURE_WORD_LEN24;
break;
default:
return -EINVAL;
}
regmap_update_bits(adav80x->regmap, adav80x_port_ctrl_regs[dai->id][0],
ADAV80X_CAPTURE_WORD_LEN_MASK, val);
return 0;
}
static int adav80x_set_playback_pcm_format(struct snd_soc_codec *codec,
struct snd_soc_dai *dai, struct snd_pcm_hw_params *params)
{
struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
unsigned int val;
if (adav80x->dai_fmt[dai->id] != SND_SOC_DAIFMT_RIGHT_J)
return 0;
switch (params_width(params)) {
case 16:
val = ADAV80X_PLAYBACK_MODE_RIGHT_J_16;
break;
case 18:
val = ADAV80X_PLAYBACK_MODE_RIGHT_J_18;
break;
case 20:
val = ADAV80X_PLAYBACK_MODE_RIGHT_J_20;
break;
case 24:
val = ADAV80X_PLAYBACK_MODE_RIGHT_J_24;
break;
default:
return -EINVAL;
}
regmap_update_bits(adav80x->regmap, adav80x_port_ctrl_regs[dai->id][1],
ADAV80X_PLAYBACK_MODE_MASK, val);
return 0;
}
static int adav80x_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
unsigned int rate = params_rate(params);
if (rate * 256 != adav80x->sysclk)
return -EINVAL;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
adav80x_set_playback_pcm_format(codec, dai, params);
adav80x_set_dac_clock(codec, rate);
} else {
adav80x_set_capture_pcm_format(codec, dai, params);
adav80x_set_adc_clock(codec, rate);
}
adav80x->rate = rate;
adav80x_set_deemph(codec);
return 0;
}
static int adav80x_set_sysclk(struct snd_soc_codec *codec,
int clk_id, int source,
unsigned int freq, int dir)
{
struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
struct snd_soc_dapm_context *dapm = &codec->dapm;
if (dir == SND_SOC_CLOCK_IN) {
switch (clk_id) {
case ADAV80X_CLK_XIN:
case ADAV80X_CLK_XTAL:
case ADAV80X_CLK_MCLKI:
case ADAV80X_CLK_PLL1:
case ADAV80X_CLK_PLL2:
break;
default:
return -EINVAL;
}
adav80x->sysclk = freq;
if (adav80x->clk_src != clk_id) {
unsigned int iclk_ctrl1, iclk_ctrl2;
adav80x->clk_src = clk_id;
if (clk_id == ADAV80X_CLK_XTAL)
clk_id = ADAV80X_CLK_XIN;
iclk_ctrl1 = ADAV80X_ICLK_CTRL1_DAC_SRC(clk_id) |
ADAV80X_ICLK_CTRL1_ADC_SRC(clk_id) |
ADAV80X_ICLK_CTRL1_ICLK2_SRC(clk_id);
iclk_ctrl2 = ADAV80X_ICLK_CTRL2_ICLK1_SRC(clk_id);
regmap_write(adav80x->regmap, ADAV80X_ICLK_CTRL1,
iclk_ctrl1);
regmap_write(adav80x->regmap, ADAV80X_ICLK_CTRL2,
iclk_ctrl2);
snd_soc_dapm_sync(dapm);
}
} else {
unsigned int mask;
switch (clk_id) {
case ADAV80X_CLK_SYSCLK1:
case ADAV80X_CLK_SYSCLK2:
case ADAV80X_CLK_SYSCLK3:
break;
default:
return -EINVAL;
}
clk_id -= ADAV80X_CLK_SYSCLK1;
mask = ADAV80X_PLL_OUTE_SYSCLKPD(clk_id);
if (freq == 0) {
regmap_update_bits(adav80x->regmap, ADAV80X_PLL_OUTE,
mask, mask);
adav80x->sysclk_pd[clk_id] = true;
} else {
regmap_update_bits(adav80x->regmap, ADAV80X_PLL_OUTE,
mask, 0);
adav80x->sysclk_pd[clk_id] = false;
}
snd_soc_dapm_mutex_lock(dapm);
if (adav80x->sysclk_pd[0])
snd_soc_dapm_disable_pin_unlocked(dapm, "PLL1");
else
snd_soc_dapm_force_enable_pin_unlocked(dapm, "PLL1");
if (adav80x->sysclk_pd[1] || adav80x->sysclk_pd[2])
snd_soc_dapm_disable_pin_unlocked(dapm, "PLL2");
else
snd_soc_dapm_force_enable_pin_unlocked(dapm, "PLL2");
snd_soc_dapm_sync_unlocked(dapm);
snd_soc_dapm_mutex_unlock(dapm);
}
return 0;
}
static int adav80x_set_pll(struct snd_soc_codec *codec, int pll_id,
int source, unsigned int freq_in, unsigned int freq_out)
{
struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
unsigned int pll_ctrl1 = 0;
unsigned int pll_ctrl2 = 0;
unsigned int pll_src;
switch (source) {
case ADAV80X_PLL_SRC_XTAL:
case ADAV80X_PLL_SRC_XIN:
case ADAV80X_PLL_SRC_MCLKI:
break;
default:
return -EINVAL;
}
if (!freq_out)
return 0;
switch (freq_in) {
case 27000000:
break;
case 54000000:
if (source == ADAV80X_PLL_SRC_XIN) {
pll_ctrl1 |= ADAV80X_PLL_CTRL1_PLLDIV;
break;
}
default:
return -EINVAL;
}
if (freq_out > 12288000) {
pll_ctrl2 |= ADAV80X_PLL_CTRL2_DOUB(pll_id);
freq_out /= 2;
}
/* freq_out = sample_rate * 256 */
switch (freq_out) {
case 8192000:
pll_ctrl2 |= ADAV80X_PLL_CTRL2_FS_32(pll_id);
break;
case 11289600:
pll_ctrl2 |= ADAV80X_PLL_CTRL2_FS_44(pll_id);
break;
case 12288000:
pll_ctrl2 |= ADAV80X_PLL_CTRL2_FS_48(pll_id);
break;
default:
return -EINVAL;
}
regmap_update_bits(adav80x->regmap, ADAV80X_PLL_CTRL1,
ADAV80X_PLL_CTRL1_PLLDIV, pll_ctrl1);
regmap_update_bits(adav80x->regmap, ADAV80X_PLL_CTRL2,
ADAV80X_PLL_CTRL2_PLL_MASK(pll_id), pll_ctrl2);
if (source != adav80x->pll_src) {
if (source == ADAV80X_PLL_SRC_MCLKI)
pll_src = ADAV80X_PLL_CLK_SRC_PLL_MCLKI(pll_id);
else
pll_src = ADAV80X_PLL_CLK_SRC_PLL_XIN(pll_id);
regmap_update_bits(adav80x->regmap, ADAV80X_PLL_CLK_SRC,
ADAV80X_PLL_CLK_SRC_PLL_MASK(pll_id), pll_src);
adav80x->pll_src = source;
snd_soc_dapm_sync(&codec->dapm);
}
return 0;
}
static int adav80x_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
unsigned int mask = ADAV80X_DAC_CTRL1_PD;
switch (level) {
case SND_SOC_BIAS_ON:
break;
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
regmap_update_bits(adav80x->regmap, ADAV80X_DAC_CTRL1, mask,
0x00);
break;
case SND_SOC_BIAS_OFF:
regmap_update_bits(adav80x->regmap, ADAV80X_DAC_CTRL1, mask,
mask);
break;
}
codec->dapm.bias_level = level;
return 0;
}
/* Enforce the same sample rate on all audio interfaces */
static int adav80x_dai_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
if (!snd_soc_codec_is_active(codec) || !adav80x->rate)
return 0;
return snd_pcm_hw_constraint_minmax(substream->runtime,
SNDRV_PCM_HW_PARAM_RATE, adav80x->rate, adav80x->rate);
}
static void adav80x_dai_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
if (!snd_soc_codec_is_active(codec))
adav80x->rate = 0;
}
static const struct snd_soc_dai_ops adav80x_dai_ops = {
.set_fmt = adav80x_set_dai_fmt,
.hw_params = adav80x_hw_params,
.startup = adav80x_dai_startup,
.shutdown = adav80x_dai_shutdown,
};
#define ADAV80X_PLAYBACK_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_64000 | SNDRV_PCM_RATE_88200 | \
SNDRV_PCM_RATE_96000)
#define ADAV80X_CAPTURE_RATES (SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000)
#define ADAV80X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE | \
SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE)
static struct snd_soc_dai_driver adav80x_dais[] = {
{
.name = "adav80x-hifi",
.id = 0,
.playback = {
.stream_name = "HiFi Playback",
.channels_min = 2,
.channels_max = 2,
.rates = ADAV80X_PLAYBACK_RATES,
.formats = ADAV80X_FORMATS,
},
.capture = {
.stream_name = "HiFi Capture",
.channels_min = 2,
.channels_max = 2,
.rates = ADAV80X_CAPTURE_RATES,
.formats = ADAV80X_FORMATS,
},
.ops = &adav80x_dai_ops,
},
{
.name = "adav80x-aux",
.id = 1,
.playback = {
.stream_name = "Aux Playback",
.channels_min = 2,
.channels_max = 2,
.rates = ADAV80X_PLAYBACK_RATES,
.formats = ADAV80X_FORMATS,
},
.capture = {
.stream_name = "Aux Capture",
.channels_min = 2,
.channels_max = 2,
.rates = ADAV80X_CAPTURE_RATES,
.formats = ADAV80X_FORMATS,
},
.ops = &adav80x_dai_ops,
},
};
static int adav80x_probe(struct snd_soc_codec *codec)
{
struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
/* Force PLLs on for SYSCLK output */
snd_soc_dapm_force_enable_pin(&codec->dapm, "PLL1");
snd_soc_dapm_force_enable_pin(&codec->dapm, "PLL2");
/* Power down S/PDIF receiver, since it is currently not supported */
regmap_write(adav80x->regmap, ADAV80X_PLL_OUTE, 0x20);
/* Disable DAC zero flag */
regmap_write(adav80x->regmap, ADAV80X_DAC_CTRL3, 0x6);
return 0;
}
static int adav80x_resume(struct snd_soc_codec *codec)
{
struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
regcache_sync(adav80x->regmap);
return 0;
}
static struct snd_soc_codec_driver adav80x_codec_driver = {
.probe = adav80x_probe,
.resume = adav80x_resume,
.set_bias_level = adav80x_set_bias_level,
.suspend_bias_off = true,
.set_pll = adav80x_set_pll,
.set_sysclk = adav80x_set_sysclk,
.controls = adav80x_controls,
.num_controls = ARRAY_SIZE(adav80x_controls),
.dapm_widgets = adav80x_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(adav80x_dapm_widgets),
.dapm_routes = adav80x_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(adav80x_dapm_routes),
};
int adav80x_bus_probe(struct device *dev, struct regmap *regmap)
{
struct adav80x *adav80x;
if (IS_ERR(regmap))
return PTR_ERR(regmap);
adav80x = devm_kzalloc(dev, sizeof(*adav80x), GFP_KERNEL);
if (!adav80x)
return -ENOMEM;
dev_set_drvdata(dev, adav80x);
adav80x->regmap = regmap;
return snd_soc_register_codec(dev, &adav80x_codec_driver,
adav80x_dais, ARRAY_SIZE(adav80x_dais));
}
EXPORT_SYMBOL_GPL(adav80x_bus_probe);
const struct regmap_config adav80x_regmap_config = {
.val_bits = 8,
.pad_bits = 1,
.reg_bits = 7,
.read_flag_mask = 0x01,
.max_register = ADAV80X_PLL_OUTE,
.cache_type = REGCACHE_RBTREE,
.reg_defaults = adav80x_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(adav80x_reg_defaults),
};
EXPORT_SYMBOL_GPL(adav80x_regmap_config);
MODULE_DESCRIPTION("ASoC ADAV80x driver");
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
MODULE_AUTHOR("Yi Li <yi.li@analog.com>>");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,42 @@
/*
* header file for ADAV80X parts
*
* Copyright 2011 Analog Devices Inc.
*
* Licensed under the GPL-2 or later.
*/
#ifndef _ADAV80X_H
#define _ADAV80X_H
#include <linux/regmap.h>
struct device;
extern const struct regmap_config adav80x_regmap_config;
int adav80x_bus_probe(struct device *dev, struct regmap *regmap);
enum adav80x_pll_src {
ADAV80X_PLL_SRC_XIN,
ADAV80X_PLL_SRC_XTAL,
ADAV80X_PLL_SRC_MCLKI,
};
enum adav80x_pll {
ADAV80X_PLL1 = 0,
ADAV80X_PLL2 = 1,
};
enum adav80x_clk_src {
ADAV80X_CLK_XIN = 0,
ADAV80X_CLK_MCLKI = 1,
ADAV80X_CLK_PLL1 = 2,
ADAV80X_CLK_PLL2 = 3,
ADAV80X_CLK_XTAL = 6,
ADAV80X_CLK_SYSCLK1 = 6,
ADAV80X_CLK_SYSCLK2 = 7,
ADAV80X_CLK_SYSCLK3 = 8,
};
#endif

View file

@ -0,0 +1,92 @@
/*
* ads117x.c -- Driver for ads1174/8 ADC chips
*
* Copyright 2009 ShotSpotter Inc.
* Author: Graeme Gregory <gg@slimlogic.co.uk>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/module.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/initval.h>
#include <sound/soc.h>
#define ADS117X_RATES (SNDRV_PCM_RATE_8000_48000)
#define ADS117X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE)
static const struct snd_soc_dapm_widget ads117x_dapm_widgets[] = {
SND_SOC_DAPM_INPUT("Input1"),
SND_SOC_DAPM_INPUT("Input2"),
SND_SOC_DAPM_INPUT("Input3"),
SND_SOC_DAPM_INPUT("Input4"),
SND_SOC_DAPM_INPUT("Input5"),
SND_SOC_DAPM_INPUT("Input6"),
SND_SOC_DAPM_INPUT("Input7"),
SND_SOC_DAPM_INPUT("Input8"),
};
static const struct snd_soc_dapm_route ads117x_dapm_routes[] = {
{ "Capture", NULL, "Input1" },
{ "Capture", NULL, "Input2" },
{ "Capture", NULL, "Input3" },
{ "Capture", NULL, "Input4" },
{ "Capture", NULL, "Input5" },
{ "Capture", NULL, "Input6" },
{ "Capture", NULL, "Input7" },
{ "Capture", NULL, "Input8" },
};
static struct snd_soc_dai_driver ads117x_dai = {
/* ADC */
.name = "ads117x-hifi",
.capture = {
.stream_name = "Capture",
.channels_min = 1,
.channels_max = 32,
.rates = ADS117X_RATES,
.formats = ADS117X_FORMATS,},
};
static struct snd_soc_codec_driver soc_codec_dev_ads117x = {
.dapm_widgets = ads117x_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(ads117x_dapm_widgets),
.dapm_routes = ads117x_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(ads117x_dapm_routes),
};
static int ads117x_probe(struct platform_device *pdev)
{
return snd_soc_register_codec(&pdev->dev,
&soc_codec_dev_ads117x, &ads117x_dai, 1);
}
static int ads117x_remove(struct platform_device *pdev)
{
snd_soc_unregister_codec(&pdev->dev);
return 0;
}
static struct platform_driver ads117x_codec_driver = {
.driver = {
.name = "ads117x-codec",
.owner = THIS_MODULE,
},
.probe = ads117x_probe,
.remove = ads117x_remove,
};
module_platform_driver(ads117x_codec_driver);
MODULE_DESCRIPTION("ASoC ads117x driver");
MODULE_AUTHOR("Graeme Gregory");
MODULE_LICENSE("GPL");

360
sound/soc/codecs/ak4104.c Normal file
View file

@ -0,0 +1,360 @@
/*
* AK4104 ALSA SoC (ASoC) driver
*
* Copyright (c) 2009 Daniel Mack <daniel@caiaq.de>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/spi/spi.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#include <linux/regulator/consumer.h>
#include <sound/asoundef.h>
#include <sound/core.h>
#include <sound/soc.h>
#include <sound/initval.h>
/* AK4104 registers addresses */
#define AK4104_REG_CONTROL1 0x00
#define AK4104_REG_RESERVED 0x01
#define AK4104_REG_CONTROL2 0x02
#define AK4104_REG_TX 0x03
#define AK4104_REG_CHN_STATUS(x) ((x) + 0x04)
#define AK4104_NUM_REGS 10
#define AK4104_REG_MASK 0x1f
#define AK4104_READ 0xc0
#define AK4104_WRITE 0xe0
#define AK4104_RESERVED_VAL 0x5b
/* Bit masks for AK4104 registers */
#define AK4104_CONTROL1_RSTN (1 << 0)
#define AK4104_CONTROL1_PW (1 << 1)
#define AK4104_CONTROL1_DIF0 (1 << 2)
#define AK4104_CONTROL1_DIF1 (1 << 3)
#define AK4104_CONTROL2_SEL0 (1 << 0)
#define AK4104_CONTROL2_SEL1 (1 << 1)
#define AK4104_CONTROL2_MODE (1 << 2)
#define AK4104_TX_TXE (1 << 0)
#define AK4104_TX_V (1 << 1)
struct ak4104_private {
struct regmap *regmap;
struct regulator *regulator;
};
static const struct snd_soc_dapm_widget ak4104_dapm_widgets[] = {
SND_SOC_DAPM_PGA("TXE", AK4104_REG_TX, AK4104_TX_TXE, 0, NULL, 0),
SND_SOC_DAPM_OUTPUT("TX"),
};
static const struct snd_soc_dapm_route ak4104_dapm_routes[] = {
{ "TXE", NULL, "Playback" },
{ "TX", NULL, "TXE" },
};
static int ak4104_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int format)
{
struct snd_soc_codec *codec = codec_dai->codec;
struct ak4104_private *ak4104 = snd_soc_codec_get_drvdata(codec);
int val = 0;
int ret;
/* set DAI format */
switch (format & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_RIGHT_J:
break;
case SND_SOC_DAIFMT_LEFT_J:
val |= AK4104_CONTROL1_DIF0;
break;
case SND_SOC_DAIFMT_I2S:
val |= AK4104_CONTROL1_DIF0 | AK4104_CONTROL1_DIF1;
break;
default:
dev_err(codec->dev, "invalid dai format\n");
return -EINVAL;
}
/* This device can only be slave */
if ((format & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS)
return -EINVAL;
ret = regmap_update_bits(ak4104->regmap, AK4104_REG_CONTROL1,
AK4104_CONTROL1_DIF0 | AK4104_CONTROL1_DIF1,
val);
if (ret < 0)
return ret;
return 0;
}
static int ak4104_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
struct ak4104_private *ak4104 = snd_soc_codec_get_drvdata(codec);
int ret, val = 0;
/* set the IEC958 bits: consumer mode, no copyright bit */
val |= IEC958_AES0_CON_NOT_COPYRIGHT;
regmap_write(ak4104->regmap, AK4104_REG_CHN_STATUS(0), val);
val = 0;
switch (params_rate(params)) {
case 22050:
val |= IEC958_AES3_CON_FS_22050;
break;
case 24000:
val |= IEC958_AES3_CON_FS_24000;
break;
case 32000:
val |= IEC958_AES3_CON_FS_32000;
break;
case 44100:
val |= IEC958_AES3_CON_FS_44100;
break;
case 48000:
val |= IEC958_AES3_CON_FS_48000;
break;
case 88200:
val |= IEC958_AES3_CON_FS_88200;
break;
case 96000:
val |= IEC958_AES3_CON_FS_96000;
break;
case 176400:
val |= IEC958_AES3_CON_FS_176400;
break;
case 192000:
val |= IEC958_AES3_CON_FS_192000;
break;
default:
dev_err(codec->dev, "unsupported sampling rate\n");
return -EINVAL;
}
ret = regmap_write(ak4104->regmap, AK4104_REG_CHN_STATUS(3), val);
if (ret < 0)
return ret;
return 0;
}
static const struct snd_soc_dai_ops ak4101_dai_ops = {
.hw_params = ak4104_hw_params,
.set_fmt = ak4104_set_dai_fmt,
};
static struct snd_soc_dai_driver ak4104_dai = {
.name = "ak4104-hifi",
.playback = {
.stream_name = "Playback",
.channels_min = 2,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_192000,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_3LE |
SNDRV_PCM_FMTBIT_S24_LE
},
.ops = &ak4101_dai_ops,
};
static int ak4104_probe(struct snd_soc_codec *codec)
{
struct ak4104_private *ak4104 = snd_soc_codec_get_drvdata(codec);
int ret;
ret = regulator_enable(ak4104->regulator);
if (ret < 0) {
dev_err(codec->dev, "Unable to enable regulator: %d\n", ret);
return ret;
}
/* set power-up and non-reset bits */
ret = regmap_update_bits(ak4104->regmap, AK4104_REG_CONTROL1,
AK4104_CONTROL1_PW | AK4104_CONTROL1_RSTN,
AK4104_CONTROL1_PW | AK4104_CONTROL1_RSTN);
if (ret < 0)
goto exit_disable_regulator;
/* enable transmitter */
ret = regmap_update_bits(ak4104->regmap, AK4104_REG_TX,
AK4104_TX_TXE, AK4104_TX_TXE);
if (ret < 0)
goto exit_disable_regulator;
return 0;
exit_disable_regulator:
regulator_disable(ak4104->regulator);
return ret;
}
static int ak4104_remove(struct snd_soc_codec *codec)
{
struct ak4104_private *ak4104 = snd_soc_codec_get_drvdata(codec);
regmap_update_bits(ak4104->regmap, AK4104_REG_CONTROL1,
AK4104_CONTROL1_PW | AK4104_CONTROL1_RSTN, 0);
regulator_disable(ak4104->regulator);
return 0;
}
#ifdef CONFIG_PM
static int ak4104_soc_suspend(struct snd_soc_codec *codec)
{
struct ak4104_private *priv = snd_soc_codec_get_drvdata(codec);
regulator_disable(priv->regulator);
return 0;
}
static int ak4104_soc_resume(struct snd_soc_codec *codec)
{
struct ak4104_private *priv = snd_soc_codec_get_drvdata(codec);
int ret;
ret = regulator_enable(priv->regulator);
if (ret < 0)
return ret;
return 0;
}
#else
#define ak4104_soc_suspend NULL
#define ak4104_soc_resume NULL
#endif /* CONFIG_PM */
static struct snd_soc_codec_driver soc_codec_device_ak4104 = {
.probe = ak4104_probe,
.remove = ak4104_remove,
.suspend = ak4104_soc_suspend,
.resume = ak4104_soc_resume,
.dapm_widgets = ak4104_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(ak4104_dapm_widgets),
.dapm_routes = ak4104_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(ak4104_dapm_routes),
};
static const struct regmap_config ak4104_regmap = {
.reg_bits = 8,
.val_bits = 8,
.max_register = AK4104_NUM_REGS - 1,
.read_flag_mask = AK4104_READ,
.write_flag_mask = AK4104_WRITE,
.cache_type = REGCACHE_RBTREE,
};
static int ak4104_spi_probe(struct spi_device *spi)
{
struct device_node *np = spi->dev.of_node;
struct ak4104_private *ak4104;
unsigned int val;
int ret;
spi->bits_per_word = 8;
spi->mode = SPI_MODE_0;
ret = spi_setup(spi);
if (ret < 0)
return ret;
ak4104 = devm_kzalloc(&spi->dev, sizeof(struct ak4104_private),
GFP_KERNEL);
if (ak4104 == NULL)
return -ENOMEM;
ak4104->regulator = devm_regulator_get(&spi->dev, "vdd");
if (IS_ERR(ak4104->regulator)) {
ret = PTR_ERR(ak4104->regulator);
dev_err(&spi->dev, "Unable to get Vdd regulator: %d\n", ret);
return ret;
}
ak4104->regmap = devm_regmap_init_spi(spi, &ak4104_regmap);
if (IS_ERR(ak4104->regmap)) {
ret = PTR_ERR(ak4104->regmap);
return ret;
}
if (np) {
enum of_gpio_flags flags;
int gpio = of_get_named_gpio_flags(np, "reset-gpio", 0, &flags);
if (gpio_is_valid(gpio)) {
ret = devm_gpio_request_one(&spi->dev, gpio,
flags & OF_GPIO_ACTIVE_LOW ?
GPIOF_OUT_INIT_LOW : GPIOF_OUT_INIT_HIGH,
"ak4104 reset");
if (ret < 0)
return ret;
}
}
/* read the 'reserved' register - according to the datasheet, it
* should contain 0x5b. Not a good way to verify the presence of
* the device, but there is no hardware ID register. */
ret = regmap_read(ak4104->regmap, AK4104_REG_RESERVED, &val);
if (ret != 0)
return ret;
if (val != AK4104_RESERVED_VAL)
return -ENODEV;
spi_set_drvdata(spi, ak4104);
ret = snd_soc_register_codec(&spi->dev,
&soc_codec_device_ak4104, &ak4104_dai, 1);
return ret;
}
static int ak4104_spi_remove(struct spi_device *spi)
{
snd_soc_unregister_codec(&spi->dev);
return 0;
}
static const struct of_device_id ak4104_of_match[] = {
{ .compatible = "asahi-kasei,ak4104", },
{ }
};
MODULE_DEVICE_TABLE(of, ak4104_of_match);
static const struct spi_device_id ak4104_id_table[] = {
{ "ak4104", 0 },
{ }
};
MODULE_DEVICE_TABLE(spi, ak4104_id_table);
static struct spi_driver ak4104_spi_driver = {
.driver = {
.name = "ak4104",
.owner = THIS_MODULE,
.of_match_table = ak4104_of_match,
},
.id_table = ak4104_id_table,
.probe = ak4104_spi_probe,
.remove = ak4104_spi_remove,
};
module_spi_driver(ak4104_spi_driver);
MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>");
MODULE_DESCRIPTION("Asahi Kasei AK4104 ALSA SoC driver");
MODULE_LICENSE("GPL");

482
sound/soc/codecs/ak4535.c Normal file
View file

@ -0,0 +1,482 @@
/*
* ak4535.c -- AK4535 ALSA Soc Audio driver
*
* Copyright 2005 Openedhand Ltd.
*
* Author: Richard Purdie <richard@openedhand.com>
*
* Based on wm8753.c by Liam Girdwood
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/initval.h>
#include "ak4535.h"
/* codec private data */
struct ak4535_priv {
struct regmap *regmap;
unsigned int sysclk;
};
/*
* ak4535 register cache
*/
static const struct reg_default ak4535_reg_defaults[] = {
{ 0, 0x00 },
{ 1, 0x80 },
{ 2, 0x00 },
{ 3, 0x03 },
{ 4, 0x02 },
{ 5, 0x00 },
{ 6, 0x11 },
{ 7, 0x01 },
{ 8, 0x00 },
{ 9, 0x40 },
{ 10, 0x36 },
{ 11, 0x10 },
{ 12, 0x00 },
{ 13, 0x00 },
{ 14, 0x57 },
};
static bool ak4535_volatile(struct device *dev, unsigned int reg)
{
switch (reg) {
case AK4535_STATUS:
return true;
default:
return false;
}
}
static const char *ak4535_mono_gain[] = {"+6dB", "-17dB"};
static const char *ak4535_mono_out[] = {"(L + R)/2", "Hi-Z"};
static const char *ak4535_hp_out[] = {"Stereo", "Mono"};
static const char *ak4535_deemp[] = {"44.1kHz", "Off", "48kHz", "32kHz"};
static const char *ak4535_mic_select[] = {"Internal", "External"};
static const struct soc_enum ak4535_enum[] = {
SOC_ENUM_SINGLE(AK4535_SIG1, 7, 2, ak4535_mono_gain),
SOC_ENUM_SINGLE(AK4535_SIG1, 6, 2, ak4535_mono_out),
SOC_ENUM_SINGLE(AK4535_MODE2, 2, 2, ak4535_hp_out),
SOC_ENUM_SINGLE(AK4535_DAC, 0, 4, ak4535_deemp),
SOC_ENUM_SINGLE(AK4535_MIC, 1, 2, ak4535_mic_select),
};
static const struct snd_kcontrol_new ak4535_snd_controls[] = {
SOC_SINGLE("ALC2 Switch", AK4535_SIG1, 1, 1, 0),
SOC_ENUM("Mono 1 Output", ak4535_enum[1]),
SOC_ENUM("Mono 1 Gain", ak4535_enum[0]),
SOC_ENUM("Headphone Output", ak4535_enum[2]),
SOC_ENUM("Playback Deemphasis", ak4535_enum[3]),
SOC_SINGLE("Bass Volume", AK4535_DAC, 2, 3, 0),
SOC_SINGLE("Mic Boost (+20dB) Switch", AK4535_MIC, 0, 1, 0),
SOC_ENUM("Mic Select", ak4535_enum[4]),
SOC_SINGLE("ALC Operation Time", AK4535_TIMER, 0, 3, 0),
SOC_SINGLE("ALC Recovery Time", AK4535_TIMER, 2, 3, 0),
SOC_SINGLE("ALC ZC Time", AK4535_TIMER, 4, 3, 0),
SOC_SINGLE("ALC 1 Switch", AK4535_ALC1, 5, 1, 0),
SOC_SINGLE("ALC 2 Switch", AK4535_ALC1, 6, 1, 0),
SOC_SINGLE("ALC Volume", AK4535_ALC2, 0, 127, 0),
SOC_SINGLE("Capture Volume", AK4535_PGA, 0, 127, 0),
SOC_SINGLE("Left Playback Volume", AK4535_LATT, 0, 127, 1),
SOC_SINGLE("Right Playback Volume", AK4535_RATT, 0, 127, 1),
SOC_SINGLE("AUX Bypass Volume", AK4535_VOL, 0, 15, 0),
SOC_SINGLE("Mic Sidetone Volume", AK4535_VOL, 4, 7, 0),
};
/* Mono 1 Mixer */
static const struct snd_kcontrol_new ak4535_mono1_mixer_controls[] = {
SOC_DAPM_SINGLE("Mic Sidetone Switch", AK4535_SIG1, 4, 1, 0),
SOC_DAPM_SINGLE("Mono Playback Switch", AK4535_SIG1, 5, 1, 0),
};
/* Stereo Mixer */
static const struct snd_kcontrol_new ak4535_stereo_mixer_controls[] = {
SOC_DAPM_SINGLE("Mic Sidetone Switch", AK4535_SIG2, 4, 1, 0),
SOC_DAPM_SINGLE("Playback Switch", AK4535_SIG2, 7, 1, 0),
SOC_DAPM_SINGLE("Aux Bypass Switch", AK4535_SIG2, 5, 1, 0),
};
/* Input Mixer */
static const struct snd_kcontrol_new ak4535_input_mixer_controls[] = {
SOC_DAPM_SINGLE("Mic Capture Switch", AK4535_MIC, 2, 1, 0),
SOC_DAPM_SINGLE("Aux Capture Switch", AK4535_MIC, 5, 1, 0),
};
/* Input mux */
static const struct snd_kcontrol_new ak4535_input_mux_control =
SOC_DAPM_ENUM("Input Select", ak4535_enum[4]);
/* HP L switch */
static const struct snd_kcontrol_new ak4535_hpl_control =
SOC_DAPM_SINGLE("Switch", AK4535_SIG2, 1, 1, 1);
/* HP R switch */
static const struct snd_kcontrol_new ak4535_hpr_control =
SOC_DAPM_SINGLE("Switch", AK4535_SIG2, 0, 1, 1);
/* mono 2 switch */
static const struct snd_kcontrol_new ak4535_mono2_control =
SOC_DAPM_SINGLE("Switch", AK4535_SIG1, 0, 1, 0);
/* Line out switch */
static const struct snd_kcontrol_new ak4535_line_control =
SOC_DAPM_SINGLE("Switch", AK4535_SIG2, 6, 1, 0);
/* ak4535 dapm widgets */
static const struct snd_soc_dapm_widget ak4535_dapm_widgets[] = {
SND_SOC_DAPM_MIXER("Stereo Mixer", SND_SOC_NOPM, 0, 0,
&ak4535_stereo_mixer_controls[0],
ARRAY_SIZE(ak4535_stereo_mixer_controls)),
SND_SOC_DAPM_MIXER("Mono1 Mixer", SND_SOC_NOPM, 0, 0,
&ak4535_mono1_mixer_controls[0],
ARRAY_SIZE(ak4535_mono1_mixer_controls)),
SND_SOC_DAPM_MIXER("Input Mixer", SND_SOC_NOPM, 0, 0,
&ak4535_input_mixer_controls[0],
ARRAY_SIZE(ak4535_input_mixer_controls)),
SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0,
&ak4535_input_mux_control),
SND_SOC_DAPM_DAC("DAC", "Playback", AK4535_PM2, 0, 0),
SND_SOC_DAPM_SWITCH("Mono 2 Enable", SND_SOC_NOPM, 0, 0,
&ak4535_mono2_control),
/* speaker powersave bit */
SND_SOC_DAPM_PGA("Speaker Enable", AK4535_MODE2, 0, 0, NULL, 0),
SND_SOC_DAPM_SWITCH("Line Out Enable", SND_SOC_NOPM, 0, 0,
&ak4535_line_control),
SND_SOC_DAPM_SWITCH("Left HP Enable", SND_SOC_NOPM, 0, 0,
&ak4535_hpl_control),
SND_SOC_DAPM_SWITCH("Right HP Enable", SND_SOC_NOPM, 0, 0,
&ak4535_hpr_control),
SND_SOC_DAPM_OUTPUT("LOUT"),
SND_SOC_DAPM_OUTPUT("HPL"),
SND_SOC_DAPM_OUTPUT("ROUT"),
SND_SOC_DAPM_OUTPUT("HPR"),
SND_SOC_DAPM_OUTPUT("SPP"),
SND_SOC_DAPM_OUTPUT("SPN"),
SND_SOC_DAPM_OUTPUT("MOUT1"),
SND_SOC_DAPM_OUTPUT("MOUT2"),
SND_SOC_DAPM_OUTPUT("MICOUT"),
SND_SOC_DAPM_ADC("ADC", "Capture", AK4535_PM1, 0, 0),
SND_SOC_DAPM_PGA("Spk Amp", AK4535_PM2, 3, 0, NULL, 0),
SND_SOC_DAPM_PGA("HP R Amp", AK4535_PM2, 1, 0, NULL, 0),
SND_SOC_DAPM_PGA("HP L Amp", AK4535_PM2, 2, 0, NULL, 0),
SND_SOC_DAPM_PGA("Mic", AK4535_PM1, 1, 0, NULL, 0),
SND_SOC_DAPM_PGA("Line Out", AK4535_PM1, 4, 0, NULL, 0),
SND_SOC_DAPM_PGA("Mono Out", AK4535_PM1, 3, 0, NULL, 0),
SND_SOC_DAPM_PGA("AUX In", AK4535_PM1, 2, 0, NULL, 0),
SND_SOC_DAPM_MICBIAS("Mic Int Bias", AK4535_MIC, 3, 0),
SND_SOC_DAPM_MICBIAS("Mic Ext Bias", AK4535_MIC, 4, 0),
SND_SOC_DAPM_INPUT("MICIN"),
SND_SOC_DAPM_INPUT("MICEXT"),
SND_SOC_DAPM_INPUT("AUX"),
SND_SOC_DAPM_INPUT("MIN"),
SND_SOC_DAPM_INPUT("AIN"),
};
static const struct snd_soc_dapm_route ak4535_audio_map[] = {
/*stereo mixer */
{"Stereo Mixer", "Playback Switch", "DAC"},
{"Stereo Mixer", "Mic Sidetone Switch", "Mic"},
{"Stereo Mixer", "Aux Bypass Switch", "AUX In"},
/* mono1 mixer */
{"Mono1 Mixer", "Mic Sidetone Switch", "Mic"},
{"Mono1 Mixer", "Mono Playback Switch", "DAC"},
/* Mic */
{"Mic", NULL, "AIN"},
{"Input Mux", "Internal", "Mic Int Bias"},
{"Input Mux", "External", "Mic Ext Bias"},
{"Mic Int Bias", NULL, "MICIN"},
{"Mic Ext Bias", NULL, "MICEXT"},
{"MICOUT", NULL, "Input Mux"},
/* line out */
{"LOUT", NULL, "Line Out Enable"},
{"ROUT", NULL, "Line Out Enable"},
{"Line Out Enable", "Switch", "Line Out"},
{"Line Out", NULL, "Stereo Mixer"},
/* mono1 out */
{"MOUT1", NULL, "Mono Out"},
{"Mono Out", NULL, "Mono1 Mixer"},
/* left HP */
{"HPL", NULL, "Left HP Enable"},
{"Left HP Enable", "Switch", "HP L Amp"},
{"HP L Amp", NULL, "Stereo Mixer"},
/* right HP */
{"HPR", NULL, "Right HP Enable"},
{"Right HP Enable", "Switch", "HP R Amp"},
{"HP R Amp", NULL, "Stereo Mixer"},
/* speaker */
{"SPP", NULL, "Speaker Enable"},
{"SPN", NULL, "Speaker Enable"},
{"Speaker Enable", "Switch", "Spk Amp"},
{"Spk Amp", NULL, "MIN"},
/* mono 2 */
{"MOUT2", NULL, "Mono 2 Enable"},
{"Mono 2 Enable", "Switch", "Stereo Mixer"},
/* Aux In */
{"Aux In", NULL, "AUX"},
/* ADC */
{"ADC", NULL, "Input Mixer"},
{"Input Mixer", "Mic Capture Switch", "Mic"},
{"Input Mixer", "Aux Capture Switch", "Aux In"},
};
static int ak4535_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = codec_dai->codec;
struct ak4535_priv *ak4535 = snd_soc_codec_get_drvdata(codec);
ak4535->sysclk = freq;
return 0;
}
static int ak4535_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
struct ak4535_priv *ak4535 = snd_soc_codec_get_drvdata(codec);
u8 mode2 = snd_soc_read(codec, AK4535_MODE2) & ~(0x3 << 5);
int rate = params_rate(params), fs = 256;
if (rate)
fs = ak4535->sysclk / rate;
/* set fs */
switch (fs) {
case 1024:
mode2 |= (0x2 << 5);
break;
case 512:
mode2 |= (0x1 << 5);
break;
case 256:
break;
}
/* set rate */
snd_soc_write(codec, AK4535_MODE2, mode2);
return 0;
}
static int ak4535_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
u8 mode1 = 0;
/* interface format */
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
mode1 = 0x0002;
break;
case SND_SOC_DAIFMT_LEFT_J:
mode1 = 0x0001;
break;
default:
return -EINVAL;
}
/* use 32 fs for BCLK to save power */
mode1 |= 0x4;
snd_soc_write(codec, AK4535_MODE1, mode1);
return 0;
}
static int ak4535_mute(struct snd_soc_dai *dai, int mute)
{
struct snd_soc_codec *codec = dai->codec;
u16 mute_reg = snd_soc_read(codec, AK4535_DAC);
if (!mute)
snd_soc_write(codec, AK4535_DAC, mute_reg & ~0x20);
else
snd_soc_write(codec, AK4535_DAC, mute_reg | 0x20);
return 0;
}
static int ak4535_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
switch (level) {
case SND_SOC_BIAS_ON:
snd_soc_update_bits(codec, AK4535_DAC, 0x20, 0);
break;
case SND_SOC_BIAS_PREPARE:
snd_soc_update_bits(codec, AK4535_DAC, 0x20, 0x20);
break;
case SND_SOC_BIAS_STANDBY:
snd_soc_update_bits(codec, AK4535_PM1, 0x80, 0x80);
snd_soc_update_bits(codec, AK4535_PM2, 0x80, 0);
break;
case SND_SOC_BIAS_OFF:
snd_soc_update_bits(codec, AK4535_PM1, 0x80, 0);
break;
}
codec->dapm.bias_level = level;
return 0;
}
#define AK4535_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\
SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
static const struct snd_soc_dai_ops ak4535_dai_ops = {
.hw_params = ak4535_hw_params,
.set_fmt = ak4535_set_dai_fmt,
.digital_mute = ak4535_mute,
.set_sysclk = ak4535_set_dai_sysclk,
};
static struct snd_soc_dai_driver ak4535_dai = {
.name = "ak4535-hifi",
.playback = {
.stream_name = "Playback",
.channels_min = 1,
.channels_max = 2,
.rates = AK4535_RATES,
.formats = SNDRV_PCM_FMTBIT_S16_LE,},
.capture = {
.stream_name = "Capture",
.channels_min = 1,
.channels_max = 2,
.rates = AK4535_RATES,
.formats = SNDRV_PCM_FMTBIT_S16_LE,},
.ops = &ak4535_dai_ops,
};
static int ak4535_suspend(struct snd_soc_codec *codec)
{
ak4535_set_bias_level(codec, SND_SOC_BIAS_OFF);
return 0;
}
static int ak4535_resume(struct snd_soc_codec *codec)
{
snd_soc_cache_sync(codec);
ak4535_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
return 0;
}
static int ak4535_probe(struct snd_soc_codec *codec)
{
/* power on device */
ak4535_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
snd_soc_add_codec_controls(codec, ak4535_snd_controls,
ARRAY_SIZE(ak4535_snd_controls));
return 0;
}
/* power down chip */
static int ak4535_remove(struct snd_soc_codec *codec)
{
ak4535_set_bias_level(codec, SND_SOC_BIAS_OFF);
return 0;
}
static const struct regmap_config ak4535_regmap = {
.reg_bits = 8,
.val_bits = 8,
.max_register = AK4535_STATUS,
.volatile_reg = ak4535_volatile,
.cache_type = REGCACHE_RBTREE,
.reg_defaults = ak4535_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(ak4535_reg_defaults),
};
static struct snd_soc_codec_driver soc_codec_dev_ak4535 = {
.probe = ak4535_probe,
.remove = ak4535_remove,
.suspend = ak4535_suspend,
.resume = ak4535_resume,
.set_bias_level = ak4535_set_bias_level,
.dapm_widgets = ak4535_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(ak4535_dapm_widgets),
.dapm_routes = ak4535_audio_map,
.num_dapm_routes = ARRAY_SIZE(ak4535_audio_map),
};
static int ak4535_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct ak4535_priv *ak4535;
int ret;
ak4535 = devm_kzalloc(&i2c->dev, sizeof(struct ak4535_priv),
GFP_KERNEL);
if (ak4535 == NULL)
return -ENOMEM;
ak4535->regmap = devm_regmap_init_i2c(i2c, &ak4535_regmap);
if (IS_ERR(ak4535->regmap)) {
ret = PTR_ERR(ak4535->regmap);
dev_err(&i2c->dev, "Failed to init regmap: %d\n", ret);
return ret;
}
i2c_set_clientdata(i2c, ak4535);
ret = snd_soc_register_codec(&i2c->dev,
&soc_codec_dev_ak4535, &ak4535_dai, 1);
return ret;
}
static int ak4535_i2c_remove(struct i2c_client *client)
{
snd_soc_unregister_codec(&client->dev);
return 0;
}
static const struct i2c_device_id ak4535_i2c_id[] = {
{ "ak4535", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, ak4535_i2c_id);
static struct i2c_driver ak4535_i2c_driver = {
.driver = {
.name = "ak4535",
.owner = THIS_MODULE,
},
.probe = ak4535_i2c_probe,
.remove = ak4535_i2c_remove,
.id_table = ak4535_i2c_id,
};
module_i2c_driver(ak4535_i2c_driver);
MODULE_DESCRIPTION("Soc AK4535 driver");
MODULE_AUTHOR("Richard Purdie");
MODULE_LICENSE("GPL");

37
sound/soc/codecs/ak4535.h Normal file
View file

@ -0,0 +1,37 @@
/*
* ak4535.h -- AK4535 Soc Audio driver
*
* Copyright 2005 Openedhand Ltd.
*
* Author: Richard Purdie <richard@openedhand.com>
*
* Based on wm8753.h
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef _AK4535_H
#define _AK4535_H
/* AK4535 register space */
#define AK4535_PM1 0x0
#define AK4535_PM2 0x1
#define AK4535_SIG1 0x2
#define AK4535_SIG2 0x3
#define AK4535_MODE1 0x4
#define AK4535_MODE2 0x5
#define AK4535_DAC 0x6
#define AK4535_MIC 0x7
#define AK4535_TIMER 0x8
#define AK4535_ALC1 0x9
#define AK4535_ALC2 0xa
#define AK4535_PGA 0xb
#define AK4535_LATT 0xc
#define AK4535_RATT 0xd
#define AK4535_VOL 0xe
#define AK4535_STATUS 0xf
#endif

106
sound/soc/codecs/ak4554.c Normal file
View file

@ -0,0 +1,106 @@
/*
* ak4554.c
*
* Copyright (C) 2013 Renesas Solutions Corp.
* Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <sound/soc.h>
/*
* ak4554 is very simple DA/AD converter which has no setting register.
*
* CAUTION
*
* ak4554 playback format is SND_SOC_DAIFMT_RIGHT_J,
* and, capture format is SND_SOC_DAIFMT_LEFT_J
* on same bit clock, LR clock.
* But, this driver doesn't have snd_soc_dai_ops :: set_fmt
*
* CPU/Codec DAI image
*
* CPU-DAI1 (plaback only fmt = RIGHT_J) --+-- ak4554
* |
* CPU-DAI2 (capture only fmt = LEFT_J) ---+
*/
static const struct snd_soc_dapm_widget ak4554_dapm_widgets[] = {
SND_SOC_DAPM_INPUT("AINL"),
SND_SOC_DAPM_INPUT("AINR"),
SND_SOC_DAPM_OUTPUT("AOUTL"),
SND_SOC_DAPM_OUTPUT("AOUTR"),
};
static const struct snd_soc_dapm_route ak4554_dapm_routes[] = {
{ "Capture", NULL, "AINL" },
{ "Capture", NULL, "AINR" },
{ "AOUTL", NULL, "Playback" },
{ "AOUTR", NULL, "Playback" },
};
static struct snd_soc_dai_driver ak4554_dai = {
.name = "ak4554-hifi",
.playback = {
.stream_name = "Playback",
.channels_min = 2,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
.capture = {
.stream_name = "Capture",
.channels_min = 2,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
.symmetric_rates = 1,
};
static struct snd_soc_codec_driver soc_codec_dev_ak4554 = {
.dapm_widgets = ak4554_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(ak4554_dapm_widgets),
.dapm_routes = ak4554_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(ak4554_dapm_routes),
};
static int ak4554_soc_probe(struct platform_device *pdev)
{
return snd_soc_register_codec(&pdev->dev,
&soc_codec_dev_ak4554,
&ak4554_dai, 1);
}
static int ak4554_soc_remove(struct platform_device *pdev)
{
snd_soc_unregister_codec(&pdev->dev);
return 0;
}
static struct of_device_id ak4554_of_match[] = {
{ .compatible = "asahi-kasei,ak4554" },
{},
};
MODULE_DEVICE_TABLE(of, ak4554_of_match);
static struct platform_driver ak4554_driver = {
.driver = {
.name = "ak4554-adc-dac",
.owner = THIS_MODULE,
.of_match_table = ak4554_of_match,
},
.probe = ak4554_soc_probe,
.remove = ak4554_soc_remove,
};
module_platform_driver(ak4554_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("SoC AK4554 driver");
MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");

655
sound/soc/codecs/ak4641.c Normal file
View file

@ -0,0 +1,655 @@
/*
* ak4641.c -- AK4641 ALSA Soc Audio driver
*
* Copyright (C) 2008 Harald Welte <laforge@gnufiish.org>
* Copyright (C) 2011 Dmitry Artamonow <mad_soft@inbox.ru>
*
* Based on ak4535.c by Richard Purdie
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/initval.h>
#include <sound/tlv.h>
#include <sound/ak4641.h>
#include "ak4641.h"
/* codec private data */
struct ak4641_priv {
struct regmap *regmap;
unsigned int sysclk;
int deemph;
int playback_fs;
};
/*
* ak4641 register cache
*/
static const struct reg_default ak4641_reg_defaults[] = {
{ 0, 0x00 }, { 1, 0x80 }, { 2, 0x00 }, { 3, 0x80 },
{ 4, 0x02 }, { 5, 0x00 }, { 6, 0x11 }, { 7, 0x05 },
{ 8, 0x00 }, { 9, 0x00 }, { 10, 0x36 }, { 11, 0x10 },
{ 12, 0x00 }, { 13, 0x00 }, { 14, 0x57 }, { 15, 0x00 },
{ 16, 0x88 }, { 17, 0x88 }, { 18, 0x08 }, { 19, 0x08 }
};
static const int deemph_settings[] = {44100, 0, 48000, 32000};
static int ak4641_set_deemph(struct snd_soc_codec *codec)
{
struct ak4641_priv *ak4641 = snd_soc_codec_get_drvdata(codec);
int i, best = 0;
for (i = 0 ; i < ARRAY_SIZE(deemph_settings); i++) {
/* if deemphasis is on, select the nearest available rate */
if (ak4641->deemph && deemph_settings[i] != 0 &&
abs(deemph_settings[i] - ak4641->playback_fs) <
abs(deemph_settings[best] - ak4641->playback_fs))
best = i;
if (!ak4641->deemph && deemph_settings[i] == 0)
best = i;
}
dev_dbg(codec->dev, "Set deemphasis %d\n", best);
return snd_soc_update_bits(codec, AK4641_DAC, 0x3, best);
}
static int ak4641_put_deemph(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct ak4641_priv *ak4641 = snd_soc_codec_get_drvdata(codec);
int deemph = ucontrol->value.integer.value[0];
if (deemph > 1)
return -EINVAL;
ak4641->deemph = deemph;
return ak4641_set_deemph(codec);
}
static int ak4641_get_deemph(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct ak4641_priv *ak4641 = snd_soc_codec_get_drvdata(codec);
ucontrol->value.integer.value[0] = ak4641->deemph;
return 0;
};
static const char *ak4641_mono_out[] = {"(L + R)/2", "Hi-Z"};
static const char *ak4641_hp_out[] = {"Stereo", "Mono"};
static const char *ak4641_mic_select[] = {"Internal", "External"};
static const char *ak4641_mic_or_dac[] = {"Microphone", "Voice DAC"};
static const DECLARE_TLV_DB_SCALE(mono_gain_tlv, -1700, 2300, 0);
static const DECLARE_TLV_DB_SCALE(mic_boost_tlv, 0, 2000, 0);
static const DECLARE_TLV_DB_SCALE(eq_tlv, -1050, 150, 0);
static const DECLARE_TLV_DB_SCALE(master_tlv, -12750, 50, 0);
static const DECLARE_TLV_DB_SCALE(mic_stereo_sidetone_tlv, -2700, 300, 0);
static const DECLARE_TLV_DB_SCALE(mic_mono_sidetone_tlv, -400, 400, 0);
static const DECLARE_TLV_DB_SCALE(capture_tlv, -800, 50, 0);
static const DECLARE_TLV_DB_SCALE(alc_tlv, -800, 50, 0);
static const DECLARE_TLV_DB_SCALE(aux_in_tlv, -2100, 300, 0);
static SOC_ENUM_SINGLE_DECL(ak4641_mono_out_enum,
AK4641_SIG1, 6, ak4641_mono_out);
static SOC_ENUM_SINGLE_DECL(ak4641_hp_out_enum,
AK4641_MODE2, 2, ak4641_hp_out);
static SOC_ENUM_SINGLE_DECL(ak4641_mic_select_enum,
AK4641_MIC, 1, ak4641_mic_select);
static SOC_ENUM_SINGLE_DECL(ak4641_mic_or_dac_enum,
AK4641_BTIF, 4, ak4641_mic_or_dac);
static const struct snd_kcontrol_new ak4641_snd_controls[] = {
SOC_ENUM("Mono 1 Output", ak4641_mono_out_enum),
SOC_SINGLE_TLV("Mono 1 Gain Volume", AK4641_SIG1, 7, 1, 1,
mono_gain_tlv),
SOC_ENUM("Headphone Output", ak4641_hp_out_enum),
SOC_SINGLE_BOOL_EXT("Playback Deemphasis Switch", 0,
ak4641_get_deemph, ak4641_put_deemph),
SOC_SINGLE_TLV("Mic Boost Volume", AK4641_MIC, 0, 1, 0, mic_boost_tlv),
SOC_SINGLE("ALC Operation Time", AK4641_TIMER, 0, 3, 0),
SOC_SINGLE("ALC Recovery Time", AK4641_TIMER, 2, 3, 0),
SOC_SINGLE("ALC ZC Time", AK4641_TIMER, 4, 3, 0),
SOC_SINGLE("ALC 1 Switch", AK4641_ALC1, 5, 1, 0),
SOC_SINGLE_TLV("ALC Volume", AK4641_ALC2, 0, 71, 0, alc_tlv),
SOC_SINGLE("Left Out Enable Switch", AK4641_SIG2, 1, 1, 0),
SOC_SINGLE("Right Out Enable Switch", AK4641_SIG2, 0, 1, 0),
SOC_SINGLE_TLV("Capture Volume", AK4641_PGA, 0, 71, 0, capture_tlv),
SOC_DOUBLE_R_TLV("Master Playback Volume", AK4641_LATT,
AK4641_RATT, 0, 255, 1, master_tlv),
SOC_SINGLE_TLV("AUX In Volume", AK4641_VOL, 0, 15, 0, aux_in_tlv),
SOC_SINGLE("Equalizer Switch", AK4641_DAC, 2, 1, 0),
SOC_SINGLE_TLV("EQ1 100 Hz Volume", AK4641_EQLO, 0, 15, 1, eq_tlv),
SOC_SINGLE_TLV("EQ2 250 Hz Volume", AK4641_EQLO, 4, 15, 1, eq_tlv),
SOC_SINGLE_TLV("EQ3 1 kHz Volume", AK4641_EQMID, 0, 15, 1, eq_tlv),
SOC_SINGLE_TLV("EQ4 3.5 kHz Volume", AK4641_EQMID, 4, 15, 1, eq_tlv),
SOC_SINGLE_TLV("EQ5 10 kHz Volume", AK4641_EQHI, 0, 15, 1, eq_tlv),
};
/* Mono 1 Mixer */
static const struct snd_kcontrol_new ak4641_mono1_mixer_controls[] = {
SOC_DAPM_SINGLE_TLV("Mic Mono Sidetone Volume", AK4641_VOL, 7, 1, 0,
mic_mono_sidetone_tlv),
SOC_DAPM_SINGLE("Mic Mono Sidetone Switch", AK4641_SIG1, 4, 1, 0),
SOC_DAPM_SINGLE("Mono Playback Switch", AK4641_SIG1, 5, 1, 0),
};
/* Stereo Mixer */
static const struct snd_kcontrol_new ak4641_stereo_mixer_controls[] = {
SOC_DAPM_SINGLE_TLV("Mic Sidetone Volume", AK4641_VOL, 4, 7, 0,
mic_stereo_sidetone_tlv),
SOC_DAPM_SINGLE("Mic Sidetone Switch", AK4641_SIG2, 4, 1, 0),
SOC_DAPM_SINGLE("Playback Switch", AK4641_SIG2, 7, 1, 0),
SOC_DAPM_SINGLE("Aux Bypass Switch", AK4641_SIG2, 5, 1, 0),
};
/* Input Mixer */
static const struct snd_kcontrol_new ak4641_input_mixer_controls[] = {
SOC_DAPM_SINGLE("Mic Capture Switch", AK4641_MIC, 2, 1, 0),
SOC_DAPM_SINGLE("Aux Capture Switch", AK4641_MIC, 5, 1, 0),
};
/* Mic mux */
static const struct snd_kcontrol_new ak4641_mic_mux_control =
SOC_DAPM_ENUM("Mic Select", ak4641_mic_select_enum);
/* Input mux */
static const struct snd_kcontrol_new ak4641_input_mux_control =
SOC_DAPM_ENUM("Input Select", ak4641_mic_or_dac_enum);
/* mono 2 switch */
static const struct snd_kcontrol_new ak4641_mono2_control =
SOC_DAPM_SINGLE("Switch", AK4641_SIG1, 0, 1, 0);
/* ak4641 dapm widgets */
static const struct snd_soc_dapm_widget ak4641_dapm_widgets[] = {
SND_SOC_DAPM_MIXER("Stereo Mixer", SND_SOC_NOPM, 0, 0,
&ak4641_stereo_mixer_controls[0],
ARRAY_SIZE(ak4641_stereo_mixer_controls)),
SND_SOC_DAPM_MIXER("Mono1 Mixer", SND_SOC_NOPM, 0, 0,
&ak4641_mono1_mixer_controls[0],
ARRAY_SIZE(ak4641_mono1_mixer_controls)),
SND_SOC_DAPM_MIXER("Input Mixer", SND_SOC_NOPM, 0, 0,
&ak4641_input_mixer_controls[0],
ARRAY_SIZE(ak4641_input_mixer_controls)),
SND_SOC_DAPM_MUX("Mic Mux", SND_SOC_NOPM, 0, 0,
&ak4641_mic_mux_control),
SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0,
&ak4641_input_mux_control),
SND_SOC_DAPM_SWITCH("Mono 2 Enable", SND_SOC_NOPM, 0, 0,
&ak4641_mono2_control),
SND_SOC_DAPM_OUTPUT("LOUT"),
SND_SOC_DAPM_OUTPUT("ROUT"),
SND_SOC_DAPM_OUTPUT("MOUT1"),
SND_SOC_DAPM_OUTPUT("MOUT2"),
SND_SOC_DAPM_OUTPUT("MICOUT"),
SND_SOC_DAPM_ADC("ADC", "HiFi Capture", AK4641_PM1, 0, 0),
SND_SOC_DAPM_PGA("Mic", AK4641_PM1, 1, 0, NULL, 0),
SND_SOC_DAPM_PGA("AUX In", AK4641_PM1, 2, 0, NULL, 0),
SND_SOC_DAPM_PGA("Mono Out", AK4641_PM1, 3, 0, NULL, 0),
SND_SOC_DAPM_PGA("Line Out", AK4641_PM1, 4, 0, NULL, 0),
SND_SOC_DAPM_DAC("DAC", "HiFi Playback", AK4641_PM2, 0, 0),
SND_SOC_DAPM_PGA("Mono Out 2", AK4641_PM2, 3, 0, NULL, 0),
SND_SOC_DAPM_ADC("Voice ADC", "Voice Capture", AK4641_BTIF, 0, 0),
SND_SOC_DAPM_DAC("Voice DAC", "Voice Playback", AK4641_BTIF, 1, 0),
SND_SOC_DAPM_MICBIAS("Mic Int Bias", AK4641_MIC, 3, 0),
SND_SOC_DAPM_MICBIAS("Mic Ext Bias", AK4641_MIC, 4, 0),
SND_SOC_DAPM_INPUT("MICIN"),
SND_SOC_DAPM_INPUT("MICEXT"),
SND_SOC_DAPM_INPUT("AUX"),
SND_SOC_DAPM_INPUT("AIN"),
};
static const struct snd_soc_dapm_route ak4641_audio_map[] = {
/* Stereo Mixer */
{"Stereo Mixer", "Playback Switch", "DAC"},
{"Stereo Mixer", "Mic Sidetone Switch", "Input Mux"},
{"Stereo Mixer", "Aux Bypass Switch", "AUX In"},
/* Mono 1 Mixer */
{"Mono1 Mixer", "Mic Mono Sidetone Switch", "Input Mux"},
{"Mono1 Mixer", "Mono Playback Switch", "DAC"},
/* Mic */
{"Mic", NULL, "AIN"},
{"Mic Mux", "Internal", "Mic Int Bias"},
{"Mic Mux", "External", "Mic Ext Bias"},
{"Mic Int Bias", NULL, "MICIN"},
{"Mic Ext Bias", NULL, "MICEXT"},
{"MICOUT", NULL, "Mic Mux"},
/* Input Mux */
{"Input Mux", "Microphone", "Mic"},
{"Input Mux", "Voice DAC", "Voice DAC"},
/* Line Out */
{"LOUT", NULL, "Line Out"},
{"ROUT", NULL, "Line Out"},
{"Line Out", NULL, "Stereo Mixer"},
/* Mono 1 Out */
{"MOUT1", NULL, "Mono Out"},
{"Mono Out", NULL, "Mono1 Mixer"},
/* Mono 2 Out */
{"MOUT2", NULL, "Mono 2 Enable"},
{"Mono 2 Enable", "Switch", "Mono Out 2"},
{"Mono Out 2", NULL, "Stereo Mixer"},
{"Voice ADC", NULL, "Mono 2 Enable"},
/* Aux In */
{"AUX In", NULL, "AUX"},
/* ADC */
{"ADC", NULL, "Input Mixer"},
{"Input Mixer", "Mic Capture Switch", "Mic"},
{"Input Mixer", "Aux Capture Switch", "AUX In"},
};
static int ak4641_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = codec_dai->codec;
struct ak4641_priv *ak4641 = snd_soc_codec_get_drvdata(codec);
ak4641->sysclk = freq;
return 0;
}
static int ak4641_i2s_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
struct ak4641_priv *ak4641 = snd_soc_codec_get_drvdata(codec);
int rate = params_rate(params), fs = 256;
u8 mode2;
if (rate)
fs = ak4641->sysclk / rate;
else
return -EINVAL;
/* set fs */
switch (fs) {
case 1024:
mode2 = (0x2 << 5);
break;
case 512:
mode2 = (0x1 << 5);
break;
case 256:
mode2 = (0x0 << 5);
break;
default:
dev_err(codec->dev, "Error: unsupported fs=%d\n", fs);
return -EINVAL;
}
snd_soc_update_bits(codec, AK4641_MODE2, (0x3 << 5), mode2);
/* Update de-emphasis filter for the new rate */
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
ak4641->playback_fs = rate;
ak4641_set_deemph(codec);
}
return 0;
}
static int ak4641_pcm_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
u8 btif;
int ret;
/* interface format */
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
btif = (0x3 << 5);
break;
case SND_SOC_DAIFMT_LEFT_J:
btif = (0x2 << 5);
break;
case SND_SOC_DAIFMT_DSP_A: /* MSB after FRM */
btif = (0x0 << 5);
break;
case SND_SOC_DAIFMT_DSP_B: /* MSB during FRM */
btif = (0x1 << 5);
break;
default:
return -EINVAL;
}
ret = snd_soc_update_bits(codec, AK4641_BTIF, (0x3 << 5), btif);
if (ret < 0)
return ret;
return 0;
}
static int ak4641_i2s_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
u8 mode1 = 0;
/* interface format */
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
mode1 = 0x02;
break;
case SND_SOC_DAIFMT_LEFT_J:
mode1 = 0x01;
break;
default:
return -EINVAL;
}
return snd_soc_write(codec, AK4641_MODE1, mode1);
}
static int ak4641_mute(struct snd_soc_dai *dai, int mute)
{
struct snd_soc_codec *codec = dai->codec;
return snd_soc_update_bits(codec, AK4641_DAC, 0x20, mute ? 0x20 : 0);
}
static int ak4641_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
struct ak4641_priv *ak4641 = snd_soc_codec_get_drvdata(codec);
struct ak4641_platform_data *pdata = codec->dev->platform_data;
int ret;
switch (level) {
case SND_SOC_BIAS_ON:
/* unmute */
snd_soc_update_bits(codec, AK4641_DAC, 0x20, 0);
break;
case SND_SOC_BIAS_PREPARE:
/* mute */
snd_soc_update_bits(codec, AK4641_DAC, 0x20, 0x20);
break;
case SND_SOC_BIAS_STANDBY:
if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
if (pdata && gpio_is_valid(pdata->gpio_power))
gpio_set_value(pdata->gpio_power, 1);
mdelay(1);
if (pdata && gpio_is_valid(pdata->gpio_npdn))
gpio_set_value(pdata->gpio_npdn, 1);
mdelay(1);
ret = regcache_sync(ak4641->regmap);
if (ret) {
dev_err(codec->dev,
"Failed to sync cache: %d\n", ret);
return ret;
}
}
snd_soc_update_bits(codec, AK4641_PM1, 0x80, 0x80);
snd_soc_update_bits(codec, AK4641_PM2, 0x80, 0);
break;
case SND_SOC_BIAS_OFF:
snd_soc_update_bits(codec, AK4641_PM1, 0x80, 0);
if (pdata && gpio_is_valid(pdata->gpio_npdn))
gpio_set_value(pdata->gpio_npdn, 0);
if (pdata && gpio_is_valid(pdata->gpio_power))
gpio_set_value(pdata->gpio_power, 0);
regcache_mark_dirty(ak4641->regmap);
break;
}
codec->dapm.bias_level = level;
return 0;
}
#define AK4641_RATES (SNDRV_PCM_RATE_8000_48000)
#define AK4641_RATES_BT (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
SNDRV_PCM_RATE_16000)
#define AK4641_FORMATS (SNDRV_PCM_FMTBIT_S16_LE)
static const struct snd_soc_dai_ops ak4641_i2s_dai_ops = {
.hw_params = ak4641_i2s_hw_params,
.set_fmt = ak4641_i2s_set_dai_fmt,
.digital_mute = ak4641_mute,
.set_sysclk = ak4641_set_dai_sysclk,
};
static const struct snd_soc_dai_ops ak4641_pcm_dai_ops = {
.hw_params = NULL, /* rates are controlled by BT chip */
.set_fmt = ak4641_pcm_set_dai_fmt,
.digital_mute = ak4641_mute,
.set_sysclk = ak4641_set_dai_sysclk,
};
static struct snd_soc_dai_driver ak4641_dai[] = {
{
.name = "ak4641-hifi",
.id = 1,
.playback = {
.stream_name = "HiFi Playback",
.channels_min = 1,
.channels_max = 2,
.rates = AK4641_RATES,
.formats = AK4641_FORMATS,
},
.capture = {
.stream_name = "HiFi Capture",
.channels_min = 1,
.channels_max = 2,
.rates = AK4641_RATES,
.formats = AK4641_FORMATS,
},
.ops = &ak4641_i2s_dai_ops,
.symmetric_rates = 1,
},
{
.name = "ak4641-voice",
.id = 1,
.playback = {
.stream_name = "Voice Playback",
.channels_min = 1,
.channels_max = 1,
.rates = AK4641_RATES_BT,
.formats = AK4641_FORMATS,
},
.capture = {
.stream_name = "Voice Capture",
.channels_min = 1,
.channels_max = 1,
.rates = AK4641_RATES_BT,
.formats = AK4641_FORMATS,
},
.ops = &ak4641_pcm_dai_ops,
.symmetric_rates = 1,
},
};
static int ak4641_suspend(struct snd_soc_codec *codec)
{
ak4641_set_bias_level(codec, SND_SOC_BIAS_OFF);
return 0;
}
static int ak4641_resume(struct snd_soc_codec *codec)
{
ak4641_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
return 0;
}
static int ak4641_probe(struct snd_soc_codec *codec)
{
/* power on device */
ak4641_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
return 0;
}
static int ak4641_remove(struct snd_soc_codec *codec)
{
ak4641_set_bias_level(codec, SND_SOC_BIAS_OFF);
return 0;
}
static struct snd_soc_codec_driver soc_codec_dev_ak4641 = {
.probe = ak4641_probe,
.remove = ak4641_remove,
.suspend = ak4641_suspend,
.resume = ak4641_resume,
.controls = ak4641_snd_controls,
.num_controls = ARRAY_SIZE(ak4641_snd_controls),
.dapm_widgets = ak4641_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(ak4641_dapm_widgets),
.dapm_routes = ak4641_audio_map,
.num_dapm_routes = ARRAY_SIZE(ak4641_audio_map),
.set_bias_level = ak4641_set_bias_level,
};
static const struct regmap_config ak4641_regmap = {
.reg_bits = 8,
.val_bits = 8,
.max_register = AK4641_BTIF,
.reg_defaults = ak4641_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(ak4641_reg_defaults),
.cache_type = REGCACHE_RBTREE,
};
static int ak4641_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct ak4641_platform_data *pdata = i2c->dev.platform_data;
struct ak4641_priv *ak4641;
int ret;
ak4641 = devm_kzalloc(&i2c->dev, sizeof(struct ak4641_priv),
GFP_KERNEL);
if (!ak4641)
return -ENOMEM;
ak4641->regmap = devm_regmap_init_i2c(i2c, &ak4641_regmap);
if (IS_ERR(ak4641->regmap))
return PTR_ERR(ak4641->regmap);
if (pdata) {
if (gpio_is_valid(pdata->gpio_power)) {
ret = gpio_request_one(pdata->gpio_power,
GPIOF_OUT_INIT_LOW, "ak4641 power");
if (ret)
goto err_out;
}
if (gpio_is_valid(pdata->gpio_npdn)) {
ret = gpio_request_one(pdata->gpio_npdn,
GPIOF_OUT_INIT_LOW, "ak4641 npdn");
if (ret)
goto err_gpio;
udelay(1); /* > 150 ns */
gpio_set_value(pdata->gpio_npdn, 1);
}
}
i2c_set_clientdata(i2c, ak4641);
ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_ak4641,
ak4641_dai, ARRAY_SIZE(ak4641_dai));
if (ret != 0)
goto err_gpio2;
return 0;
err_gpio2:
if (pdata) {
if (gpio_is_valid(pdata->gpio_power))
gpio_set_value(pdata->gpio_power, 0);
if (gpio_is_valid(pdata->gpio_npdn))
gpio_free(pdata->gpio_npdn);
}
err_gpio:
if (pdata && gpio_is_valid(pdata->gpio_power))
gpio_free(pdata->gpio_power);
err_out:
return ret;
}
static int ak4641_i2c_remove(struct i2c_client *i2c)
{
struct ak4641_platform_data *pdata = i2c->dev.platform_data;
snd_soc_unregister_codec(&i2c->dev);
if (pdata) {
if (gpio_is_valid(pdata->gpio_power)) {
gpio_set_value(pdata->gpio_power, 0);
gpio_free(pdata->gpio_power);
}
if (gpio_is_valid(pdata->gpio_npdn))
gpio_free(pdata->gpio_npdn);
}
return 0;
}
static const struct i2c_device_id ak4641_i2c_id[] = {
{ "ak4641", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, ak4641_i2c_id);
static struct i2c_driver ak4641_i2c_driver = {
.driver = {
.name = "ak4641",
.owner = THIS_MODULE,
},
.probe = ak4641_i2c_probe,
.remove = ak4641_i2c_remove,
.id_table = ak4641_i2c_id,
};
module_i2c_driver(ak4641_i2c_driver);
MODULE_DESCRIPTION("SoC AK4641 driver");
MODULE_AUTHOR("Harald Welte <laforge@gnufiish.org>");
MODULE_LICENSE("GPL");

47
sound/soc/codecs/ak4641.h Normal file
View file

@ -0,0 +1,47 @@
/*
* ak4641.h -- AK4641 SoC Audio driver
*
* Copyright 2008 Harald Welte <laforge@gnufiish.org>
*
* Based on ak4535.h
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef _AK4641_H
#define _AK4641_H
/* AK4641 register space */
#define AK4641_PM1 0x00
#define AK4641_PM2 0x01
#define AK4641_SIG1 0x02
#define AK4641_SIG2 0x03
#define AK4641_MODE1 0x04
#define AK4641_MODE2 0x05
#define AK4641_DAC 0x06
#define AK4641_MIC 0x07
#define AK4641_TIMER 0x08
#define AK4641_ALC1 0x09
#define AK4641_ALC2 0x0a
#define AK4641_PGA 0x0b
#define AK4641_LATT 0x0c
#define AK4641_RATT 0x0d
#define AK4641_VOL 0x0e
#define AK4641_STATUS 0x0f
#define AK4641_EQLO 0x10
#define AK4641_EQMID 0x11
#define AK4641_EQHI 0x12
#define AK4641_BTIF 0x13
#define AK4641_CACHEREGNUM 0x14
#define AK4641_DAI_HIFI 0
#define AK4641_DAI_VOICE 1
#endif

627
sound/soc/codecs/ak4642.c Normal file
View file

@ -0,0 +1,627 @@
/*
* ak4642.c -- AK4642/AK4643 ALSA Soc Audio driver
*
* Copyright (C) 2009 Renesas Solutions Corp.
* Kuninori Morimoto <morimoto.kuninori@renesas.com>
*
* Based on wm8731.c by Richard Purdie
* Based on ak4535.c by Richard Purdie
* Based on wm8753.c by Liam Girdwood
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
/* ** CAUTION **
*
* This is very simple driver.
* It can use headphone output / stereo input only
*
* AK4642 is tested.
* AK4643 is tested.
* AK4648 is tested.
*/
#include <linux/delay.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/initval.h>
#include <sound/tlv.h>
#define PW_MGMT1 0x00
#define PW_MGMT2 0x01
#define SG_SL1 0x02
#define SG_SL2 0x03
#define MD_CTL1 0x04
#define MD_CTL2 0x05
#define TIMER 0x06
#define ALC_CTL1 0x07
#define ALC_CTL2 0x08
#define L_IVC 0x09
#define L_DVC 0x0a
#define ALC_CTL3 0x0b
#define R_IVC 0x0c
#define R_DVC 0x0d
#define MD_CTL3 0x0e
#define MD_CTL4 0x0f
#define PW_MGMT3 0x10
#define DF_S 0x11
#define FIL3_0 0x12
#define FIL3_1 0x13
#define FIL3_2 0x14
#define FIL3_3 0x15
#define EQ_0 0x16
#define EQ_1 0x17
#define EQ_2 0x18
#define EQ_3 0x19
#define EQ_4 0x1a
#define EQ_5 0x1b
#define FIL1_0 0x1c
#define FIL1_1 0x1d
#define FIL1_2 0x1e
#define FIL1_3 0x1f
#define PW_MGMT4 0x20
#define MD_CTL5 0x21
#define LO_MS 0x22
#define HP_MS 0x23
#define SPK_MS 0x24
/* PW_MGMT1*/
#define PMVCM (1 << 6) /* VCOM Power Management */
#define PMMIN (1 << 5) /* MIN Input Power Management */
#define PMDAC (1 << 2) /* DAC Power Management */
#define PMADL (1 << 0) /* MIC Amp Lch and ADC Lch Power Management */
/* PW_MGMT2 */
#define HPMTN (1 << 6)
#define PMHPL (1 << 5)
#define PMHPR (1 << 4)
#define MS (1 << 3) /* master/slave select */
#define MCKO (1 << 1)
#define PMPLL (1 << 0)
#define PMHP_MASK (PMHPL | PMHPR)
#define PMHP PMHP_MASK
/* PW_MGMT3 */
#define PMADR (1 << 0) /* MIC L / ADC R Power Management */
/* SG_SL1 */
#define MINS (1 << 6) /* Switch from MIN to Speaker */
#define DACL (1 << 4) /* Switch from DAC to Stereo or Receiver */
#define PMMP (1 << 2) /* MPWR pin Power Management */
#define MGAIN0 (1 << 0) /* MIC amp gain*/
/* TIMER */
#define ZTM(param) ((param & 0x3) << 4) /* ALC Zero Crossing TimeOut */
#define WTM(param) (((param & 0x4) << 4) | ((param & 0x3) << 2))
/* ALC_CTL1 */
#define ALC (1 << 5) /* ALC Enable */
#define LMTH0 (1 << 0) /* ALC Limiter / Recovery Level */
/* MD_CTL1 */
#define PLL3 (1 << 7)
#define PLL2 (1 << 6)
#define PLL1 (1 << 5)
#define PLL0 (1 << 4)
#define PLL_MASK (PLL3 | PLL2 | PLL1 | PLL0)
#define BCKO_MASK (1 << 3)
#define BCKO_64 BCKO_MASK
#define DIF_MASK (3 << 0)
#define DSP (0 << 0)
#define RIGHT_J (1 << 0)
#define LEFT_J (2 << 0)
#define I2S (3 << 0)
/* MD_CTL2 */
#define FS0 (1 << 0)
#define FS1 (1 << 1)
#define FS2 (1 << 2)
#define FS3 (1 << 5)
#define FS_MASK (FS0 | FS1 | FS2 | FS3)
/* MD_CTL3 */
#define BST1 (1 << 3)
/* MD_CTL4 */
#define DACH (1 << 0)
struct ak4642_drvdata {
const struct regmap_config *regmap_config;
int extended_frequencies;
};
struct ak4642_priv {
const struct ak4642_drvdata *drvdata;
};
/*
* Playback Volume (table 39)
*
* max : 0x00 : +12.0 dB
* ( 0.5 dB step )
* min : 0xFE : -115.0 dB
* mute: 0xFF
*/
static const DECLARE_TLV_DB_SCALE(out_tlv, -11550, 50, 1);
static const struct snd_kcontrol_new ak4642_snd_controls[] = {
SOC_DOUBLE_R_TLV("Digital Playback Volume", L_DVC, R_DVC,
0, 0xFF, 1, out_tlv),
SOC_SINGLE("ALC Capture Switch", ALC_CTL1, 5, 1, 0),
SOC_SINGLE("ALC Capture ZC Switch", ALC_CTL1, 4, 1, 1),
};
static const struct snd_kcontrol_new ak4642_headphone_control =
SOC_DAPM_SINGLE("Switch", PW_MGMT2, 6, 1, 0);
static const struct snd_kcontrol_new ak4642_lout_mixer_controls[] = {
SOC_DAPM_SINGLE("DACL", SG_SL1, 4, 1, 0),
};
static const struct snd_soc_dapm_widget ak4642_dapm_widgets[] = {
/* Outputs */
SND_SOC_DAPM_OUTPUT("HPOUTL"),
SND_SOC_DAPM_OUTPUT("HPOUTR"),
SND_SOC_DAPM_OUTPUT("LINEOUT"),
SND_SOC_DAPM_PGA("HPL Out", PW_MGMT2, 5, 0, NULL, 0),
SND_SOC_DAPM_PGA("HPR Out", PW_MGMT2, 4, 0, NULL, 0),
SND_SOC_DAPM_SWITCH("Headphone Enable", SND_SOC_NOPM, 0, 0,
&ak4642_headphone_control),
SND_SOC_DAPM_PGA("DACH", MD_CTL4, 0, 0, NULL, 0),
SND_SOC_DAPM_MIXER("LINEOUT Mixer", PW_MGMT1, 3, 0,
&ak4642_lout_mixer_controls[0],
ARRAY_SIZE(ak4642_lout_mixer_controls)),
/* DAC */
SND_SOC_DAPM_DAC("DAC", "HiFi Playback", PW_MGMT1, 2, 0),
};
static const struct snd_soc_dapm_route ak4642_intercon[] = {
/* Outputs */
{"HPOUTL", NULL, "HPL Out"},
{"HPOUTR", NULL, "HPR Out"},
{"LINEOUT", NULL, "LINEOUT Mixer"},
{"HPL Out", NULL, "Headphone Enable"},
{"HPR Out", NULL, "Headphone Enable"},
{"Headphone Enable", "Switch", "DACH"},
{"DACH", NULL, "DAC"},
{"LINEOUT Mixer", "DACL", "DAC"},
};
/*
* ak4642 register cache
*/
static const struct reg_default ak4642_reg[] = {
{ 0, 0x00 }, { 1, 0x00 }, { 2, 0x01 }, { 3, 0x00 },
{ 4, 0x02 }, { 5, 0x00 }, { 6, 0x00 }, { 7, 0x00 },
{ 8, 0xe1 }, { 9, 0xe1 }, { 10, 0x18 }, { 11, 0x00 },
{ 12, 0xe1 }, { 13, 0x18 }, { 14, 0x11 }, { 15, 0x08 },
{ 16, 0x00 }, { 17, 0x00 }, { 18, 0x00 }, { 19, 0x00 },
{ 20, 0x00 }, { 21, 0x00 }, { 22, 0x00 }, { 23, 0x00 },
{ 24, 0x00 }, { 25, 0x00 }, { 26, 0x00 }, { 27, 0x00 },
{ 28, 0x00 }, { 29, 0x00 }, { 30, 0x00 }, { 31, 0x00 },
{ 32, 0x00 }, { 33, 0x00 }, { 34, 0x00 }, { 35, 0x00 },
{ 36, 0x00 },
};
static const struct reg_default ak4648_reg[] = {
{ 0, 0x00 }, { 1, 0x00 }, { 2, 0x01 }, { 3, 0x00 },
{ 4, 0x02 }, { 5, 0x00 }, { 6, 0x00 }, { 7, 0x00 },
{ 8, 0xe1 }, { 9, 0xe1 }, { 10, 0x18 }, { 11, 0x00 },
{ 12, 0xe1 }, { 13, 0x18 }, { 14, 0x11 }, { 15, 0xb8 },
{ 16, 0x00 }, { 17, 0x00 }, { 18, 0x00 }, { 19, 0x00 },
{ 20, 0x00 }, { 21, 0x00 }, { 22, 0x00 }, { 23, 0x00 },
{ 24, 0x00 }, { 25, 0x00 }, { 26, 0x00 }, { 27, 0x00 },
{ 28, 0x00 }, { 29, 0x00 }, { 30, 0x00 }, { 31, 0x00 },
{ 32, 0x00 }, { 33, 0x00 }, { 34, 0x00 }, { 35, 0x00 },
{ 36, 0x00 }, { 37, 0x88 }, { 38, 0x88 }, { 39, 0x08 },
};
static int ak4642_dai_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
struct snd_soc_codec *codec = dai->codec;
if (is_play) {
/*
* start headphone output
*
* PLL, Master Mode
* Audio I/F Format :MSB justified (ADC & DAC)
* Bass Boost Level : Middle
*
* This operation came from example code of
* "ASAHI KASEI AK4642" (japanese) manual p97.
*/
snd_soc_write(codec, L_IVC, 0x91); /* volume */
snd_soc_write(codec, R_IVC, 0x91); /* volume */
} else {
/*
* start stereo input
*
* PLL Master Mode
* Audio I/F Format:MSB justified (ADC & DAC)
* Pre MIC AMP:+20dB
* MIC Power On
* ALC setting:Refer to Table 35
* ALC bit=1
*
* This operation came from example code of
* "ASAHI KASEI AK4642" (japanese) manual p94.
*/
snd_soc_update_bits(codec, SG_SL1, PMMP | MGAIN0, PMMP | MGAIN0);
snd_soc_write(codec, TIMER, ZTM(0x3) | WTM(0x3));
snd_soc_write(codec, ALC_CTL1, ALC | LMTH0);
snd_soc_update_bits(codec, PW_MGMT1, PMADL, PMADL);
snd_soc_update_bits(codec, PW_MGMT3, PMADR, PMADR);
}
return 0;
}
static void ak4642_dai_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
struct snd_soc_codec *codec = dai->codec;
if (is_play) {
} else {
/* stop stereo input */
snd_soc_update_bits(codec, PW_MGMT1, PMADL, 0);
snd_soc_update_bits(codec, PW_MGMT3, PMADR, 0);
snd_soc_update_bits(codec, ALC_CTL1, ALC, 0);
}
}
static int ak4642_dai_set_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = codec_dai->codec;
struct ak4642_priv *priv = snd_soc_codec_get_drvdata(codec);
u8 pll;
int extended_freq = 0;
switch (freq) {
case 11289600:
pll = PLL2;
break;
case 12288000:
pll = PLL2 | PLL0;
break;
case 12000000:
pll = PLL2 | PLL1;
break;
case 24000000:
pll = PLL2 | PLL1 | PLL0;
break;
case 13500000:
pll = PLL3 | PLL2;
break;
case 27000000:
pll = PLL3 | PLL2 | PLL0;
break;
case 19200000:
pll = PLL3;
extended_freq = 1;
break;
case 13000000:
pll = PLL3 | PLL2 | PLL1;
extended_freq = 1;
break;
case 26000000:
pll = PLL3 | PLL2 | PLL1 | PLL0;
extended_freq = 1;
break;
default:
return -EINVAL;
}
if (extended_freq && !priv->drvdata->extended_frequencies)
return -EINVAL;
snd_soc_update_bits(codec, MD_CTL1, PLL_MASK, pll);
return 0;
}
static int ak4642_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
struct snd_soc_codec *codec = dai->codec;
u8 data;
u8 bcko;
data = MCKO | PMPLL; /* use MCKO */
bcko = 0;
/* set master/slave audio interface */
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBM_CFM:
data |= MS;
bcko = BCKO_64;
break;
case SND_SOC_DAIFMT_CBS_CFS:
break;
default:
return -EINVAL;
}
snd_soc_update_bits(codec, PW_MGMT2, MS | MCKO | PMPLL, data);
snd_soc_update_bits(codec, MD_CTL1, BCKO_MASK, bcko);
/* format type */
data = 0;
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_LEFT_J:
data = LEFT_J;
break;
case SND_SOC_DAIFMT_I2S:
data = I2S;
break;
/* FIXME
* Please add RIGHT_J / DSP support here
*/
default:
return -EINVAL;
}
snd_soc_update_bits(codec, MD_CTL1, DIF_MASK, data);
return 0;
}
static int ak4642_dai_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
u8 rate;
switch (params_rate(params)) {
case 7350:
rate = FS2;
break;
case 8000:
rate = 0;
break;
case 11025:
rate = FS2 | FS0;
break;
case 12000:
rate = FS0;
break;
case 14700:
rate = FS2 | FS1;
break;
case 16000:
rate = FS1;
break;
case 22050:
rate = FS2 | FS1 | FS0;
break;
case 24000:
rate = FS1 | FS0;
break;
case 29400:
rate = FS3 | FS2 | FS1;
break;
case 32000:
rate = FS3 | FS1;
break;
case 44100:
rate = FS3 | FS2 | FS1 | FS0;
break;
case 48000:
rate = FS3 | FS1 | FS0;
break;
default:
return -EINVAL;
}
snd_soc_update_bits(codec, MD_CTL2, FS_MASK, rate);
return 0;
}
static int ak4642_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
switch (level) {
case SND_SOC_BIAS_OFF:
snd_soc_write(codec, PW_MGMT1, 0x00);
break;
default:
snd_soc_update_bits(codec, PW_MGMT1, PMVCM, PMVCM);
break;
}
codec->dapm.bias_level = level;
return 0;
}
static const struct snd_soc_dai_ops ak4642_dai_ops = {
.startup = ak4642_dai_startup,
.shutdown = ak4642_dai_shutdown,
.set_sysclk = ak4642_dai_set_sysclk,
.set_fmt = ak4642_dai_set_fmt,
.hw_params = ak4642_dai_hw_params,
};
static struct snd_soc_dai_driver ak4642_dai = {
.name = "ak4642-hifi",
.playback = {
.stream_name = "Playback",
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE },
.capture = {
.stream_name = "Capture",
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE },
.ops = &ak4642_dai_ops,
.symmetric_rates = 1,
};
static int ak4642_resume(struct snd_soc_codec *codec)
{
struct regmap *regmap = dev_get_regmap(codec->dev, NULL);
regcache_mark_dirty(regmap);
regcache_sync(regmap);
return 0;
}
static int ak4642_probe(struct snd_soc_codec *codec)
{
ak4642_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
return 0;
}
static int ak4642_remove(struct snd_soc_codec *codec)
{
ak4642_set_bias_level(codec, SND_SOC_BIAS_OFF);
return 0;
}
static struct snd_soc_codec_driver soc_codec_dev_ak4642 = {
.probe = ak4642_probe,
.remove = ak4642_remove,
.resume = ak4642_resume,
.set_bias_level = ak4642_set_bias_level,
.controls = ak4642_snd_controls,
.num_controls = ARRAY_SIZE(ak4642_snd_controls),
.dapm_widgets = ak4642_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(ak4642_dapm_widgets),
.dapm_routes = ak4642_intercon,
.num_dapm_routes = ARRAY_SIZE(ak4642_intercon),
};
static const struct regmap_config ak4642_regmap = {
.reg_bits = 8,
.val_bits = 8,
.max_register = ARRAY_SIZE(ak4642_reg) + 1,
.reg_defaults = ak4642_reg,
.num_reg_defaults = ARRAY_SIZE(ak4642_reg),
};
static const struct regmap_config ak4648_regmap = {
.reg_bits = 8,
.val_bits = 8,
.max_register = ARRAY_SIZE(ak4648_reg) + 1,
.reg_defaults = ak4648_reg,
.num_reg_defaults = ARRAY_SIZE(ak4648_reg),
};
static const struct ak4642_drvdata ak4642_drvdata = {
.regmap_config = &ak4642_regmap,
};
static const struct ak4642_drvdata ak4643_drvdata = {
.regmap_config = &ak4642_regmap,
};
static const struct ak4642_drvdata ak4648_drvdata = {
.regmap_config = &ak4648_regmap,
.extended_frequencies = 1,
};
static const struct of_device_id ak4642_of_match[];
static int ak4642_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct device_node *np = i2c->dev.of_node;
const struct ak4642_drvdata *drvdata = NULL;
struct regmap *regmap;
struct ak4642_priv *priv;
if (np) {
const struct of_device_id *of_id;
of_id = of_match_device(ak4642_of_match, &i2c->dev);
if (of_id)
drvdata = of_id->data;
} else {
drvdata = (const struct ak4642_drvdata *)id->driver_data;
}
if (!drvdata) {
dev_err(&i2c->dev, "Unknown device type\n");
return -EINVAL;
}
priv = devm_kzalloc(&i2c->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->drvdata = drvdata;
i2c_set_clientdata(i2c, priv);
regmap = devm_regmap_init_i2c(i2c, drvdata->regmap_config);
if (IS_ERR(regmap))
return PTR_ERR(regmap);
return snd_soc_register_codec(&i2c->dev,
&soc_codec_dev_ak4642, &ak4642_dai, 1);
}
static int ak4642_i2c_remove(struct i2c_client *client)
{
snd_soc_unregister_codec(&client->dev);
return 0;
}
static const struct of_device_id ak4642_of_match[] = {
{ .compatible = "asahi-kasei,ak4642", .data = &ak4642_drvdata},
{ .compatible = "asahi-kasei,ak4643", .data = &ak4643_drvdata},
{ .compatible = "asahi-kasei,ak4648", .data = &ak4648_drvdata},
{},
};
MODULE_DEVICE_TABLE(of, ak4642_of_match);
static const struct i2c_device_id ak4642_i2c_id[] = {
{ "ak4642", (kernel_ulong_t)&ak4642_drvdata },
{ "ak4643", (kernel_ulong_t)&ak4643_drvdata },
{ "ak4648", (kernel_ulong_t)&ak4648_drvdata },
{ }
};
MODULE_DEVICE_TABLE(i2c, ak4642_i2c_id);
static struct i2c_driver ak4642_i2c_driver = {
.driver = {
.name = "ak4642-codec",
.owner = THIS_MODULE,
.of_match_table = ak4642_of_match,
},
.probe = ak4642_i2c_probe,
.remove = ak4642_i2c_remove,
.id_table = ak4642_i2c_id,
};
module_i2c_driver(ak4642_i2c_driver);
MODULE_DESCRIPTION("Soc AK4642 driver");
MODULE_AUTHOR("Kuninori Morimoto <morimoto.kuninori@renesas.com>");
MODULE_LICENSE("GPL");

691
sound/soc/codecs/ak4671.c Normal file
View file

@ -0,0 +1,691 @@
/*
* ak4671.c -- audio driver for AK4671
*
* Copyright (C) 2009 Samsung Electronics Co.Ltd
* Author: Joonyoung Shim <jy0922.shim@samsung.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <sound/soc.h>
#include <sound/initval.h>
#include <sound/tlv.h>
#include "ak4671.h"
/* ak4671 register cache & default register settings */
static const struct reg_default ak4671_reg_defaults[] = {
{ 0x00, 0x00 }, /* AK4671_AD_DA_POWER_MANAGEMENT (0x00) */
{ 0x01, 0xf6 }, /* AK4671_PLL_MODE_SELECT0 (0x01) */
{ 0x02, 0x00 }, /* AK4671_PLL_MODE_SELECT1 (0x02) */
{ 0x03, 0x02 }, /* AK4671_FORMAT_SELECT (0x03) */
{ 0x04, 0x00 }, /* AK4671_MIC_SIGNAL_SELECT (0x04) */
{ 0x05, 0x55 }, /* AK4671_MIC_AMP_GAIN (0x05) */
{ 0x06, 0x00 }, /* AK4671_MIXING_POWER_MANAGEMENT0 (0x06) */
{ 0x07, 0x00 }, /* AK4671_MIXING_POWER_MANAGEMENT1 (0x07) */
{ 0x08, 0xb5 }, /* AK4671_OUTPUT_VOLUME_CONTROL (0x08) */
{ 0x09, 0x00 }, /* AK4671_LOUT1_SIGNAL_SELECT (0x09) */
{ 0x0a, 0x00 }, /* AK4671_ROUT1_SIGNAL_SELECT (0x0a) */
{ 0x0b, 0x00 }, /* AK4671_LOUT2_SIGNAL_SELECT (0x0b) */
{ 0x0c, 0x00 }, /* AK4671_ROUT2_SIGNAL_SELECT (0x0c) */
{ 0x0d, 0x00 }, /* AK4671_LOUT3_SIGNAL_SELECT (0x0d) */
{ 0x0e, 0x00 }, /* AK4671_ROUT3_SIGNAL_SELECT (0x0e) */
{ 0x0f, 0x00 }, /* AK4671_LOUT1_POWER_MANAGERMENT (0x0f) */
{ 0x10, 0x00 }, /* AK4671_LOUT2_POWER_MANAGERMENT (0x10) */
{ 0x11, 0x80 }, /* AK4671_LOUT3_POWER_MANAGERMENT (0x11) */
{ 0x12, 0x91 }, /* AK4671_LCH_INPUT_VOLUME_CONTROL (0x12) */
{ 0x13, 0x91 }, /* AK4671_RCH_INPUT_VOLUME_CONTROL (0x13) */
{ 0x14, 0xe1 }, /* AK4671_ALC_REFERENCE_SELECT (0x14) */
{ 0x15, 0x00 }, /* AK4671_DIGITAL_MIXING_CONTROL (0x15) */
{ 0x16, 0x00 }, /* AK4671_ALC_TIMER_SELECT (0x16) */
{ 0x17, 0x00 }, /* AK4671_ALC_MODE_CONTROL (0x17) */
{ 0x18, 0x02 }, /* AK4671_MODE_CONTROL1 (0x18) */
{ 0x19, 0x01 }, /* AK4671_MODE_CONTROL2 (0x19) */
{ 0x1a, 0x18 }, /* AK4671_LCH_OUTPUT_VOLUME_CONTROL (0x1a) */
{ 0x1b, 0x18 }, /* AK4671_RCH_OUTPUT_VOLUME_CONTROL (0x1b) */
{ 0x1c, 0x00 }, /* AK4671_SIDETONE_A_CONTROL (0x1c) */
{ 0x1d, 0x02 }, /* AK4671_DIGITAL_FILTER_SELECT (0x1d) */
{ 0x1e, 0x00 }, /* AK4671_FIL3_COEFFICIENT0 (0x1e) */
{ 0x1f, 0x00 }, /* AK4671_FIL3_COEFFICIENT1 (0x1f) */
{ 0x20, 0x00 }, /* AK4671_FIL3_COEFFICIENT2 (0x20) */
{ 0x21, 0x00 }, /* AK4671_FIL3_COEFFICIENT3 (0x21) */
{ 0x22, 0x00 }, /* AK4671_EQ_COEFFICIENT0 (0x22) */
{ 0x23, 0x00 }, /* AK4671_EQ_COEFFICIENT1 (0x23) */
{ 0x24, 0x00 }, /* AK4671_EQ_COEFFICIENT2 (0x24) */
{ 0x25, 0x00 }, /* AK4671_EQ_COEFFICIENT3 (0x25) */
{ 0x26, 0x00 }, /* AK4671_EQ_COEFFICIENT4 (0x26) */
{ 0x27, 0x00 }, /* AK4671_EQ_COEFFICIENT5 (0x27) */
{ 0x28, 0xa9 }, /* AK4671_FIL1_COEFFICIENT0 (0x28) */
{ 0x29, 0x1f }, /* AK4671_FIL1_COEFFICIENT1 (0x29) */
{ 0x2a, 0xad }, /* AK4671_FIL1_COEFFICIENT2 (0x2a) */
{ 0x2b, 0x20 }, /* AK4671_FIL1_COEFFICIENT3 (0x2b) */
{ 0x2c, 0x00 }, /* AK4671_FIL2_COEFFICIENT0 (0x2c) */
{ 0x2d, 0x00 }, /* AK4671_FIL2_COEFFICIENT1 (0x2d) */
{ 0x2e, 0x00 }, /* AK4671_FIL2_COEFFICIENT2 (0x2e) */
{ 0x2f, 0x00 }, /* AK4671_FIL2_COEFFICIENT3 (0x2f) */
{ 0x30, 0x00 }, /* AK4671_DIGITAL_FILTER_SELECT2 (0x30) */
{ 0x32, 0x00 }, /* AK4671_E1_COEFFICIENT0 (0x32) */
{ 0x33, 0x00 }, /* AK4671_E1_COEFFICIENT1 (0x33) */
{ 0x34, 0x00 }, /* AK4671_E1_COEFFICIENT2 (0x34) */
{ 0x35, 0x00 }, /* AK4671_E1_COEFFICIENT3 (0x35) */
{ 0x36, 0x00 }, /* AK4671_E1_COEFFICIENT4 (0x36) */
{ 0x37, 0x00 }, /* AK4671_E1_COEFFICIENT5 (0x37) */
{ 0x38, 0x00 }, /* AK4671_E2_COEFFICIENT0 (0x38) */
{ 0x39, 0x00 }, /* AK4671_E2_COEFFICIENT1 (0x39) */
{ 0x3a, 0x00 }, /* AK4671_E2_COEFFICIENT2 (0x3a) */
{ 0x3b, 0x00 }, /* AK4671_E2_COEFFICIENT3 (0x3b) */
{ 0x3c, 0x00 }, /* AK4671_E2_COEFFICIENT4 (0x3c) */
{ 0x3d, 0x00 }, /* AK4671_E2_COEFFICIENT5 (0x3d) */
{ 0x3e, 0x00 }, /* AK4671_E3_COEFFICIENT0 (0x3e) */
{ 0x3f, 0x00 }, /* AK4671_E3_COEFFICIENT1 (0x3f) */
{ 0x40, 0x00 }, /* AK4671_E3_COEFFICIENT2 (0x40) */
{ 0x41, 0x00 }, /* AK4671_E3_COEFFICIENT3 (0x41) */
{ 0x42, 0x00 }, /* AK4671_E3_COEFFICIENT4 (0x42) */
{ 0x43, 0x00 }, /* AK4671_E3_COEFFICIENT5 (0x43) */
{ 0x44, 0x00 }, /* AK4671_E4_COEFFICIENT0 (0x44) */
{ 0x45, 0x00 }, /* AK4671_E4_COEFFICIENT1 (0x45) */
{ 0x46, 0x00 }, /* AK4671_E4_COEFFICIENT2 (0x46) */
{ 0x47, 0x00 }, /* AK4671_E4_COEFFICIENT3 (0x47) */
{ 0x48, 0x00 }, /* AK4671_E4_COEFFICIENT4 (0x48) */
{ 0x49, 0x00 }, /* AK4671_E4_COEFFICIENT5 (0x49) */
{ 0x4a, 0x00 }, /* AK4671_E5_COEFFICIENT0 (0x4a) */
{ 0x4b, 0x00 }, /* AK4671_E5_COEFFICIENT1 (0x4b) */
{ 0x4c, 0x00 }, /* AK4671_E5_COEFFICIENT2 (0x4c) */
{ 0x4d, 0x00 }, /* AK4671_E5_COEFFICIENT3 (0x4d) */
{ 0x4e, 0x00 }, /* AK4671_E5_COEFFICIENT4 (0x4e) */
{ 0x4f, 0x00 }, /* AK4671_E5_COEFFICIENT5 (0x4f) */
{ 0x50, 0x88 }, /* AK4671_EQ_CONTROL_250HZ_100HZ (0x50) */
{ 0x51, 0x88 }, /* AK4671_EQ_CONTROL_3500HZ_1KHZ (0x51) */
{ 0x52, 0x08 }, /* AK4671_EQ_CONTRO_10KHZ (0x52) */
{ 0x53, 0x00 }, /* AK4671_PCM_IF_CONTROL0 (0x53) */
{ 0x54, 0x00 }, /* AK4671_PCM_IF_CONTROL1 (0x54) */
{ 0x55, 0x00 }, /* AK4671_PCM_IF_CONTROL2 (0x55) */
{ 0x56, 0x18 }, /* AK4671_DIGITAL_VOLUME_B_CONTROL (0x56) */
{ 0x57, 0x18 }, /* AK4671_DIGITAL_VOLUME_C_CONTROL (0x57) */
{ 0x58, 0x00 }, /* AK4671_SIDETONE_VOLUME_CONTROL (0x58) */
{ 0x59, 0x00 }, /* AK4671_DIGITAL_MIXING_CONTROL2 (0x59) */
{ 0x5a, 0x00 }, /* AK4671_SAR_ADC_CONTROL (0x5a) */
};
/*
* LOUT1/ROUT1 output volume control:
* from -24 to 6 dB in 6 dB steps (mute instead of -30 dB)
*/
static DECLARE_TLV_DB_SCALE(out1_tlv, -3000, 600, 1);
/*
* LOUT2/ROUT2 output volume control:
* from -33 to 6 dB in 3 dB steps (mute instead of -33 dB)
*/
static DECLARE_TLV_DB_SCALE(out2_tlv, -3300, 300, 1);
/*
* LOUT3/ROUT3 output volume control:
* from -6 to 3 dB in 3 dB steps
*/
static DECLARE_TLV_DB_SCALE(out3_tlv, -600, 300, 0);
/*
* Mic amp gain control:
* from -15 to 30 dB in 3 dB steps
* REVISIT: The actual min value(0x01) is -12 dB and the reg value 0x00 is not
* available
*/
static DECLARE_TLV_DB_SCALE(mic_amp_tlv, -1500, 300, 0);
static const struct snd_kcontrol_new ak4671_snd_controls[] = {
/* Common playback gain controls */
SOC_SINGLE_TLV("Line Output1 Playback Volume",
AK4671_OUTPUT_VOLUME_CONTROL, 0, 0x6, 0, out1_tlv),
SOC_SINGLE_TLV("Headphone Output2 Playback Volume",
AK4671_OUTPUT_VOLUME_CONTROL, 4, 0xd, 0, out2_tlv),
SOC_SINGLE_TLV("Line Output3 Playback Volume",
AK4671_LOUT3_POWER_MANAGERMENT, 6, 0x3, 0, out3_tlv),
/* Common capture gain controls */
SOC_DOUBLE_TLV("Mic Amp Capture Volume",
AK4671_MIC_AMP_GAIN, 0, 4, 0xf, 0, mic_amp_tlv),
};
/* event handlers */
static int ak4671_out2_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = w->codec;
switch (event) {
case SND_SOC_DAPM_POST_PMU:
snd_soc_update_bits(codec, AK4671_LOUT2_POWER_MANAGERMENT,
AK4671_MUTEN, AK4671_MUTEN);
break;
case SND_SOC_DAPM_PRE_PMD:
snd_soc_update_bits(codec, AK4671_LOUT2_POWER_MANAGERMENT,
AK4671_MUTEN, 0);
break;
}
return 0;
}
/* Output Mixers */
static const struct snd_kcontrol_new ak4671_lout1_mixer_controls[] = {
SOC_DAPM_SINGLE("DACL", AK4671_LOUT1_SIGNAL_SELECT, 0, 1, 0),
SOC_DAPM_SINGLE("LINL1", AK4671_LOUT1_SIGNAL_SELECT, 1, 1, 0),
SOC_DAPM_SINGLE("LINL2", AK4671_LOUT1_SIGNAL_SELECT, 2, 1, 0),
SOC_DAPM_SINGLE("LINL3", AK4671_LOUT1_SIGNAL_SELECT, 3, 1, 0),
SOC_DAPM_SINGLE("LINL4", AK4671_LOUT1_SIGNAL_SELECT, 4, 1, 0),
SOC_DAPM_SINGLE("LOOPL", AK4671_LOUT1_SIGNAL_SELECT, 5, 1, 0),
};
static const struct snd_kcontrol_new ak4671_rout1_mixer_controls[] = {
SOC_DAPM_SINGLE("DACR", AK4671_ROUT1_SIGNAL_SELECT, 0, 1, 0),
SOC_DAPM_SINGLE("RINR1", AK4671_ROUT1_SIGNAL_SELECT, 1, 1, 0),
SOC_DAPM_SINGLE("RINR2", AK4671_ROUT1_SIGNAL_SELECT, 2, 1, 0),
SOC_DAPM_SINGLE("RINR3", AK4671_ROUT1_SIGNAL_SELECT, 3, 1, 0),
SOC_DAPM_SINGLE("RINR4", AK4671_ROUT1_SIGNAL_SELECT, 4, 1, 0),
SOC_DAPM_SINGLE("LOOPR", AK4671_ROUT1_SIGNAL_SELECT, 5, 1, 0),
};
static const struct snd_kcontrol_new ak4671_lout2_mixer_controls[] = {
SOC_DAPM_SINGLE("DACHL", AK4671_LOUT2_SIGNAL_SELECT, 0, 1, 0),
SOC_DAPM_SINGLE("LINH1", AK4671_LOUT2_SIGNAL_SELECT, 1, 1, 0),
SOC_DAPM_SINGLE("LINH2", AK4671_LOUT2_SIGNAL_SELECT, 2, 1, 0),
SOC_DAPM_SINGLE("LINH3", AK4671_LOUT2_SIGNAL_SELECT, 3, 1, 0),
SOC_DAPM_SINGLE("LINH4", AK4671_LOUT2_SIGNAL_SELECT, 4, 1, 0),
SOC_DAPM_SINGLE("LOOPHL", AK4671_LOUT2_SIGNAL_SELECT, 5, 1, 0),
};
static const struct snd_kcontrol_new ak4671_rout2_mixer_controls[] = {
SOC_DAPM_SINGLE("DACHR", AK4671_ROUT2_SIGNAL_SELECT, 0, 1, 0),
SOC_DAPM_SINGLE("RINH1", AK4671_ROUT2_SIGNAL_SELECT, 1, 1, 0),
SOC_DAPM_SINGLE("RINH2", AK4671_ROUT2_SIGNAL_SELECT, 2, 1, 0),
SOC_DAPM_SINGLE("RINH3", AK4671_ROUT2_SIGNAL_SELECT, 3, 1, 0),
SOC_DAPM_SINGLE("RINH4", AK4671_ROUT2_SIGNAL_SELECT, 4, 1, 0),
SOC_DAPM_SINGLE("LOOPHR", AK4671_ROUT2_SIGNAL_SELECT, 5, 1, 0),
};
static const struct snd_kcontrol_new ak4671_lout3_mixer_controls[] = {
SOC_DAPM_SINGLE("DACSL", AK4671_LOUT3_SIGNAL_SELECT, 0, 1, 0),
SOC_DAPM_SINGLE("LINS1", AK4671_LOUT3_SIGNAL_SELECT, 1, 1, 0),
SOC_DAPM_SINGLE("LINS2", AK4671_LOUT3_SIGNAL_SELECT, 2, 1, 0),
SOC_DAPM_SINGLE("LINS3", AK4671_LOUT3_SIGNAL_SELECT, 3, 1, 0),
SOC_DAPM_SINGLE("LINS4", AK4671_LOUT3_SIGNAL_SELECT, 4, 1, 0),
SOC_DAPM_SINGLE("LOOPSL", AK4671_LOUT3_SIGNAL_SELECT, 5, 1, 0),
};
static const struct snd_kcontrol_new ak4671_rout3_mixer_controls[] = {
SOC_DAPM_SINGLE("DACSR", AK4671_ROUT3_SIGNAL_SELECT, 0, 1, 0),
SOC_DAPM_SINGLE("RINS1", AK4671_ROUT3_SIGNAL_SELECT, 1, 1, 0),
SOC_DAPM_SINGLE("RINS2", AK4671_ROUT3_SIGNAL_SELECT, 2, 1, 0),
SOC_DAPM_SINGLE("RINS3", AK4671_ROUT3_SIGNAL_SELECT, 3, 1, 0),
SOC_DAPM_SINGLE("RINS4", AK4671_ROUT3_SIGNAL_SELECT, 4, 1, 0),
SOC_DAPM_SINGLE("LOOPSR", AK4671_ROUT3_SIGNAL_SELECT, 5, 1, 0),
};
/* Input MUXs */
static const char *ak4671_lin_mux_texts[] =
{"LIN1", "LIN2", "LIN3", "LIN4"};
static SOC_ENUM_SINGLE_DECL(ak4671_lin_mux_enum,
AK4671_MIC_SIGNAL_SELECT, 0,
ak4671_lin_mux_texts);
static const struct snd_kcontrol_new ak4671_lin_mux_control =
SOC_DAPM_ENUM("Route", ak4671_lin_mux_enum);
static const char *ak4671_rin_mux_texts[] =
{"RIN1", "RIN2", "RIN3", "RIN4"};
static SOC_ENUM_SINGLE_DECL(ak4671_rin_mux_enum,
AK4671_MIC_SIGNAL_SELECT, 2,
ak4671_rin_mux_texts);
static const struct snd_kcontrol_new ak4671_rin_mux_control =
SOC_DAPM_ENUM("Route", ak4671_rin_mux_enum);
static const struct snd_soc_dapm_widget ak4671_dapm_widgets[] = {
/* Inputs */
SND_SOC_DAPM_INPUT("LIN1"),
SND_SOC_DAPM_INPUT("RIN1"),
SND_SOC_DAPM_INPUT("LIN2"),
SND_SOC_DAPM_INPUT("RIN2"),
SND_SOC_DAPM_INPUT("LIN3"),
SND_SOC_DAPM_INPUT("RIN3"),
SND_SOC_DAPM_INPUT("LIN4"),
SND_SOC_DAPM_INPUT("RIN4"),
/* Outputs */
SND_SOC_DAPM_OUTPUT("LOUT1"),
SND_SOC_DAPM_OUTPUT("ROUT1"),
SND_SOC_DAPM_OUTPUT("LOUT2"),
SND_SOC_DAPM_OUTPUT("ROUT2"),
SND_SOC_DAPM_OUTPUT("LOUT3"),
SND_SOC_DAPM_OUTPUT("ROUT3"),
/* DAC */
SND_SOC_DAPM_DAC("DAC Left", "Left HiFi Playback",
AK4671_AD_DA_POWER_MANAGEMENT, 6, 0),
SND_SOC_DAPM_DAC("DAC Right", "Right HiFi Playback",
AK4671_AD_DA_POWER_MANAGEMENT, 7, 0),
/* ADC */
SND_SOC_DAPM_ADC("ADC Left", "Left HiFi Capture",
AK4671_AD_DA_POWER_MANAGEMENT, 4, 0),
SND_SOC_DAPM_ADC("ADC Right", "Right HiFi Capture",
AK4671_AD_DA_POWER_MANAGEMENT, 5, 0),
/* PGA */
SND_SOC_DAPM_PGA("LOUT2 Mix Amp",
AK4671_LOUT2_POWER_MANAGERMENT, 5, 0, NULL, 0),
SND_SOC_DAPM_PGA("ROUT2 Mix Amp",
AK4671_LOUT2_POWER_MANAGERMENT, 6, 0, NULL, 0),
SND_SOC_DAPM_PGA("LIN1 Mixing Circuit",
AK4671_MIXING_POWER_MANAGEMENT1, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("RIN1 Mixing Circuit",
AK4671_MIXING_POWER_MANAGEMENT1, 1, 0, NULL, 0),
SND_SOC_DAPM_PGA("LIN2 Mixing Circuit",
AK4671_MIXING_POWER_MANAGEMENT1, 2, 0, NULL, 0),
SND_SOC_DAPM_PGA("RIN2 Mixing Circuit",
AK4671_MIXING_POWER_MANAGEMENT1, 3, 0, NULL, 0),
SND_SOC_DAPM_PGA("LIN3 Mixing Circuit",
AK4671_MIXING_POWER_MANAGEMENT1, 4, 0, NULL, 0),
SND_SOC_DAPM_PGA("RIN3 Mixing Circuit",
AK4671_MIXING_POWER_MANAGEMENT1, 5, 0, NULL, 0),
SND_SOC_DAPM_PGA("LIN4 Mixing Circuit",
AK4671_MIXING_POWER_MANAGEMENT1, 6, 0, NULL, 0),
SND_SOC_DAPM_PGA("RIN4 Mixing Circuit",
AK4671_MIXING_POWER_MANAGEMENT1, 7, 0, NULL, 0),
/* Output Mixers */
SND_SOC_DAPM_MIXER("LOUT1 Mixer", AK4671_LOUT1_POWER_MANAGERMENT, 0, 0,
&ak4671_lout1_mixer_controls[0],
ARRAY_SIZE(ak4671_lout1_mixer_controls)),
SND_SOC_DAPM_MIXER("ROUT1 Mixer", AK4671_LOUT1_POWER_MANAGERMENT, 1, 0,
&ak4671_rout1_mixer_controls[0],
ARRAY_SIZE(ak4671_rout1_mixer_controls)),
SND_SOC_DAPM_MIXER_E("LOUT2 Mixer", AK4671_LOUT2_POWER_MANAGERMENT,
0, 0, &ak4671_lout2_mixer_controls[0],
ARRAY_SIZE(ak4671_lout2_mixer_controls),
ak4671_out2_event,
SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_PRE_PMD),
SND_SOC_DAPM_MIXER_E("ROUT2 Mixer", AK4671_LOUT2_POWER_MANAGERMENT,
1, 0, &ak4671_rout2_mixer_controls[0],
ARRAY_SIZE(ak4671_rout2_mixer_controls),
ak4671_out2_event,
SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_PRE_PMD),
SND_SOC_DAPM_MIXER("LOUT3 Mixer", AK4671_LOUT3_POWER_MANAGERMENT, 0, 0,
&ak4671_lout3_mixer_controls[0],
ARRAY_SIZE(ak4671_lout3_mixer_controls)),
SND_SOC_DAPM_MIXER("ROUT3 Mixer", AK4671_LOUT3_POWER_MANAGERMENT, 1, 0,
&ak4671_rout3_mixer_controls[0],
ARRAY_SIZE(ak4671_rout3_mixer_controls)),
/* Input MUXs */
SND_SOC_DAPM_MUX("LIN MUX", AK4671_AD_DA_POWER_MANAGEMENT, 2, 0,
&ak4671_lin_mux_control),
SND_SOC_DAPM_MUX("RIN MUX", AK4671_AD_DA_POWER_MANAGEMENT, 3, 0,
&ak4671_rin_mux_control),
/* Mic Power */
SND_SOC_DAPM_MICBIAS("Mic Bias", AK4671_AD_DA_POWER_MANAGEMENT, 1, 0),
/* Supply */
SND_SOC_DAPM_SUPPLY("PMPLL", AK4671_PLL_MODE_SELECT1, 0, 0, NULL, 0),
};
static const struct snd_soc_dapm_route ak4671_intercon[] = {
{"DAC Left", NULL, "PMPLL"},
{"DAC Right", NULL, "PMPLL"},
{"ADC Left", NULL, "PMPLL"},
{"ADC Right", NULL, "PMPLL"},
/* Outputs */
{"LOUT1", NULL, "LOUT1 Mixer"},
{"ROUT1", NULL, "ROUT1 Mixer"},
{"LOUT2", NULL, "LOUT2 Mix Amp"},
{"ROUT2", NULL, "ROUT2 Mix Amp"},
{"LOUT3", NULL, "LOUT3 Mixer"},
{"ROUT3", NULL, "ROUT3 Mixer"},
{"LOUT1 Mixer", "DACL", "DAC Left"},
{"ROUT1 Mixer", "DACR", "DAC Right"},
{"LOUT2 Mixer", "DACHL", "DAC Left"},
{"ROUT2 Mixer", "DACHR", "DAC Right"},
{"LOUT2 Mix Amp", NULL, "LOUT2 Mixer"},
{"ROUT2 Mix Amp", NULL, "ROUT2 Mixer"},
{"LOUT3 Mixer", "DACSL", "DAC Left"},
{"ROUT3 Mixer", "DACSR", "DAC Right"},
/* Inputs */
{"LIN MUX", "LIN1", "LIN1"},
{"LIN MUX", "LIN2", "LIN2"},
{"LIN MUX", "LIN3", "LIN3"},
{"LIN MUX", "LIN4", "LIN4"},
{"RIN MUX", "RIN1", "RIN1"},
{"RIN MUX", "RIN2", "RIN2"},
{"RIN MUX", "RIN3", "RIN3"},
{"RIN MUX", "RIN4", "RIN4"},
{"LIN1", NULL, "Mic Bias"},
{"RIN1", NULL, "Mic Bias"},
{"LIN2", NULL, "Mic Bias"},
{"RIN2", NULL, "Mic Bias"},
{"ADC Left", NULL, "LIN MUX"},
{"ADC Right", NULL, "RIN MUX"},
/* Analog Loops */
{"LIN1 Mixing Circuit", NULL, "LIN1"},
{"RIN1 Mixing Circuit", NULL, "RIN1"},
{"LIN2 Mixing Circuit", NULL, "LIN2"},
{"RIN2 Mixing Circuit", NULL, "RIN2"},
{"LIN3 Mixing Circuit", NULL, "LIN3"},
{"RIN3 Mixing Circuit", NULL, "RIN3"},
{"LIN4 Mixing Circuit", NULL, "LIN4"},
{"RIN4 Mixing Circuit", NULL, "RIN4"},
{"LOUT1 Mixer", "LINL1", "LIN1 Mixing Circuit"},
{"ROUT1 Mixer", "RINR1", "RIN1 Mixing Circuit"},
{"LOUT2 Mixer", "LINH1", "LIN1 Mixing Circuit"},
{"ROUT2 Mixer", "RINH1", "RIN1 Mixing Circuit"},
{"LOUT3 Mixer", "LINS1", "LIN1 Mixing Circuit"},
{"ROUT3 Mixer", "RINS1", "RIN1 Mixing Circuit"},
{"LOUT1 Mixer", "LINL2", "LIN2 Mixing Circuit"},
{"ROUT1 Mixer", "RINR2", "RIN2 Mixing Circuit"},
{"LOUT2 Mixer", "LINH2", "LIN2 Mixing Circuit"},
{"ROUT2 Mixer", "RINH2", "RIN2 Mixing Circuit"},
{"LOUT3 Mixer", "LINS2", "LIN2 Mixing Circuit"},
{"ROUT3 Mixer", "RINS2", "RIN2 Mixing Circuit"},
{"LOUT1 Mixer", "LINL3", "LIN3 Mixing Circuit"},
{"ROUT1 Mixer", "RINR3", "RIN3 Mixing Circuit"},
{"LOUT2 Mixer", "LINH3", "LIN3 Mixing Circuit"},
{"ROUT2 Mixer", "RINH3", "RIN3 Mixing Circuit"},
{"LOUT3 Mixer", "LINS3", "LIN3 Mixing Circuit"},
{"ROUT3 Mixer", "RINS3", "RIN3 Mixing Circuit"},
{"LOUT1 Mixer", "LINL4", "LIN4 Mixing Circuit"},
{"ROUT1 Mixer", "RINR4", "RIN4 Mixing Circuit"},
{"LOUT2 Mixer", "LINH4", "LIN4 Mixing Circuit"},
{"ROUT2 Mixer", "RINH4", "RIN4 Mixing Circuit"},
{"LOUT3 Mixer", "LINS4", "LIN4 Mixing Circuit"},
{"ROUT3 Mixer", "RINS4", "RIN4 Mixing Circuit"},
};
static int ak4671_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
u8 fs;
fs = snd_soc_read(codec, AK4671_PLL_MODE_SELECT0);
fs &= ~AK4671_FS;
switch (params_rate(params)) {
case 8000:
fs |= AK4671_FS_8KHZ;
break;
case 12000:
fs |= AK4671_FS_12KHZ;
break;
case 16000:
fs |= AK4671_FS_16KHZ;
break;
case 24000:
fs |= AK4671_FS_24KHZ;
break;
case 11025:
fs |= AK4671_FS_11_025KHZ;
break;
case 22050:
fs |= AK4671_FS_22_05KHZ;
break;
case 32000:
fs |= AK4671_FS_32KHZ;
break;
case 44100:
fs |= AK4671_FS_44_1KHZ;
break;
case 48000:
fs |= AK4671_FS_48KHZ;
break;
default:
return -EINVAL;
}
snd_soc_write(codec, AK4671_PLL_MODE_SELECT0, fs);
return 0;
}
static int ak4671_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
unsigned int freq, int dir)
{
struct snd_soc_codec *codec = dai->codec;
u8 pll;
pll = snd_soc_read(codec, AK4671_PLL_MODE_SELECT0);
pll &= ~AK4671_PLL;
switch (freq) {
case 11289600:
pll |= AK4671_PLL_11_2896MHZ;
break;
case 12000000:
pll |= AK4671_PLL_12MHZ;
break;
case 12288000:
pll |= AK4671_PLL_12_288MHZ;
break;
case 13000000:
pll |= AK4671_PLL_13MHZ;
break;
case 13500000:
pll |= AK4671_PLL_13_5MHZ;
break;
case 19200000:
pll |= AK4671_PLL_19_2MHZ;
break;
case 24000000:
pll |= AK4671_PLL_24MHZ;
break;
case 26000000:
pll |= AK4671_PLL_26MHZ;
break;
case 27000000:
pll |= AK4671_PLL_27MHZ;
break;
default:
return -EINVAL;
}
snd_soc_write(codec, AK4671_PLL_MODE_SELECT0, pll);
return 0;
}
static int ak4671_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
struct snd_soc_codec *codec = dai->codec;
u8 mode;
u8 format;
/* set master/slave audio interface */
mode = snd_soc_read(codec, AK4671_PLL_MODE_SELECT1);
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBM_CFM:
mode |= AK4671_M_S;
break;
case SND_SOC_DAIFMT_CBM_CFS:
mode &= ~(AK4671_M_S);
break;
default:
return -EINVAL;
}
/* interface format */
format = snd_soc_read(codec, AK4671_FORMAT_SELECT);
format &= ~AK4671_DIF;
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
format |= AK4671_DIF_I2S_MODE;
break;
case SND_SOC_DAIFMT_LEFT_J:
format |= AK4671_DIF_MSB_MODE;
break;
case SND_SOC_DAIFMT_DSP_A:
format |= AK4671_DIF_DSP_MODE;
format |= AK4671_BCKP;
format |= AK4671_MSBS;
break;
default:
return -EINVAL;
}
/* set mode and format */
snd_soc_write(codec, AK4671_PLL_MODE_SELECT1, mode);
snd_soc_write(codec, AK4671_FORMAT_SELECT, format);
return 0;
}
static int ak4671_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
switch (level) {
case SND_SOC_BIAS_ON:
case SND_SOC_BIAS_PREPARE:
case SND_SOC_BIAS_STANDBY:
snd_soc_update_bits(codec, AK4671_AD_DA_POWER_MANAGEMENT,
AK4671_PMVCM, AK4671_PMVCM);
break;
case SND_SOC_BIAS_OFF:
snd_soc_write(codec, AK4671_AD_DA_POWER_MANAGEMENT, 0x00);
break;
}
codec->dapm.bias_level = level;
return 0;
}
#define AK4671_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\
SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
SNDRV_PCM_RATE_48000)
#define AK4671_FORMATS SNDRV_PCM_FMTBIT_S16_LE
static const struct snd_soc_dai_ops ak4671_dai_ops = {
.hw_params = ak4671_hw_params,
.set_sysclk = ak4671_set_dai_sysclk,
.set_fmt = ak4671_set_dai_fmt,
};
static struct snd_soc_dai_driver ak4671_dai = {
.name = "ak4671-hifi",
.playback = {
.stream_name = "Playback",
.channels_min = 1,
.channels_max = 2,
.rates = AK4671_RATES,
.formats = AK4671_FORMATS,},
.capture = {
.stream_name = "Capture",
.channels_min = 1,
.channels_max = 2,
.rates = AK4671_RATES,
.formats = AK4671_FORMATS,},
.ops = &ak4671_dai_ops,
};
static int ak4671_probe(struct snd_soc_codec *codec)
{
return ak4671_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
}
static int ak4671_remove(struct snd_soc_codec *codec)
{
ak4671_set_bias_level(codec, SND_SOC_BIAS_OFF);
return 0;
}
static struct snd_soc_codec_driver soc_codec_dev_ak4671 = {
.probe = ak4671_probe,
.remove = ak4671_remove,
.set_bias_level = ak4671_set_bias_level,
.controls = ak4671_snd_controls,
.num_controls = ARRAY_SIZE(ak4671_snd_controls),
.dapm_widgets = ak4671_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(ak4671_dapm_widgets),
.dapm_routes = ak4671_intercon,
.num_dapm_routes = ARRAY_SIZE(ak4671_intercon),
};
static const struct regmap_config ak4671_regmap = {
.reg_bits = 8,
.val_bits = 8,
.max_register = AK4671_SAR_ADC_CONTROL,
.reg_defaults = ak4671_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(ak4671_reg_defaults),
.cache_type = REGCACHE_RBTREE,
};
static int ak4671_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct regmap *regmap;
int ret;
regmap = devm_regmap_init_i2c(client, &ak4671_regmap);
if (IS_ERR(regmap)) {
ret = PTR_ERR(regmap);
dev_err(&client->dev, "Failed to create regmap: %d\n", ret);
return ret;
}
ret = snd_soc_register_codec(&client->dev,
&soc_codec_dev_ak4671, &ak4671_dai, 1);
return ret;
}
static int ak4671_i2c_remove(struct i2c_client *client)
{
snd_soc_unregister_codec(&client->dev);
return 0;
}
static const struct i2c_device_id ak4671_i2c_id[] = {
{ "ak4671", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, ak4671_i2c_id);
static struct i2c_driver ak4671_i2c_driver = {
.driver = {
.name = "ak4671-codec",
.owner = THIS_MODULE,
},
.probe = ak4671_i2c_probe,
.remove = ak4671_i2c_remove,
.id_table = ak4671_i2c_id,
};
module_i2c_driver(ak4671_i2c_driver);
MODULE_DESCRIPTION("ASoC AK4671 codec driver");
MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
MODULE_LICENSE("GPL");

151
sound/soc/codecs/ak4671.h Normal file
View file

@ -0,0 +1,151 @@
/*
* ak4671.h -- audio driver for AK4671
*
* Copyright (C) 2009 Samsung Electronics Co.Ltd
* Author: Joonyoung Shim <jy0922.shim@samsung.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#ifndef _AK4671_H
#define _AK4671_H
#define AK4671_AD_DA_POWER_MANAGEMENT 0x00
#define AK4671_PLL_MODE_SELECT0 0x01
#define AK4671_PLL_MODE_SELECT1 0x02
#define AK4671_FORMAT_SELECT 0x03
#define AK4671_MIC_SIGNAL_SELECT 0x04
#define AK4671_MIC_AMP_GAIN 0x05
#define AK4671_MIXING_POWER_MANAGEMENT0 0x06
#define AK4671_MIXING_POWER_MANAGEMENT1 0x07
#define AK4671_OUTPUT_VOLUME_CONTROL 0x08
#define AK4671_LOUT1_SIGNAL_SELECT 0x09
#define AK4671_ROUT1_SIGNAL_SELECT 0x0a
#define AK4671_LOUT2_SIGNAL_SELECT 0x0b
#define AK4671_ROUT2_SIGNAL_SELECT 0x0c
#define AK4671_LOUT3_SIGNAL_SELECT 0x0d
#define AK4671_ROUT3_SIGNAL_SELECT 0x0e
#define AK4671_LOUT1_POWER_MANAGERMENT 0x0f
#define AK4671_LOUT2_POWER_MANAGERMENT 0x10
#define AK4671_LOUT3_POWER_MANAGERMENT 0x11
#define AK4671_LCH_INPUT_VOLUME_CONTROL 0x12
#define AK4671_RCH_INPUT_VOLUME_CONTROL 0x13
#define AK4671_ALC_REFERENCE_SELECT 0x14
#define AK4671_DIGITAL_MIXING_CONTROL 0x15
#define AK4671_ALC_TIMER_SELECT 0x16
#define AK4671_ALC_MODE_CONTROL 0x17
#define AK4671_MODE_CONTROL1 0x18
#define AK4671_MODE_CONTROL2 0x19
#define AK4671_LCH_OUTPUT_VOLUME_CONTROL 0x1a
#define AK4671_RCH_OUTPUT_VOLUME_CONTROL 0x1b
#define AK4671_SIDETONE_A_CONTROL 0x1c
#define AK4671_DIGITAL_FILTER_SELECT 0x1d
#define AK4671_FIL3_COEFFICIENT0 0x1e
#define AK4671_FIL3_COEFFICIENT1 0x1f
#define AK4671_FIL3_COEFFICIENT2 0x20
#define AK4671_FIL3_COEFFICIENT3 0x21
#define AK4671_EQ_COEFFICIENT0 0x22
#define AK4671_EQ_COEFFICIENT1 0x23
#define AK4671_EQ_COEFFICIENT2 0x24
#define AK4671_EQ_COEFFICIENT3 0x25
#define AK4671_EQ_COEFFICIENT4 0x26
#define AK4671_EQ_COEFFICIENT5 0x27
#define AK4671_FIL1_COEFFICIENT0 0x28
#define AK4671_FIL1_COEFFICIENT1 0x29
#define AK4671_FIL1_COEFFICIENT2 0x2a
#define AK4671_FIL1_COEFFICIENT3 0x2b
#define AK4671_FIL2_COEFFICIENT0 0x2c
#define AK4671_FIL2_COEFFICIENT1 0x2d
#define AK4671_FIL2_COEFFICIENT2 0x2e
#define AK4671_FIL2_COEFFICIENT3 0x2f
#define AK4671_DIGITAL_FILTER_SELECT2 0x30
#define AK4671_E1_COEFFICIENT0 0x32
#define AK4671_E1_COEFFICIENT1 0x33
#define AK4671_E1_COEFFICIENT2 0x34
#define AK4671_E1_COEFFICIENT3 0x35
#define AK4671_E1_COEFFICIENT4 0x36
#define AK4671_E1_COEFFICIENT5 0x37
#define AK4671_E2_COEFFICIENT0 0x38
#define AK4671_E2_COEFFICIENT1 0x39
#define AK4671_E2_COEFFICIENT2 0x3a
#define AK4671_E2_COEFFICIENT3 0x3b
#define AK4671_E2_COEFFICIENT4 0x3c
#define AK4671_E2_COEFFICIENT5 0x3d
#define AK4671_E3_COEFFICIENT0 0x3e
#define AK4671_E3_COEFFICIENT1 0x3f
#define AK4671_E3_COEFFICIENT2 0x40
#define AK4671_E3_COEFFICIENT3 0x41
#define AK4671_E3_COEFFICIENT4 0x42
#define AK4671_E3_COEFFICIENT5 0x43
#define AK4671_E4_COEFFICIENT0 0x44
#define AK4671_E4_COEFFICIENT1 0x45
#define AK4671_E4_COEFFICIENT2 0x46
#define AK4671_E4_COEFFICIENT3 0x47
#define AK4671_E4_COEFFICIENT4 0x48
#define AK4671_E4_COEFFICIENT5 0x49
#define AK4671_E5_COEFFICIENT0 0x4a
#define AK4671_E5_COEFFICIENT1 0x4b
#define AK4671_E5_COEFFICIENT2 0x4c
#define AK4671_E5_COEFFICIENT3 0x4d
#define AK4671_E5_COEFFICIENT4 0x4e
#define AK4671_E5_COEFFICIENT5 0x4f
#define AK4671_EQ_CONTROL_250HZ_100HZ 0x50
#define AK4671_EQ_CONTROL_3500HZ_1KHZ 0x51
#define AK4671_EQ_CONTRO_10KHZ 0x52
#define AK4671_PCM_IF_CONTROL0 0x53
#define AK4671_PCM_IF_CONTROL1 0x54
#define AK4671_PCM_IF_CONTROL2 0x55
#define AK4671_DIGITAL_VOLUME_B_CONTROL 0x56
#define AK4671_DIGITAL_VOLUME_C_CONTROL 0x57
#define AK4671_SIDETONE_VOLUME_CONTROL 0x58
#define AK4671_DIGITAL_MIXING_CONTROL2 0x59
#define AK4671_SAR_ADC_CONTROL 0x5a
/* Bitfield Definitions */
/* AK4671_AD_DA_POWER_MANAGEMENT (0x00) Fields */
#define AK4671_PMVCM 0x01
/* AK4671_PLL_MODE_SELECT0 (0x01) Fields */
#define AK4671_PLL 0x0f
#define AK4671_PLL_11_2896MHZ (4 << 0)
#define AK4671_PLL_12_288MHZ (5 << 0)
#define AK4671_PLL_12MHZ (6 << 0)
#define AK4671_PLL_24MHZ (7 << 0)
#define AK4671_PLL_19_2MHZ (8 << 0)
#define AK4671_PLL_13_5MHZ (12 << 0)
#define AK4671_PLL_27MHZ (13 << 0)
#define AK4671_PLL_13MHZ (14 << 0)
#define AK4671_PLL_26MHZ (15 << 0)
#define AK4671_FS 0xf0
#define AK4671_FS_8KHZ (0 << 4)
#define AK4671_FS_12KHZ (1 << 4)
#define AK4671_FS_16KHZ (2 << 4)
#define AK4671_FS_24KHZ (3 << 4)
#define AK4671_FS_11_025KHZ (5 << 4)
#define AK4671_FS_22_05KHZ (7 << 4)
#define AK4671_FS_32KHZ (10 << 4)
#define AK4671_FS_48KHZ (11 << 4)
#define AK4671_FS_44_1KHZ (15 << 4)
/* AK4671_PLL_MODE_SELECT1 (0x02) Fields */
#define AK4671_PMPLL 0x01
#define AK4671_M_S 0x02
/* AK4671_FORMAT_SELECT (0x03) Fields */
#define AK4671_DIF 0x03
#define AK4671_DIF_DSP_MODE (0 << 0)
#define AK4671_DIF_MSB_MODE (2 << 0)
#define AK4671_DIF_I2S_MODE (3 << 0)
#define AK4671_BCKP 0x04
#define AK4671_MSBS 0x08
#define AK4671_SDOD 0x10
/* AK4671_LOUT2_POWER_MANAGEMENT (0x10) Fields */
#define AK4671_MUTEN 0x04
#endif

217
sound/soc/codecs/ak5386.c Normal file
View file

@ -0,0 +1,217 @@
/*
* ALSA SoC driver for
* Asahi Kasei AK5386 Single-ended 24-Bit 192kHz delta-sigma ADC
*
* (c) 2013 Daniel Mack <zonque@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/of_device.h>
#include <linux/regulator/consumer.h>
#include <sound/soc.h>
#include <sound/pcm.h>
#include <sound/initval.h>
static const char * const supply_names[] = {
"va", "vd"
};
struct ak5386_priv {
int reset_gpio;
struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)];
};
static const struct snd_soc_dapm_widget ak5386_dapm_widgets[] = {
SND_SOC_DAPM_INPUT("AINL"),
SND_SOC_DAPM_INPUT("AINR"),
};
static const struct snd_soc_dapm_route ak5386_dapm_routes[] = {
{ "Capture", NULL, "AINL" },
{ "Capture", NULL, "AINR" },
};
static int ak5386_soc_probe(struct snd_soc_codec *codec)
{
struct ak5386_priv *priv = snd_soc_codec_get_drvdata(codec);
return regulator_bulk_enable(ARRAY_SIZE(priv->supplies), priv->supplies);
}
static int ak5386_soc_remove(struct snd_soc_codec *codec)
{
struct ak5386_priv *priv = snd_soc_codec_get_drvdata(codec);
regulator_bulk_disable(ARRAY_SIZE(priv->supplies), priv->supplies);
return 0;
}
#ifdef CONFIG_PM
static int ak5386_soc_suspend(struct snd_soc_codec *codec)
{
struct ak5386_priv *priv = snd_soc_codec_get_drvdata(codec);
regulator_bulk_disable(ARRAY_SIZE(priv->supplies), priv->supplies);
return 0;
}
static int ak5386_soc_resume(struct snd_soc_codec *codec)
{
struct ak5386_priv *priv = snd_soc_codec_get_drvdata(codec);
return regulator_bulk_enable(ARRAY_SIZE(priv->supplies), priv->supplies);
}
#else
#define ak5386_soc_suspend NULL
#define ak5386_soc_resume NULL
#endif /* CONFIG_PM */
static struct snd_soc_codec_driver soc_codec_ak5386 = {
.probe = ak5386_soc_probe,
.remove = ak5386_soc_remove,
.suspend = ak5386_soc_suspend,
.resume = ak5386_soc_resume,
.dapm_widgets = ak5386_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(ak5386_dapm_widgets),
.dapm_routes = ak5386_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(ak5386_dapm_routes),
};
static int ak5386_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int format)
{
struct snd_soc_codec *codec = codec_dai->codec;
format &= SND_SOC_DAIFMT_FORMAT_MASK;
if (format != SND_SOC_DAIFMT_LEFT_J &&
format != SND_SOC_DAIFMT_I2S) {
dev_err(codec->dev, "Invalid DAI format\n");
return -EINVAL;
}
return 0;
}
static int ak5386_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
struct ak5386_priv *priv = snd_soc_codec_get_drvdata(codec);
/*
* From the datasheet:
*
* All external clocks (MCLK, SCLK and LRCK) must be present unless
* PDN pin = L. If these clocks are not provided, the AK5386 may
* draw excess current due to its use of internal dynamically
* refreshed logic. If the external clocks are not present, place
* the AK5386 in power-down mode (PDN pin = L).
*/
if (gpio_is_valid(priv->reset_gpio))
gpio_set_value(priv->reset_gpio, 1);
return 0;
}
static int ak5386_hw_free(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
struct ak5386_priv *priv = snd_soc_codec_get_drvdata(codec);
if (gpio_is_valid(priv->reset_gpio))
gpio_set_value(priv->reset_gpio, 0);
return 0;
}
static const struct snd_soc_dai_ops ak5386_dai_ops = {
.set_fmt = ak5386_set_dai_fmt,
.hw_params = ak5386_hw_params,
.hw_free = ak5386_hw_free,
};
static struct snd_soc_dai_driver ak5386_dai = {
.name = "ak5386-hifi",
.capture = {
.stream_name = "Capture",
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_192000,
.formats = SNDRV_PCM_FMTBIT_S8 |
SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE |
SNDRV_PCM_FMTBIT_S24_3LE,
},
.ops = &ak5386_dai_ops,
};
#ifdef CONFIG_OF
static const struct of_device_id ak5386_dt_ids[] = {
{ .compatible = "asahi-kasei,ak5386", },
{ }
};
MODULE_DEVICE_TABLE(of, ak5386_dt_ids);
#endif
static int ak5386_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct ak5386_priv *priv;
int ret, i;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->reset_gpio = -EINVAL;
dev_set_drvdata(dev, priv);
for (i = 0; i < ARRAY_SIZE(supply_names); i++)
priv->supplies[i].supply = supply_names[i];
ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(priv->supplies),
priv->supplies);
if (ret < 0)
return ret;
if (of_match_device(of_match_ptr(ak5386_dt_ids), dev))
priv->reset_gpio = of_get_named_gpio(dev->of_node,
"reset-gpio", 0);
if (gpio_is_valid(priv->reset_gpio))
if (devm_gpio_request_one(dev, priv->reset_gpio,
GPIOF_OUT_INIT_LOW,
"AK5386 Reset"))
priv->reset_gpio = -EINVAL;
return snd_soc_register_codec(dev, &soc_codec_ak5386,
&ak5386_dai, 1);
}
static int ak5386_remove(struct platform_device *pdev)
{
snd_soc_unregister_codec(&pdev->dev);
return 0;
}
static struct platform_driver ak5386_driver = {
.probe = ak5386_probe,
.remove = ak5386_remove,
.driver = {
.name = "ak5386",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(ak5386_dt_ids),
},
};
module_platform_driver(ak5386_driver);
MODULE_DESCRIPTION("ASoC driver for AK5386 ADC");
MODULE_AUTHOR("Daniel Mack <zonque@gmail.com>");
MODULE_LICENSE("GPL");

1119
sound/soc/codecs/alc5623.c Normal file

File diff suppressed because it is too large Load diff

161
sound/soc/codecs/alc5623.h Normal file
View file

@ -0,0 +1,161 @@
/*
* alc5623.h -- alc562[123] ALSA Soc Audio driver
*
* Copyright 2008 Realtek Microelectronics
* Copyright 2010 Arnaud Patard <arnaud.patard@rtp-net.org>
*
* Author: flove <flove@realtek.com>
* Arnaud Patard <arnaud.patard@rtp-net.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#ifndef _ALC5623_H
#define _ALC5623_H
#define ALC5623_RESET 0x00
/* 5621 5622 5623 */
/* speaker output vol 2 2 */
/* line output vol 4 2 */
/* HP output vol 4 0 4 */
#define ALC5623_SPK_OUT_VOL 0x02
#define ALC5623_HP_OUT_VOL 0x04
#define ALC5623_MONO_AUX_OUT_VOL 0x06
#define ALC5623_AUXIN_VOL 0x08
#define ALC5623_LINE_IN_VOL 0x0A
#define ALC5623_STEREO_DAC_VOL 0x0C
#define ALC5623_MIC_VOL 0x0E
#define ALC5623_MIC_ROUTING_CTRL 0x10
#define ALC5623_ADC_REC_GAIN 0x12
#define ALC5623_ADC_REC_MIXER 0x14
#define ALC5623_SOFT_VOL_CTRL_TIME 0x16
/* ALC5623_OUTPUT_MIXER_CTRL : */
/* same remark as for reg 2 line vs speaker */
#define ALC5623_OUTPUT_MIXER_CTRL 0x1C
#define ALC5623_MIC_CTRL 0x22
#define ALC5623_DAI_CONTROL 0x34
#define ALC5623_DAI_SDP_MASTER_MODE (0 << 15)
#define ALC5623_DAI_SDP_SLAVE_MODE (1 << 15)
#define ALC5623_DAI_I2S_PCM_MODE (1 << 14)
#define ALC5623_DAI_MAIN_I2S_BCLK_POL_CTRL (1 << 7)
#define ALC5623_DAI_ADC_DATA_L_R_SWAP (1 << 5)
#define ALC5623_DAI_DAC_DATA_L_R_SWAP (1 << 4)
#define ALC5623_DAI_I2S_DL_MASK (3 << 2)
#define ALC5623_DAI_I2S_DL_32 (3 << 2)
#define ALC5623_DAI_I2S_DL_24 (2 << 2)
#define ALC5623_DAI_I2S_DL_20 (1 << 2)
#define ALC5623_DAI_I2S_DL_16 (0 << 2)
#define ALC5623_DAI_I2S_DF_PCM (3 << 0)
#define ALC5623_DAI_I2S_DF_LEFT (2 << 0)
#define ALC5623_DAI_I2S_DF_RIGHT (1 << 0)
#define ALC5623_DAI_I2S_DF_I2S (0 << 0)
#define ALC5623_STEREO_AD_DA_CLK_CTRL 0x36
#define ALC5623_COMPANDING_CTRL 0x38
#define ALC5623_PWR_MANAG_ADD1 0x3A
#define ALC5623_PWR_ADD1_MAIN_I2S_EN (1 << 15)
#define ALC5623_PWR_ADD1_ZC_DET_PD_EN (1 << 14)
#define ALC5623_PWR_ADD1_MIC1_BIAS_EN (1 << 11)
#define ALC5623_PWR_ADD1_SHORT_CURR_DET_EN (1 << 10)
#define ALC5623_PWR_ADD1_SOFTGEN_EN (1 << 8) /* rsvd on 5622 */
#define ALC5623_PWR_ADD1_DEPOP_BUF_HP (1 << 6) /* rsvd on 5622 */
#define ALC5623_PWR_ADD1_HP_OUT_AMP (1 << 5)
#define ALC5623_PWR_ADD1_HP_OUT_ENH_AMP (1 << 4) /* rsvd on 5622 */
#define ALC5623_PWR_ADD1_DEPOP_BUF_AUX (1 << 2)
#define ALC5623_PWR_ADD1_AUX_OUT_AMP (1 << 1)
#define ALC5623_PWR_ADD1_AUX_OUT_ENH_AMP (1 << 0) /* rsvd on 5622 */
#define ALC5623_PWR_MANAG_ADD2 0x3C
#define ALC5623_PWR_ADD2_LINEOUT (1 << 15) /* rt5623 */
#define ALC5623_PWR_ADD2_CLASS_AB (1 << 15) /* rt5621 */
#define ALC5623_PWR_ADD2_CLASS_D (1 << 14) /* rt5621 */
#define ALC5623_PWR_ADD2_VREF (1 << 13)
#define ALC5623_PWR_ADD2_PLL (1 << 12)
#define ALC5623_PWR_ADD2_DAC_REF_CIR (1 << 10)
#define ALC5623_PWR_ADD2_L_DAC_CLK (1 << 9)
#define ALC5623_PWR_ADD2_R_DAC_CLK (1 << 8)
#define ALC5623_PWR_ADD2_L_ADC_CLK_GAIN (1 << 7)
#define ALC5623_PWR_ADD2_R_ADC_CLK_GAIN (1 << 6)
#define ALC5623_PWR_ADD2_L_HP_MIXER (1 << 5)
#define ALC5623_PWR_ADD2_R_HP_MIXER (1 << 4)
#define ALC5623_PWR_ADD2_SPK_MIXER (1 << 3)
#define ALC5623_PWR_ADD2_MONO_MIXER (1 << 2)
#define ALC5623_PWR_ADD2_L_ADC_REC_MIXER (1 << 1)
#define ALC5623_PWR_ADD2_R_ADC_REC_MIXER (1 << 0)
#define ALC5623_PWR_MANAG_ADD3 0x3E
#define ALC5623_PWR_ADD3_MAIN_BIAS (1 << 15)
#define ALC5623_PWR_ADD3_AUXOUT_L_VOL_AMP (1 << 14)
#define ALC5623_PWR_ADD3_AUXOUT_R_VOL_AMP (1 << 13)
#define ALC5623_PWR_ADD3_SPK_OUT (1 << 12)
#define ALC5623_PWR_ADD3_HP_L_OUT_VOL (1 << 10)
#define ALC5623_PWR_ADD3_HP_R_OUT_VOL (1 << 9)
#define ALC5623_PWR_ADD3_LINEIN_L_VOL (1 << 7)
#define ALC5623_PWR_ADD3_LINEIN_R_VOL (1 << 6)
#define ALC5623_PWR_ADD3_AUXIN_L_VOL (1 << 5)
#define ALC5623_PWR_ADD3_AUXIN_R_VOL (1 << 4)
#define ALC5623_PWR_ADD3_MIC1_FUN_CTRL (1 << 3)
#define ALC5623_PWR_ADD3_MIC2_FUN_CTRL (1 << 2)
#define ALC5623_PWR_ADD3_MIC1_BOOST_AD (1 << 1)
#define ALC5623_PWR_ADD3_MIC2_BOOST_AD (1 << 0)
#define ALC5623_ADD_CTRL_REG 0x40
#define ALC5623_GLOBAL_CLK_CTRL_REG 0x42
#define ALC5623_GBL_CLK_SYS_SOUR_SEL_PLL (1 << 15)
#define ALC5623_GBL_CLK_SYS_SOUR_SEL_MCLK (0 << 15)
#define ALC5623_GBL_CLK_PLL_SOUR_SEL_BITCLK (1 << 14)
#define ALC5623_GBL_CLK_PLL_SOUR_SEL_MCLK (0 << 14)
#define ALC5623_GBL_CLK_PLL_DIV_RATIO_DIV8 (3 << 1)
#define ALC5623_GBL_CLK_PLL_DIV_RATIO_DIV4 (2 << 1)
#define ALC5623_GBL_CLK_PLL_DIV_RATIO_DIV2 (1 << 1)
#define ALC5623_GBL_CLK_PLL_DIV_RATIO_DIV1 (0 << 1)
#define ALC5623_GBL_CLK_PLL_PRE_DIV2 (1 << 0)
#define ALC5623_GBL_CLK_PLL_PRE_DIV1 (0 << 0)
#define ALC5623_PLL_CTRL 0x44
#define ALC5623_PLL_CTRL_N_VAL(n) (((n)&0xff) << 8)
#define ALC5623_PLL_CTRL_K_VAL(k) (((k)&0x7) << 4)
#define ALC5623_PLL_CTRL_M_VAL(m) ((m)&0xf)
#define ALC5623_GPIO_OUTPUT_PIN_CTRL 0x4A
#define ALC5623_GPIO_PIN_CONFIG 0x4C
#define ALC5623_GPIO_PIN_POLARITY 0x4E
#define ALC5623_GPIO_PIN_STICKY 0x50
#define ALC5623_GPIO_PIN_WAKEUP 0x52
#define ALC5623_GPIO_PIN_STATUS 0x54
#define ALC5623_GPIO_PIN_SHARING 0x56
#define ALC5623_OVER_CURR_STATUS 0x58
#define ALC5623_JACK_DET_CTRL 0x5A
#define ALC5623_MISC_CTRL 0x5E
#define ALC5623_MISC_DISABLE_FAST_VREG (1 << 15)
#define ALC5623_MISC_SPK_CLASS_AB_OC_PD (1 << 13) /* 5621 */
#define ALC5623_MISC_SPK_CLASS_AB_OC_DET (1 << 12) /* 5621 */
#define ALC5623_MISC_HP_DEPOP_MODE3_EN (1 << 10)
#define ALC5623_MISC_HP_DEPOP_MODE2_EN (1 << 9)
#define ALC5623_MISC_HP_DEPOP_MODE1_EN (1 << 8)
#define ALC5623_MISC_AUXOUT_DEPOP_MODE3_EN (1 << 6)
#define ALC5623_MISC_AUXOUT_DEPOP_MODE2_EN (1 << 5)
#define ALC5623_MISC_AUXOUT_DEPOP_MODE1_EN (1 << 4)
#define ALC5623_MISC_M_DAC_L_INPUT (1 << 3)
#define ALC5623_MISC_M_DAC_R_INPUT (1 << 2)
#define ALC5623_MISC_IRQOUT_INV_CTRL (1 << 0)
#define ALC5623_PSEDUEO_SPATIAL_CTRL 0x60
#define ALC5623_EQ_CTRL 0x62
#define ALC5623_EQ_MODE_ENABLE 0x66
#define ALC5623_AVC_CTRL 0x68
#define ALC5623_HID_CTRL_INDEX 0x6A
#define ALC5623_HID_CTRL_DATA 0x6C
#define ALC5623_VENDOR_ID1 0x7C
#define ALC5623_VENDOR_ID2 0x7E
#define ALC5623_PLL_FR_MCLK 0
#define ALC5623_PLL_FR_BCK 1
#endif

1215
sound/soc/codecs/alc5632.c Normal file

File diff suppressed because it is too large Load diff

252
sound/soc/codecs/alc5632.h Normal file
View file

@ -0,0 +1,252 @@
/*
* alc5632.h -- ALC5632 ALSA SoC Audio Codec
*
* Copyright (C) 2011 The AC100 Kernel Team <ac100@lists.lauchpad.net>
*
* Authors: Leon Romanovsky <leon@leon.nu>
* Andrey Danin <danindrey@mail.ru>
* Ilya Petrov <ilya.muromec@gmail.com>
* Marc Dietrich <marvin24@gmx.de>
*
* Based on alc5623.h by Arnaud Patard
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef _ALC5632_H
#define _ALC5632_H
#define ALC5632_RESET 0x00
/* speaker output vol 2 2 */
/* line output vol 4 2 */
/* HP output vol 4 0 4 */
#define ALC5632_SPK_OUT_VOL 0x02 /* spe out vol */
#define ALC5632_SPK_OUT_VOL_STEP 1.5
#define ALC5632_HP_OUT_VOL 0x04 /* hp out vol */
#define ALC5632_AUX_OUT_VOL 0x06 /* aux out vol */
#define ALC5632_PHONE_IN_VOL 0x08 /* phone in vol */
#define ALC5632_LINE_IN_VOL 0x0A /* line in vol */
#define ALC5632_STEREO_DAC_IN_VOL 0x0C /* stereo dac in vol */
#define ALC5632_MIC_VOL 0x0E /* mic in vol */
/* stero dac/mic routing */
#define ALC5632_MIC_ROUTING_CTRL 0x10
#define ALC5632_MIC_ROUTE_MONOMIX (1 << 0)
#define ALC5632_MIC_ROUTE_SPK (1 << 1)
#define ALC5632_MIC_ROUTE_HP (1 << 2)
#define ALC5632_ADC_REC_GAIN 0x12 /* rec gain */
#define ALC5632_ADC_REC_GAIN_RANGE 0x1F1F
#define ALC5632_ADC_REC_GAIN_BASE (-16.5)
#define ALC5632_ADC_REC_GAIN_STEP 1.5
#define ALC5632_ADC_REC_MIXER 0x14 /* mixer control */
#define ALC5632_ADC_REC_MIC1 (1 << 6)
#define ALC5632_ADC_REC_MIC2 (1 << 5)
#define ALC5632_ADC_REC_LINE_IN (1 << 4)
#define ALC5632_ADC_REC_AUX (1 << 3)
#define ALC5632_ADC_REC_HP (1 << 2)
#define ALC5632_ADC_REC_SPK (1 << 1)
#define ALC5632_ADC_REC_MONOMIX (1 << 0)
#define ALC5632_VOICE_DAC_VOL 0x18 /* voice dac vol */
#define ALC5632_I2S_OUT_CTL 0x1A /* undocumented reg. found in path scheme */
/* ALC5632_OUTPUT_MIXER_CTRL : */
/* same remark as for reg 2 line vs speaker */
#define ALC5632_OUTPUT_MIXER_CTRL 0x1C /* out mix ctrl */
#define ALC5632_OUTPUT_MIXER_RP (1 << 14)
#define ALC5632_OUTPUT_MIXER_WEEK (1 << 12)
#define ALC5632_OUTPUT_MIXER_HP (1 << 10)
#define ALC5632_OUTPUT_MIXER_AUX_SPK (2 << 6)
#define ALC5632_OUTPUT_MIXER_AUX_HP_LR (1 << 6)
#define ALC5632_OUTPUT_MIXER_HP_R (1 << 8)
#define ALC5632_OUTPUT_MIXER_HP_L (1 << 9)
#define ALC5632_MIC_CTRL 0x22 /* mic phone ctrl */
#define ALC5632_MIC_BOOST_BYPASS 0
#define ALC5632_MIC_BOOST_20DB 1
#define ALC5632_MIC_BOOST_30DB 2
#define ALC5632_MIC_BOOST_40DB 3
#define ALC5632_DIGI_BOOST_CTRL 0x24 /* digi mic / bost ctl */
#define ALC5632_MIC_BOOST_RANGE 7
#define ALC5632_MIC_BOOST_STEP 6
#define ALC5632_PWR_DOWN_CTRL_STATUS 0x26
#define ALC5632_PWR_DOWN_CTRL_STATUS_MASK 0xEF00
#define ALC5632_PWR_VREF_PR3 (1 << 11)
#define ALC5632_PWR_VREF_PR2 (1 << 10)
#define ALC5632_PWR_VREF_STATUS (1 << 3)
#define ALC5632_PWR_AMIX_STATUS (1 << 2)
#define ALC5632_PWR_DAC_STATUS (1 << 1)
#define ALC5632_PWR_ADC_STATUS (1 << 0)
/* stereo/voice DAC / stereo adc func ctrl */
#define ALC5632_DAC_FUNC_SELECT 0x2E
/* Main serial data port ctrl (i2s) */
#define ALC5632_DAI_CONTROL 0x34
#define ALC5632_DAI_SDP_MASTER_MODE (0 << 15)
#define ALC5632_DAI_SDP_SLAVE_MODE (1 << 15)
#define ALC5632_DAI_SADLRCK_MODE (1 << 14)
/* 0:voice, 1:main */
#define ALC5632_DAI_MAIN_I2S_SYSCLK_SEL (1 << 8)
#define ALC5632_DAI_MAIN_I2S_BCLK_POL_CTRL (1 << 7)
/* 0:normal, 1:invert */
#define ALC5632_DAI_MAIN_I2S_LRCK_INV (1 << 6)
#define ALC5632_DAI_I2S_DL_MASK (3 << 2)
#define ALC5632_DAI_I2S_DL_8 (3 << 2)
#define ALC5632_DAI_I2S_DL_24 (2 << 2)
#define ALC5632_DAI_I2S_DL_20 (1 << 2)
#define ALC5632_DAI_I2S_DL_16 (0 << 2)
#define ALC5632_DAI_I2S_DF_MASK (3 << 0)
#define ALC5632_DAI_I2S_DF_PCM_B (3 << 0)
#define ALC5632_DAI_I2S_DF_PCM_A (2 << 0)
#define ALC5632_DAI_I2S_DF_LEFT (1 << 0)
#define ALC5632_DAI_I2S_DF_I2S (0 << 0)
/* extend serial data port control (VoDAC_i2c/pcm) */
#define ALC5632_DAI_CONTROL2 0x36
/* 0:gpio func, 1:voice pcm */
#define ALC5632_DAI_VOICE_PCM_ENABLE (1 << 15)
/* 0:master, 1:slave */
#define ALC5632_DAI_VOICE_MODE_SEL (1 << 14)
/* 0:disable, 1:enable */
#define ALC5632_DAI_HPF_CLK_CTRL (1 << 13)
/* 0:main, 1:voice */
#define ALC5632_DAI_VOICE_I2S_SYSCLK_SEL (1 << 8)
/* 0:normal, 1:invert */
#define ALC5632_DAI_VOICE_VBCLK_SYSCLK_SEL (1 << 7)
/* 0:normal, 1:invert */
#define ALC5632_DAI_VOICE_I2S_LR_INV (1 << 6)
#define ALC5632_DAI_VOICE_DL_MASK (3 << 2)
#define ALC5632_DAI_VOICE_DL_16 (0 << 2)
#define ALC5632_DAI_VOICE_DL_20 (1 << 2)
#define ALC5632_DAI_VOICE_DL_24 (2 << 2)
#define ALC5632_DAI_VOICE_DL_8 (3 << 2)
#define ALC5632_DAI_VOICE_DF_MASK (3 << 0)
#define ALC5632_DAI_VOICE_DF_I2S (0 << 0)
#define ALC5632_DAI_VOICE_DF_LEFT (1 << 0)
#define ALC5632_DAI_VOICE_DF_PCM_A (2 << 0)
#define ALC5632_DAI_VOICE_DF_PCM_B (3 << 0)
#define ALC5632_PWR_MANAG_ADD1 0x3A
#define ALC5632_PWR_MANAG_ADD1_MASK 0xEFFF
#define ALC5632_PWR_ADD1_DAC_L_EN (1 << 15)
#define ALC5632_PWR_ADD1_DAC_R_EN (1 << 14)
#define ALC5632_PWR_ADD1_ZERO_CROSS (1 << 13)
#define ALC5632_PWR_ADD1_MAIN_I2S_EN (1 << 11)
#define ALC5632_PWR_ADD1_SPK_AMP_EN (1 << 10)
#define ALC5632_PWR_ADD1_HP_OUT_AMP (1 << 9)
#define ALC5632_PWR_ADD1_HP_OUT_ENH_AMP (1 << 8)
#define ALC5632_PWR_ADD1_VOICE_DAC_MIX (1 << 7)
#define ALC5632_PWR_ADD1_SOFTGEN_EN (1 << 6)
#define ALC5632_PWR_ADD1_MIC1_SHORT_CURR (1 << 5)
#define ALC5632_PWR_ADD1_MIC2_SHORT_CURR (1 << 4)
#define ALC5632_PWR_ADD1_MIC1_EN (1 << 3)
#define ALC5632_PWR_ADD1_MIC2_EN (1 << 2)
#define ALC5632_PWR_ADD1_MAIN_BIAS (1 << 1)
#define ALC5632_PWR_ADD1_DAC_REF (1 << 0)
#define ALC5632_PWR_MANAG_ADD2 0x3C
#define ALC5632_PWR_MANAG_ADD2_MASK 0x7FFF
#define ALC5632_PWR_ADD2_PLL1 (1 << 15)
#define ALC5632_PWR_ADD2_PLL2 (1 << 14)
#define ALC5632_PWR_ADD2_VREF (1 << 13)
#define ALC5632_PWR_ADD2_OVT_DET (1 << 12)
#define ALC5632_PWR_ADD2_VOICE_DAC (1 << 10)
#define ALC5632_PWR_ADD2_L_DAC_CLK (1 << 9)
#define ALC5632_PWR_ADD2_R_DAC_CLK (1 << 8)
#define ALC5632_PWR_ADD2_L_ADC_CLK_GAIN (1 << 7)
#define ALC5632_PWR_ADD2_R_ADC_CLK_GAIN (1 << 6)
#define ALC5632_PWR_ADD2_L_HP_MIXER (1 << 5)
#define ALC5632_PWR_ADD2_R_HP_MIXER (1 << 4)
#define ALC5632_PWR_ADD2_SPK_MIXER (1 << 3)
#define ALC5632_PWR_ADD2_MONO_MIXER (1 << 2)
#define ALC5632_PWR_ADD2_L_ADC_REC_MIXER (1 << 1)
#define ALC5632_PWR_ADD2_R_ADC_REC_MIXER (1 << 0)
#define ALC5632_PWR_MANAG_ADD3 0x3E
#define ALC5632_PWR_MANAG_ADD3_MASK 0x7CFF
#define ALC5632_PWR_ADD3_AUXOUT_VOL (1 << 14)
#define ALC5632_PWR_ADD3_SPK_L_OUT (1 << 13)
#define ALC5632_PWR_ADD3_SPK_R_OUT (1 << 12)
#define ALC5632_PWR_ADD3_HP_L_OUT_VOL (1 << 11)
#define ALC5632_PWR_ADD3_HP_R_OUT_VOL (1 << 10)
#define ALC5632_PWR_ADD3_LINEIN_L_VOL (1 << 7)
#define ALC5632_PWR_ADD3_LINEIN_R_VOL (1 << 6)
#define ALC5632_PWR_ADD3_AUXIN_VOL (1 << 5)
#define ALC5632_PWR_ADD3_AUXIN_MIX (1 << 4)
#define ALC5632_PWR_ADD3_MIC1_VOL (1 << 3)
#define ALC5632_PWR_ADD3_MIC2_VOL (1 << 2)
#define ALC5632_PWR_ADD3_MIC1_BOOST_AD (1 << 1)
#define ALC5632_PWR_ADD3_MIC2_BOOST_AD (1 << 0)
#define ALC5632_GPCR1 0x40
#define ALC5632_GPCR1_CLK_SYS_SRC_SEL_PLL1 (1 << 15)
#define ALC5632_GPCR1_CLK_SYS_SRC_SEL_MCLK (0 << 15)
#define ALC5632_GPCR1_DAC_HI_FLT_EN (1 << 10)
#define ALC5632_GPCR1_SPK_AMP_CTRL (7 << 1)
#define ALC5632_GPCR1_VDD_100 (5 << 1)
#define ALC5632_GPCR1_VDD_125 (4 << 1)
#define ALC5632_GPCR1_VDD_150 (3 << 1)
#define ALC5632_GPCR1_VDD_175 (2 << 1)
#define ALC5632_GPCR1_VDD_200 (1 << 1)
#define ALC5632_GPCR1_VDD_225 (0 << 1)
#define ALC5632_GPCR2 0x42
#define ALC5632_GPCR2_PLL1_SOUR_SEL (3 << 12)
#define ALC5632_PLL_FR_MCLK (0 << 12)
#define ALC5632_PLL_FR_BCLK (2 << 12)
#define ALC5632_PLL_FR_VBCLK (3 << 12)
#define ALC5632_GPCR2_CLK_PLL_PRE_DIV1 (0 << 0)
#define ALC5632_PLL1_CTRL 0x44
#define ALC5632_PLL1_CTRL_N_VAL(n) (((n) & 0x0f) << 8)
#define ALC5632_PLL1_M_BYPASS (1 << 7)
#define ALC5632_PLL1_CTRL_K_VAL(k) (((k) & 0x07) << 4)
#define ALC5632_PLL1_CTRL_M_VAL(m) (((m) & 0x0f) << 0)
#define ALC5632_PLL2_CTRL 0x46
#define ALC5632_PLL2_EN (1 << 15)
#define ALC5632_PLL2_RATIO (0 << 15)
#define ALC5632_GPIO_PIN_CONFIG 0x4C
#define ALC5632_GPIO_PIN_POLARITY 0x4E
#define ALC5632_GPIO_PIN_STICKY 0x50
#define ALC5632_GPIO_PIN_WAKEUP 0x52
#define ALC5632_GPIO_PIN_STATUS 0x54
#define ALC5632_GPIO_PIN_SHARING 0x56
#define ALC5632_OVER_CURR_STATUS 0x58
#define ALC5632_SOFTVOL_CTRL 0x5A
#define ALC5632_GPIO_OUPUT_PIN_CTRL 0x5C
#define ALC5632_MISC_CTRL 0x5E
#define ALC5632_MISC_DISABLE_FAST_VREG (1 << 15)
#define ALC5632_MISC_AVC_TRGT_SEL (3 << 12)
#define ALC5632_MISC_AVC_TRGT_RIGHT (1 << 12)
#define ALC5632_MISC_AVC_TRGT_LEFT (2 << 12)
#define ALC5632_MISC_AVC_TRGT_BOTH (3 << 12)
#define ALC5632_MISC_HP_DEPOP_MODE1_EN (1 << 9)
#define ALC5632_MISC_HP_DEPOP_MODE2_EN (1 << 8)
#define ALC5632_MISC_HP_DEPOP_MUTE_L (1 << 7)
#define ALC5632_MISC_HP_DEPOP_MUTE_R (1 << 6)
#define ALC5632_MISC_HP_DEPOP_MUTE (1 << 5)
#define ALC5632_MISC_GPIO_WAKEUP_CTRL (1 << 1)
#define ALC5632_MISC_IRQOUT_INV_CTRL (1 << 0)
#define ALC5632_DAC_CLK_CTRL1 0x60
#define ALC5632_DAC_CLK_CTRL2 0x62
#define ALC5632_DAC_CLK_CTRL2_DIV1_2 (1 << 0)
#define ALC5632_VOICE_DAC_PCM_CLK_CTRL1 0x64
#define ALC5632_PSEUDO_SPATIAL_CTRL 0x68
#define ALC5632_HID_CTRL_INDEX 0x6A
#define ALC5632_HID_CTRL_DATA 0x6C
#define ALC5632_EQ_CTRL 0x6E
/* undocumented */
#define ALC5632_VENDOR_ID1 0x7C
#define ALC5632_VENDOR_ID2 0x7E
#define ALC5632_MAX_REGISTER 0x7E
#endif

5340
sound/soc/codecs/arizona.c Normal file

File diff suppressed because it is too large Load diff

500
sound/soc/codecs/arizona.h Normal file
View file

@ -0,0 +1,500 @@
/*
* arizona.h - Wolfson Arizona class device shared support
*
* Copyright 2012 Wolfson Microelectronics plc
*
* Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef _ASOC_ARIZONA_H
#define _ASOC_ARIZONA_H
#include <linux/completion.h>
#include <sound/soc.h>
#include "wm_adsp.h"
#define ARIZONA_CLK_SYSCLK 1
#define ARIZONA_CLK_ASYNCCLK 2
#define ARIZONA_CLK_OPCLK 3
#define ARIZONA_CLK_ASYNC_OPCLK 4
#define ARIZONA_CLK_SYSCLK_2 5
#define ARIZONA_CLK_SYSCLK_3 6
#define ARIZONA_CLK_ASYNCCLK_2 7
#define ARIZONA_CLK_DSPCLK 8
#define ARIZONA_CLK_SRC_MCLK1 0x0
#define ARIZONA_CLK_SRC_MCLK2 0x1
#define ARIZONA_CLK_SRC_FLL1 0x4
#define ARIZONA_CLK_SRC_FLL2 0x5
#define ARIZONA_CLK_SRC_FLL3 0x6
#define ARIZONA_CLK_SRC_FLLAO_HI 0x7
#define CLEARWATER_CLK_SRC_FLL1_DIV6 0x7
#define ARIZONA_CLK_SRC_AIF1BCLK 0x8
#define ARIZONA_CLK_SRC_AIF2BCLK 0x9
#define ARIZONA_CLK_SRC_AIF3BCLK 0xa
#define ARIZONA_CLK_SRC_AIF4BCLK 0xb
#define ARIZONA_CLK_SRC_FLLAO 0xF
#define ARIZONA_FLL_SRC_NONE -1
#define ARIZONA_FLL_SRC_MCLK1 0
#define ARIZONA_FLL_SRC_MCLK2 1
#define ARIZONA_FLL_SRC_SLIMCLK 3
#define ARIZONA_FLL_SRC_FLL1 4
#define ARIZONA_FLL_SRC_FLL2 5
#define ARIZONA_FLL_SRC_AIF1BCLK 8
#define ARIZONA_FLL_SRC_AIF2BCLK 9
#define ARIZONA_FLL_SRC_AIF3BCLK 10
#define ARIZONA_FLL_SRC_AIF4BCLK 11
#define ARIZONA_FLL_SRC_AIF1LRCLK 12
#define ARIZONA_FLL_SRC_AIF2LRCLK 13
#define ARIZONA_FLL_SRC_AIF3LRCLK 14
#define ARIZONA_FLL_SRC_AIF4LRCLK 15
#define ARIZONA_MIXER_VOL_MASK 0x00FE
#define ARIZONA_MIXER_VOL_SHIFT 1
#define ARIZONA_MIXER_VOL_WIDTH 7
#define ARIZONA_CLK_6MHZ 0
#define ARIZONA_CLK_12MHZ 1
#define ARIZONA_CLK_24MHZ 2
#define ARIZONA_CLK_49MHZ 3
#define ARIZONA_CLK_73MHZ 4
#define CLEARWATER_CLK_98MHZ 4
#define ARIZONA_CLK_98MHZ 5
#define ARIZONA_CLK_147MHZ 6
#define CLEARWATER_DSP_CLK_9MHZ 0
#define CLEARWATER_DSP_CLK_18MHZ 1
#define CLEARWATER_DSP_CLK_36MHZ 2
#define CLEARWATER_DSP_CLK_73MHZ 3
#define CLEARWATER_DSP_CLK_147MHZ 4
#define ARIZONA_MAX_DAI 11
#define ARIZONA_MAX_ADSP 7
#define ARIZONA_DVFS_SR1_RQ 0x001
#define ARIZONA_DVFS_SR2_RQ 0x002
#define ARIZONA_DVFS_SR3_RQ 0x004
#define ARIZONA_DVFS_ASR1_RQ 0x008
#define ARIZONA_DVFS_ASR2_RQ 0x010
#define ARIZONA_DVFS_ADSP1_RQ 0x100
struct arizona;
struct wm_adsp;
struct arizona_jd_state;
struct arizona_dai_priv {
int clk;
};
struct arizona_priv {
struct wm_adsp adsp[ARIZONA_MAX_ADSP];
struct arizona *arizona;
int sysclk;
int asyncclk;
int dspclk;
struct arizona_dai_priv dai[ARIZONA_MAX_DAI];
int num_inputs;
unsigned int in_pending;
unsigned int out_up_pending;
unsigned int out_up_delay;
unsigned int out_down_pending;
unsigned int out_down_delay;
unsigned int spk_mute_cache;
unsigned int spk_thr2_cache;
unsigned int dvfs_reqs;
struct mutex dvfs_lock;
bool dvfs_cached;
};
#define ARIZONA_NUM_MIXER_INPUTS 134
#define ARIZONA_V2_NUM_MIXER_INPUTS 146
extern const unsigned int arizona_mixer_tlv[];
extern const char * const arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS];
extern unsigned int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS];
extern const char * const arizona_v2_mixer_texts[ARIZONA_V2_NUM_MIXER_INPUTS];
extern unsigned int arizona_v2_mixer_values[ARIZONA_V2_NUM_MIXER_INPUTS];
#define ARIZONA_GAINMUX_CONTROLS(name, base) \
SOC_SINGLE_RANGE_TLV(name " Input Volume", base + 1, \
ARIZONA_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \
arizona_mixer_tlv)
#define ARIZONA_MIXER_CONTROLS(name, base) \
SOC_SINGLE_RANGE_TLV(name " Input 1 Volume", base + 1, \
ARIZONA_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \
arizona_mixer_tlv), \
SOC_SINGLE_RANGE_TLV(name " Input 2 Volume", base + 3, \
ARIZONA_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \
arizona_mixer_tlv), \
SOC_SINGLE_RANGE_TLV(name " Input 3 Volume", base + 5, \
ARIZONA_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \
arizona_mixer_tlv), \
SOC_SINGLE_RANGE_TLV(name " Input 4 Volume", base + 7, \
ARIZONA_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \
arizona_mixer_tlv)
struct arizona_enum {
struct soc_enum mixer_enum;
int val;
};
#define ARIZONA_ENUM_DECL(name, xreg, xshift, xmask, xtexts, xvalues) \
struct arizona_enum name = { .mixer_enum.reg = xreg, \
.mixer_enum.shift_l = xshift, .mixer_enum.shift_r = xshift, \
.mixer_enum.mask = xmask, \
.mixer_enum.items = ARRAY_SIZE(xtexts), \
.mixer_enum.texts = xtexts, \
.mixer_enum.values = xvalues, .val = 0 }
#define ARIZONA_MUX_ENUM_DECL(name, reg) \
ARIZONA_ENUM_DECL(name, reg, 0, 0xff, \
arizona_mixer_texts, arizona_mixer_values)
#define ARIZONA_MUX_CTL_DECL(xname) \
const struct snd_kcontrol_new xname##_mux = { \
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Route", \
.info = snd_soc_info_enum_double, \
.get = arizona_mux_get, \
.put = arizona_mux_put, \
.private_value = (unsigned long)&xname##_enum }
#define ARIZONA_MUX_ENUMS(name, base_reg) \
static ARIZONA_MUX_ENUM_DECL(name##_enum, base_reg); \
static ARIZONA_MUX_CTL_DECL(name)
#define ARIZONA_MIXER_ENUMS(name, base_reg) \
ARIZONA_MUX_ENUMS(name##_in1, base_reg); \
ARIZONA_MUX_ENUMS(name##_in2, base_reg + 2); \
ARIZONA_MUX_ENUMS(name##_in3, base_reg + 4); \
ARIZONA_MUX_ENUMS(name##_in4, base_reg + 6)
#define ARIZONA_DSP_AUX_ENUMS(name, base_reg) \
ARIZONA_MUX_ENUMS(name##_aux1, base_reg); \
ARIZONA_MUX_ENUMS(name##_aux2, base_reg + 8); \
ARIZONA_MUX_ENUMS(name##_aux3, base_reg + 16); \
ARIZONA_MUX_ENUMS(name##_aux4, base_reg + 24); \
ARIZONA_MUX_ENUMS(name##_aux5, base_reg + 32); \
ARIZONA_MUX_ENUMS(name##_aux6, base_reg + 40)
#define CLEARWATER_MUX_ENUM_DECL(name, reg) \
ARIZONA_ENUM_DECL(name, reg, 0, 0xff, \
arizona_v2_mixer_texts, arizona_v2_mixer_values)
#define CLEARWATER_MUX_ENUMS(name, base_reg) \
static CLEARWATER_MUX_ENUM_DECL(name##_enum, base_reg); \
static ARIZONA_MUX_CTL_DECL(name)
#define CLEARWATER_MIXER_ENUMS(name, base_reg) \
CLEARWATER_MUX_ENUMS(name##_in1, base_reg); \
CLEARWATER_MUX_ENUMS(name##_in2, base_reg + 2); \
CLEARWATER_MUX_ENUMS(name##_in3, base_reg + 4); \
CLEARWATER_MUX_ENUMS(name##_in4, base_reg + 6)
#define CLEARWATER_DSP_AUX_ENUMS(name, base_reg) \
CLEARWATER_MUX_ENUMS(name##_aux1, base_reg); \
CLEARWATER_MUX_ENUMS(name##_aux2, base_reg + 8); \
CLEARWATER_MUX_ENUMS(name##_aux3, base_reg + 16); \
CLEARWATER_MUX_ENUMS(name##_aux4, base_reg + 24); \
CLEARWATER_MUX_ENUMS(name##_aux5, base_reg + 32); \
CLEARWATER_MUX_ENUMS(name##_aux6, base_reg + 40)
#define ARIZONA_MUX(wname, wctrl) \
{ .id = snd_soc_dapm_mux, .name = wname, .reg = SND_SOC_NOPM, \
.shift = 0, .kcontrol_news = wctrl, \
.num_kcontrols = 1, .event = arizona_mux_event, \
.event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD | \
SND_SOC_DAPM_PRE_REG | SND_SOC_DAPM_POST_REG}
#define ARIZONA_MUX_WIDGETS(name, name_str) \
ARIZONA_MUX(name_str " Input", &name##_mux)
#define ARIZONA_MIXER_WIDGETS(name, name_str) \
ARIZONA_MUX(name_str " Input 1", &name##_in1_mux), \
ARIZONA_MUX(name_str " Input 2", &name##_in2_mux), \
ARIZONA_MUX(name_str " Input 3", &name##_in3_mux), \
ARIZONA_MUX(name_str " Input 4", &name##_in4_mux), \
SND_SOC_DAPM_MIXER(name_str " Mixer", SND_SOC_NOPM, 0, 0, NULL, 0)
#define ARIZONA_DSP_WIDGETS(name, name_str) \
ARIZONA_MIXER_WIDGETS(name##L, name_str "L"), \
ARIZONA_MIXER_WIDGETS(name##R, name_str "R"), \
ARIZONA_MUX(name_str " Aux 1", &name##_aux1_mux), \
ARIZONA_MUX(name_str " Aux 2", &name##_aux2_mux), \
ARIZONA_MUX(name_str " Aux 3", &name##_aux3_mux), \
ARIZONA_MUX(name_str " Aux 4", &name##_aux4_mux), \
ARIZONA_MUX(name_str " Aux 5", &name##_aux5_mux), \
ARIZONA_MUX(name_str " Aux 6", &name##_aux6_mux)
#define ARIZONA_MUX_ROUTES(widget, name) \
{ widget, NULL, name " Input" }, \
ARIZONA_MIXER_INPUT_ROUTES(name " Input")
#define ARIZONA_MIXER_ROUTES(widget, name) \
{ widget, NULL, name " Mixer" }, \
{ name " Mixer", NULL, name " Input 1" }, \
{ name " Mixer", NULL, name " Input 2" }, \
{ name " Mixer", NULL, name " Input 3" }, \
{ name " Mixer", NULL, name " Input 4" }, \
ARIZONA_MIXER_INPUT_ROUTES(name " Input 1"), \
ARIZONA_MIXER_INPUT_ROUTES(name " Input 2"), \
ARIZONA_MIXER_INPUT_ROUTES(name " Input 3"), \
ARIZONA_MIXER_INPUT_ROUTES(name " Input 4")
#define ARIZONA_DSP_ROUTES(name) \
{ name, NULL, name " Preloader"}, \
{ name " Preloader", NULL, name " Aux 1" }, \
{ name " Preloader", NULL, name " Aux 2" }, \
{ name " Preloader", NULL, name " Aux 3" }, \
{ name " Preloader", NULL, name " Aux 4" }, \
{ name " Preloader", NULL, name " Aux 5" }, \
{ name " Preloader", NULL, name " Aux 6" }, \
ARIZONA_MIXER_INPUT_ROUTES(name " Aux 1"), \
ARIZONA_MIXER_INPUT_ROUTES(name " Aux 2"), \
ARIZONA_MIXER_INPUT_ROUTES(name " Aux 3"), \
ARIZONA_MIXER_INPUT_ROUTES(name " Aux 4"), \
ARIZONA_MIXER_INPUT_ROUTES(name " Aux 5"), \
ARIZONA_MIXER_INPUT_ROUTES(name " Aux 6"), \
ARIZONA_MIXER_ROUTES(name " Preloader", name "L"), \
ARIZONA_MIXER_ROUTES(name " Preloader", name "R")
#define ARIZONA_SAMPLE_RATE_CONTROL(name, domain) \
SOC_ENUM(name, arizona_sample_rate[(domain) - 2])
#define ARIZONA_SAMPLE_RATE_CONTROL_DVFS(name, domain) \
SOC_ENUM_EXT(name, arizona_sample_rate[(domain) - 2], \
snd_soc_get_enum_double, \
arizona_put_sample_rate_enum)
#define ARIZONA_EQ_CONTROL(xname, xbase) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
.info = snd_soc_bytes_info, .get = snd_soc_bytes_get, \
.put = arizona_eq_coeff_put, .private_value = \
((unsigned long)&(struct soc_bytes) { .base = xbase, \
.num_regs = 20, .mask = ~ARIZONA_EQ1_B1_MODE }) }
#define ARIZONA_LHPF_CONTROL(xname, xbase) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
.info = snd_soc_bytes_info, .get = snd_soc_bytes_get, \
.put = arizona_lhpf_coeff_put, .private_value = \
((unsigned long)&(struct soc_bytes) { .base = xbase, \
.num_regs = 1 }) }
#define CLEARWATER_OSR_ENUM_SIZE 5
#define ARIZONA_RATE_ENUM_SIZE 5
#define ARIZONA_SYNC_RATE_ENUM_SIZE 3
#define ARIZONA_ASYNC_RATE_ENUM_SIZE 2
#define ARIZONA_SAMPLE_RATE_ENUM_SIZE 14
#define MOON_DFC_TYPE_ENUM_SIZE 5
#define MOON_DFC_WIDTH_ENUM_SIZE 5
extern const char * const arizona_rate_text[ARIZONA_RATE_ENUM_SIZE];
extern const unsigned int arizona_rate_val[ARIZONA_RATE_ENUM_SIZE];
extern const char * const arizona_sample_rate_text[ARIZONA_SAMPLE_RATE_ENUM_SIZE];
extern const unsigned int arizona_sample_rate_val[ARIZONA_SAMPLE_RATE_ENUM_SIZE];
extern const char * const moon_dfc_width_text[MOON_DFC_WIDTH_ENUM_SIZE];
extern const unsigned int moon_dfc_width_val[MOON_DFC_WIDTH_ENUM_SIZE];
extern const char * const moon_dfc_type_text[MOON_DFC_TYPE_ENUM_SIZE];
extern const unsigned int moon_dfc_type_val[MOON_DFC_TYPE_ENUM_SIZE];
extern const struct soc_enum arizona_sample_rate[];
extern const struct soc_enum arizona_isrc_fsl[];
extern const struct soc_enum arizona_isrc_fsh[];
extern const struct soc_enum arizona_asrc_rate1;
extern const struct soc_enum arizona_asrc_rate2;
extern const struct soc_enum clearwater_asrc1_rate[];
extern const struct soc_enum clearwater_asrc2_rate[];
extern const struct soc_enum arizona_input_rate;
extern const struct soc_enum arizona_output_rate;
extern const struct soc_enum arizona_fx_rate;
extern const struct soc_enum arizona_spdif_rate;
extern const struct soc_enum moon_input_rate[];
extern const struct soc_enum moon_dfc_width[];
extern const struct soc_enum moon_dfc_type[];
extern const struct soc_enum arizona_in_vi_ramp;
extern const struct soc_enum arizona_in_vd_ramp;
extern const struct soc_enum arizona_out_vi_ramp;
extern const struct soc_enum arizona_out_vd_ramp;
extern const struct soc_enum arizona_lhpf1_mode;
extern const struct soc_enum arizona_lhpf2_mode;
extern const struct soc_enum arizona_lhpf3_mode;
extern const struct soc_enum arizona_lhpf4_mode;
extern const struct soc_enum arizona_ng_hold;
extern const struct soc_enum arizona_in_hpf_cut_enum;
extern const struct soc_enum arizona_in_dmic_osr[];
extern const struct soc_enum clearwater_in_dmic_osr[];
extern const struct soc_enum arizona_anc_input_src[];
extern const struct soc_enum clearwater_anc_input_src[];
extern const struct soc_enum arizona_anc_ng_enum;
extern const struct soc_enum arizona_output_anc_src[];
extern const struct soc_enum clearwater_output_anc_src_defs[];
extern const struct soc_enum arizona_ip_mode[];
extern int arizona_ip_mode_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
extern int moon_in_rate_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
extern int moon_dfc_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
extern int moon_osr_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
extern int moon_lp_mode_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
extern int arizona_in_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol,
int event);
extern int arizona_out_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol,
int event);
extern int arizona_hp_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol,
int event);
extern int clearwater_hp_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol,
int event);
extern int moon_hp_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol,
int event);
extern int moon_analog_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol,
int event);
extern int arizona_anc_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol,
int event);
extern int arizona_mux_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
extern int arizona_mux_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
extern int arizona_mux_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event);
extern int arizona_put_sample_rate_enum(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
extern int arizona_eq_coeff_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
extern int arizona_lhpf_coeff_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
extern int arizona_set_sysclk(struct snd_soc_codec *codec, int clk_id,
int source, unsigned int freq, int dir);
extern int arizona_cache_and_clear_sources(struct arizona *arizona,
const int *sources,
unsigned int *cache,
int lim);
extern int arizona_restore_sources(struct arizona *arizona,
const int *sources,
unsigned int *cache,
int lim);
extern void clearwater_spin_sysclk(struct arizona *arizona);
extern const struct snd_soc_dai_ops arizona_dai_ops;
extern const struct snd_soc_dai_ops arizona_simple_dai_ops;
struct arizona_fll_cfg {
unsigned int fin;
unsigned int fvco;
int n;
int theta;
int lambda;
int refdiv;
int outdiv;
int fratio;
int gain;
int intg_gain;
struct reg_sequence *patch;
unsigned int patch_size;
};
struct arizona_fll {
struct arizona *arizona;
int id;
unsigned int base;
unsigned int sync_offset;
unsigned int vco_mult;
unsigned int fvco;
int min_outdiv;
int max_outdiv;
int outdiv;
unsigned int fout;
int sync_src;
unsigned int sync_freq;
int ref_src;
unsigned int ref_freq;
struct arizona_fll_cfg ref_cfg;
struct arizona_fll_cfg sync_cfg;
};
extern int arizona_dvfs_up(struct snd_soc_codec *codec, unsigned int flags);
extern int arizona_dvfs_down(struct snd_soc_codec *codec, unsigned int flags);
extern int arizona_dvfs_sysclk_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event);
extern void arizona_init_dvfs(struct arizona_priv *priv);
extern int arizona_init_fll(struct arizona *arizona, int id, int base,
int lock_irq, int ok_irq, struct arizona_fll *fll);
extern int arizona_set_fll_refclk(struct arizona_fll *fll, int source,
unsigned int fin, unsigned int fout);
extern int arizona_set_fll(struct arizona_fll *fll, int source,
unsigned int fin, unsigned int fout);
extern int arizona_set_fll_ao(struct arizona_fll *fll, int source,
unsigned int fin, unsigned int fout);
extern int arizona_init_spk(struct snd_soc_codec *codec);
extern int arizona_init_gpio(struct snd_soc_codec *codec);
extern int arizona_init_mono(struct snd_soc_codec *codec);
extern int arizona_init_input(struct snd_soc_codec *codec);
extern int arizona_init_dai(struct arizona_priv *priv, int dai);
int arizona_set_output_mode(struct snd_soc_codec *codec, int output,
bool diff);
extern int arizona_set_hpdet_cb(struct snd_soc_codec *codec,
void (*hpdet_cb)(unsigned int measurement));
extern int arizona_set_micd_cb(struct snd_soc_codec *codec,
void (*micd_cb)(bool mic));
extern int arizona_set_ez2ctrl_cb(struct snd_soc_codec *codec,
void (*ez2ctrl_trigger)(void));
extern int arizona_set_custom_jd(struct snd_soc_codec *codec,
const struct arizona_jd_state *custom_jd);
extern int clearwater_put_dre(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
extern struct regmap *arizona_get_regmap_dsp(struct snd_soc_codec *codec);
extern struct arizona_extcon_info *
arizona_get_extcon_info(struct snd_soc_codec *codec);
extern int arizona_enable_force_bypass(struct snd_soc_codec *codec);
extern int arizona_disable_force_bypass(struct snd_soc_codec *codec);
extern const char *arizona_sample_rate_val_to_name(unsigned int rate_val);
#endif

91
sound/soc/codecs/bt-sco.c Normal file
View file

@ -0,0 +1,91 @@
/*
* Driver for generic Bluetooth SCO link
* Copyright 2011 Lars-Peter Clausen <lars@metafoo.de>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <sound/soc.h>
static const struct snd_soc_dapm_widget bt_sco_widgets[] = {
SND_SOC_DAPM_INPUT("RX"),
SND_SOC_DAPM_OUTPUT("TX"),
};
static const struct snd_soc_dapm_route bt_sco_routes[] = {
{ "Capture", NULL, "RX" },
{ "TX", NULL, "Playback" },
};
static struct snd_soc_dai_driver bt_sco_dai = {
.name = "bt-sco-pcm",
.playback = {
.stream_name = "Playback",
.channels_min = 1,
.channels_max = 1,
.rates = SNDRV_PCM_RATE_8000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
.capture = {
.stream_name = "Capture",
.channels_min = 1,
.channels_max = 1,
.rates = SNDRV_PCM_RATE_8000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
};
static struct snd_soc_codec_driver soc_codec_dev_bt_sco = {
.dapm_widgets = bt_sco_widgets,
.num_dapm_widgets = ARRAY_SIZE(bt_sco_widgets),
.dapm_routes = bt_sco_routes,
.num_dapm_routes = ARRAY_SIZE(bt_sco_routes),
};
static int bt_sco_probe(struct platform_device *pdev)
{
return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_bt_sco,
&bt_sco_dai, 1);
}
static int bt_sco_remove(struct platform_device *pdev)
{
snd_soc_unregister_codec(&pdev->dev);
return 0;
}
static struct platform_device_id bt_sco_driver_ids[] = {
{
.name = "dfbmcs320",
},
{
.name = "bt-sco",
},
{},
};
MODULE_DEVICE_TABLE(platform, bt_sco_driver_ids);
static struct platform_driver bt_sco_driver = {
.driver = {
.name = "bt-sco",
.owner = THIS_MODULE,
},
.probe = bt_sco_probe,
.remove = bt_sco_remove,
.id_table = bt_sco_driver_ids,
};
module_platform_driver(bt_sco_driver);
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
MODULE_DESCRIPTION("ASoC generic bluethooth sco link driver");
MODULE_LICENSE("GPL");

3305
sound/soc/codecs/cod3026x.c Executable file

File diff suppressed because it is too large Load diff

1310
sound/soc/codecs/cod3026x.h Executable file

File diff suppressed because it is too large Load diff

3152
sound/soc/codecs/cod9002x.c Executable file

File diff suppressed because it is too large Load diff

1362
sound/soc/codecs/cod9002x.h Executable file

File diff suppressed because it is too large Load diff

198
sound/soc/codecs/cq93vc.c Normal file
View file

@ -0,0 +1,198 @@
/*
* ALSA SoC CQ0093 Voice Codec Driver for DaVinci platforms
*
* Copyright (C) 2010 Texas Instruments, Inc
*
* Author: Miguel Aguilar <miguel.aguilar@ridgerun.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/platform_device.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/clk.h>
#include <linux/mfd/davinci_voicecodec.h>
#include <linux/spi/spi.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/initval.h>
static const struct snd_kcontrol_new cq93vc_snd_controls[] = {
SOC_SINGLE("PGA Capture Volume", DAVINCI_VC_REG05, 0, 0x03, 0),
SOC_SINGLE("Mono DAC Playback Volume", DAVINCI_VC_REG09, 0, 0x3f, 0),
};
static int cq93vc_mute(struct snd_soc_dai *dai, int mute)
{
struct snd_soc_codec *codec = dai->codec;
u8 reg;
if (mute)
reg = DAVINCI_VC_REG09_MUTE;
else
reg = 0;
snd_soc_update_bits(codec, DAVINCI_VC_REG09, DAVINCI_VC_REG09_MUTE,
reg);
return 0;
}
static int cq93vc_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = codec_dai->codec;
struct davinci_vc *davinci_vc = codec->dev->platform_data;
switch (freq) {
case 22579200:
case 27000000:
case 33868800:
davinci_vc->cq93vc.sysclk = freq;
return 0;
}
return -EINVAL;
}
static int cq93vc_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
switch (level) {
case SND_SOC_BIAS_ON:
snd_soc_write(codec, DAVINCI_VC_REG12,
DAVINCI_VC_REG12_POWER_ALL_ON);
break;
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
snd_soc_write(codec, DAVINCI_VC_REG12,
DAVINCI_VC_REG12_POWER_ALL_OFF);
break;
case SND_SOC_BIAS_OFF:
/* force all power off */
snd_soc_write(codec, DAVINCI_VC_REG12,
DAVINCI_VC_REG12_POWER_ALL_OFF);
break;
}
codec->dapm.bias_level = level;
return 0;
}
#define CQ93VC_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000)
#define CQ93VC_FORMATS (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE)
static const struct snd_soc_dai_ops cq93vc_dai_ops = {
.digital_mute = cq93vc_mute,
.set_sysclk = cq93vc_set_dai_sysclk,
};
static struct snd_soc_dai_driver cq93vc_dai = {
.name = "cq93vc-hifi",
.playback = {
.stream_name = "Playback",
.channels_min = 1,
.channels_max = 2,
.rates = CQ93VC_RATES,
.formats = CQ93VC_FORMATS,},
.capture = {
.stream_name = "Capture",
.channels_min = 1,
.channels_max = 2,
.rates = CQ93VC_RATES,
.formats = CQ93VC_FORMATS,},
.ops = &cq93vc_dai_ops,
};
static int cq93vc_resume(struct snd_soc_codec *codec)
{
cq93vc_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
return 0;
}
static int cq93vc_probe(struct snd_soc_codec *codec)
{
struct davinci_vc *davinci_vc = codec->dev->platform_data;
davinci_vc->cq93vc.codec = codec;
/* Off, with power on */
cq93vc_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
return 0;
}
static int cq93vc_remove(struct snd_soc_codec *codec)
{
cq93vc_set_bias_level(codec, SND_SOC_BIAS_OFF);
return 0;
}
static struct regmap *cq93vc_get_regmap(struct device *dev)
{
struct davinci_vc *davinci_vc = dev->platform_data;
return davinci_vc->regmap;
}
static struct snd_soc_codec_driver soc_codec_dev_cq93vc = {
.set_bias_level = cq93vc_set_bias_level,
.probe = cq93vc_probe,
.remove = cq93vc_remove,
.resume = cq93vc_resume,
.get_regmap = cq93vc_get_regmap,
.controls = cq93vc_snd_controls,
.num_controls = ARRAY_SIZE(cq93vc_snd_controls),
};
static int cq93vc_platform_probe(struct platform_device *pdev)
{
return snd_soc_register_codec(&pdev->dev,
&soc_codec_dev_cq93vc, &cq93vc_dai, 1);
}
static int cq93vc_platform_remove(struct platform_device *pdev)
{
snd_soc_unregister_codec(&pdev->dev);
return 0;
}
static struct platform_driver cq93vc_codec_driver = {
.driver = {
.name = "cq93vc-codec",
.owner = THIS_MODULE,
},
.probe = cq93vc_platform_probe,
.remove = cq93vc_platform_remove,
};
module_platform_driver(cq93vc_codec_driver);
MODULE_DESCRIPTION("Texas Instruments DaVinci ASoC CQ0093 Voice Codec Driver");
MODULE_AUTHOR("Miguel Aguilar");
MODULE_LICENSE("GPL");

631
sound/soc/codecs/cs35l32.c Normal file
View file

@ -0,0 +1,631 @@
/*
* cs35l32.c -- CS35L32 ALSA SoC audio driver
*
* Copyright 2014 CirrusLogic, Inc.
*
* Author: Brian Austin <brian.austin@cirrus.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/gpio.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/gpio/consumer.h>
#include <linux/of_device.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <sound/initval.h>
#include <sound/tlv.h>
#include <dt-bindings/sound/cs35l32.h>
#include "cs35l32.h"
#define CS35L32_NUM_SUPPLIES 2
static const char *const cs35l32_supply_names[CS35L32_NUM_SUPPLIES] = {
"VA",
"VP",
};
struct cs35l32_private {
struct regmap *regmap;
struct snd_soc_codec *codec;
struct regulator_bulk_data supplies[CS35L32_NUM_SUPPLIES];
struct cs35l32_platform_data pdata;
struct gpio_desc *reset_gpio;
};
static const struct reg_default cs35l32_reg_defaults[] = {
{ 0x06, 0x04 }, /* Power Ctl 1 */
{ 0x07, 0xE8 }, /* Power Ctl 2 */
{ 0x08, 0x40 }, /* Clock Ctl */
{ 0x09, 0x20 }, /* Low Battery Threshold */
{ 0x0A, 0x00 }, /* Voltage Monitor [RO] */
{ 0x0B, 0x40 }, /* Conv Peak Curr Protection CTL */
{ 0x0C, 0x07 }, /* IMON Scaling */
{ 0x0D, 0x03 }, /* Audio/LED Pwr Manager */
{ 0x0F, 0x20 }, /* Serial Port Control */
{ 0x10, 0x14 }, /* Class D Amp CTL */
{ 0x11, 0x00 }, /* Protection Release CTL */
{ 0x12, 0xFF }, /* Interrupt Mask 1 */
{ 0x13, 0xFF }, /* Interrupt Mask 2 */
{ 0x14, 0xFF }, /* Interrupt Mask 3 */
{ 0x19, 0x00 }, /* LED Flash Mode Current */
{ 0x1A, 0x00 }, /* LED Movie Mode Current */
{ 0x1B, 0x20 }, /* LED Flash Timer */
{ 0x1C, 0x00 }, /* LED Flash Inhibit Current */
};
static bool cs35l32_readable_register(struct device *dev, unsigned int reg)
{
switch (reg) {
case CS35L32_DEVID_AB:
case CS35L32_DEVID_CD:
case CS35L32_DEVID_E:
case CS35L32_FAB_ID:
case CS35L32_REV_ID:
case CS35L32_PWRCTL1:
case CS35L32_PWRCTL2:
case CS35L32_CLK_CTL:
case CS35L32_BATT_THRESHOLD:
case CS35L32_VMON:
case CS35L32_BST_CPCP_CTL:
case CS35L32_IMON_SCALING:
case CS35L32_AUDIO_LED_MNGR:
case CS35L32_ADSP_CTL:
case CS35L32_CLASSD_CTL:
case CS35L32_PROTECT_CTL:
case CS35L32_INT_MASK_1:
case CS35L32_INT_MASK_2:
case CS35L32_INT_MASK_3:
case CS35L32_INT_STATUS_1:
case CS35L32_INT_STATUS_2:
case CS35L32_INT_STATUS_3:
case CS35L32_LED_STATUS:
case CS35L32_FLASH_MODE:
case CS35L32_MOVIE_MODE:
case CS35L32_FLASH_TIMER:
case CS35L32_FLASH_INHIBIT:
return true;
default:
return false;
}
}
static bool cs35l32_volatile_register(struct device *dev, unsigned int reg)
{
switch (reg) {
case CS35L32_DEVID_AB:
case CS35L32_DEVID_CD:
case CS35L32_DEVID_E:
case CS35L32_FAB_ID:
case CS35L32_REV_ID:
case CS35L32_INT_STATUS_1:
case CS35L32_INT_STATUS_2:
case CS35L32_INT_STATUS_3:
case CS35L32_LED_STATUS:
return true;
default:
return false;
}
}
static bool cs35l32_precious_register(struct device *dev, unsigned int reg)
{
switch (reg) {
case CS35L32_INT_STATUS_1:
case CS35L32_INT_STATUS_2:
case CS35L32_INT_STATUS_3:
case CS35L32_LED_STATUS:
return true;
default:
return false;
}
}
static DECLARE_TLV_DB_SCALE(classd_ctl_tlv, 900, 300, 0);
static const struct snd_kcontrol_new imon_ctl =
SOC_DAPM_SINGLE("Switch", CS35L32_PWRCTL2, 6, 1, 1);
static const struct snd_kcontrol_new vmon_ctl =
SOC_DAPM_SINGLE("Switch", CS35L32_PWRCTL2, 7, 1, 1);
static const struct snd_kcontrol_new vpmon_ctl =
SOC_DAPM_SINGLE("Switch", CS35L32_PWRCTL2, 5, 1, 1);
static const struct snd_kcontrol_new cs35l32_snd_controls[] = {
SOC_SINGLE_TLV("Speaker Volume", CS35L32_CLASSD_CTL,
3, 0x04, 1, classd_ctl_tlv),
SOC_SINGLE("Zero Cross Switch", CS35L32_CLASSD_CTL, 2, 1, 0),
SOC_SINGLE("Gain Manager Switch", CS35L32_AUDIO_LED_MNGR, 3, 1, 0),
};
static const struct snd_soc_dapm_widget cs35l32_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("BOOST", CS35L32_PWRCTL1, 2, 1, NULL, 0),
SND_SOC_DAPM_OUT_DRV("Speaker", CS35L32_PWRCTL1, 7, 1, NULL, 0),
SND_SOC_DAPM_AIF_OUT("SDOUT", NULL, 0, CS35L32_PWRCTL2, 3, 1),
SND_SOC_DAPM_INPUT("VP"),
SND_SOC_DAPM_INPUT("ISENSE"),
SND_SOC_DAPM_INPUT("VSENSE"),
SND_SOC_DAPM_SWITCH("VMON ADC", CS35L32_PWRCTL2, 7, 1, &vmon_ctl),
SND_SOC_DAPM_SWITCH("IMON ADC", CS35L32_PWRCTL2, 6, 1, &imon_ctl),
SND_SOC_DAPM_SWITCH("VPMON ADC", CS35L32_PWRCTL2, 5, 1, &vpmon_ctl),
};
static const struct snd_soc_dapm_route cs35l32_audio_map[] = {
{"Speaker", NULL, "BOOST"},
{"VMON ADC", NULL, "VSENSE"},
{"IMON ADC", NULL, "ISENSE"},
{"VPMON ADC", NULL, "VP"},
{"SDOUT", "Switch", "VMON ADC"},
{"SDOUT", "Switch", "IMON ADC"},
{"SDOUT", "Switch", "VPMON ADC"},
{"Capture", NULL, "SDOUT"},
};
static int cs35l32_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBM_CFM:
snd_soc_update_bits(codec, CS35L32_ADSP_CTL,
CS35L32_ADSP_MASTER_MASK,
CS35L32_ADSP_MASTER_MASK);
break;
case SND_SOC_DAIFMT_CBS_CFS:
snd_soc_update_bits(codec, CS35L32_ADSP_CTL,
CS35L32_ADSP_MASTER_MASK, 0);
break;
default:
return -EINVAL;
}
return 0;
}
static int cs35l32_set_tristate(struct snd_soc_dai *dai, int tristate)
{
struct snd_soc_codec *codec = dai->codec;
return snd_soc_update_bits(codec, CS35L32_PWRCTL2,
CS35L32_SDOUT_3ST, tristate << 3);
}
static const struct snd_soc_dai_ops cs35l32_ops = {
.set_fmt = cs35l32_set_dai_fmt,
.set_tristate = cs35l32_set_tristate,
};
static struct snd_soc_dai_driver cs35l32_dai[] = {
{
.name = "cs35l32-monitor",
.id = 0,
.capture = {
.stream_name = "Capture",
.channels_min = 2,
.channels_max = 2,
.rates = CS35L32_RATES,
.formats = CS35L32_FORMATS,
},
.ops = &cs35l32_ops,
.symmetric_rates = 1,
}
};
static int cs35l32_codec_set_sysclk(struct snd_soc_codec *codec,
int clk_id, int source, unsigned int freq, int dir)
{
unsigned int val;
switch (freq) {
case 6000000:
val = CS35L32_MCLK_RATIO;
break;
case 12000000:
val = CS35L32_MCLK_DIV2_MASK | CS35L32_MCLK_RATIO;
break;
case 6144000:
val = 0;
break;
case 12288000:
val = CS35L32_MCLK_DIV2_MASK;
break;
default:
return -EINVAL;
}
return snd_soc_update_bits(codec, CS35L32_CLK_CTL,
CS35L32_MCLK_DIV2_MASK | CS35L32_MCLK_RATIO_MASK, val);
}
static struct snd_soc_codec_driver soc_codec_dev_cs35l32 = {
.set_sysclk = cs35l32_codec_set_sysclk,
.dapm_widgets = cs35l32_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(cs35l32_dapm_widgets),
.dapm_routes = cs35l32_audio_map,
.num_dapm_routes = ARRAY_SIZE(cs35l32_audio_map),
.controls = cs35l32_snd_controls,
.num_controls = ARRAY_SIZE(cs35l32_snd_controls),
};
/* Current and threshold powerup sequence Pg37 in datasheet */
static const struct reg_default cs35l32_monitor_patch[] = {
{ 0x00, 0x99 },
{ 0x48, 0x17 },
{ 0x49, 0x56 },
{ 0x43, 0x01 },
{ 0x3B, 0x62 },
{ 0x3C, 0x80 },
{ 0x00, 0x00 },
};
static struct regmap_config cs35l32_regmap = {
.reg_bits = 8,
.val_bits = 8,
.max_register = CS35L32_MAX_REGISTER,
.reg_defaults = cs35l32_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(cs35l32_reg_defaults),
.volatile_reg = cs35l32_volatile_register,
.readable_reg = cs35l32_readable_register,
.precious_reg = cs35l32_precious_register,
.cache_type = REGCACHE_RBTREE,
};
static int cs35l32_handle_of_data(struct i2c_client *i2c_client,
struct cs35l32_platform_data *pdata)
{
struct device_node *np = i2c_client->dev.of_node;
unsigned int val;
if (of_property_read_u32(np, "cirrus,sdout-share", &val) >= 0)
pdata->sdout_share = val;
of_property_read_u32(np, "cirrus,boost-manager", &val);
switch (val) {
case CS35L32_BOOST_MGR_AUTO:
case CS35L32_BOOST_MGR_AUTO_AUDIO:
case CS35L32_BOOST_MGR_BYPASS:
case CS35L32_BOOST_MGR_FIXED:
pdata->boost_mng = val;
break;
default:
dev_err(&i2c_client->dev,
"Wrong cirrus,boost-manager DT value %d\n", val);
pdata->boost_mng = CS35L32_BOOST_MGR_BYPASS;
}
of_property_read_u32(np, "cirrus,sdout-datacfg", &val);
switch (val) {
case CS35L32_DATA_CFG_LR_VP:
case CS35L32_DATA_CFG_LR_STAT:
case CS35L32_DATA_CFG_LR:
case CS35L32_DATA_CFG_LR_VPSTAT:
pdata->sdout_datacfg = val;
break;
default:
dev_err(&i2c_client->dev,
"Wrong cirrus,sdout-datacfg DT value %d\n", val);
pdata->sdout_datacfg = CS35L32_DATA_CFG_LR;
}
of_property_read_u32(np, "cirrus,battery-threshold", &val);
switch (val) {
case CS35L32_BATT_THRESH_3_1V:
case CS35L32_BATT_THRESH_3_2V:
case CS35L32_BATT_THRESH_3_3V:
case CS35L32_BATT_THRESH_3_4V:
pdata->batt_thresh = val;
break;
default:
dev_err(&i2c_client->dev,
"Wrong cirrus,battery-threshold DT value %d\n", val);
pdata->batt_thresh = CS35L32_BATT_THRESH_3_3V;
}
of_property_read_u32(np, "cirrus,battery-recovery", &val);
switch (val) {
case CS35L32_BATT_RECOV_3_1V:
case CS35L32_BATT_RECOV_3_2V:
case CS35L32_BATT_RECOV_3_3V:
case CS35L32_BATT_RECOV_3_4V:
case CS35L32_BATT_RECOV_3_5V:
case CS35L32_BATT_RECOV_3_6V:
pdata->batt_recov = val;
break;
default:
dev_err(&i2c_client->dev,
"Wrong cirrus,battery-recovery DT value %d\n", val);
pdata->batt_recov = CS35L32_BATT_RECOV_3_4V;
}
return 0;
}
static int cs35l32_i2c_probe(struct i2c_client *i2c_client,
const struct i2c_device_id *id)
{
struct cs35l32_private *cs35l32;
struct cs35l32_platform_data *pdata =
dev_get_platdata(&i2c_client->dev);
int ret, i;
unsigned int devid = 0;
unsigned int reg;
cs35l32 = devm_kzalloc(&i2c_client->dev, sizeof(struct cs35l32_private),
GFP_KERNEL);
if (!cs35l32) {
dev_err(&i2c_client->dev, "could not allocate codec\n");
return -ENOMEM;
}
i2c_set_clientdata(i2c_client, cs35l32);
cs35l32->regmap = devm_regmap_init_i2c(i2c_client, &cs35l32_regmap);
if (IS_ERR(cs35l32->regmap)) {
ret = PTR_ERR(cs35l32->regmap);
dev_err(&i2c_client->dev, "regmap_init() failed: %d\n", ret);
return ret;
}
if (pdata) {
cs35l32->pdata = *pdata;
} else {
pdata = devm_kzalloc(&i2c_client->dev,
sizeof(struct cs35l32_platform_data),
GFP_KERNEL);
if (!pdata) {
dev_err(&i2c_client->dev, "could not allocate pdata\n");
return -ENOMEM;
}
if (i2c_client->dev.of_node) {
ret = cs35l32_handle_of_data(i2c_client,
&cs35l32->pdata);
if (ret != 0)
return ret;
}
}
for (i = 0; i < ARRAY_SIZE(cs35l32->supplies); i++)
cs35l32->supplies[i].supply = cs35l32_supply_names[i];
ret = devm_regulator_bulk_get(&i2c_client->dev,
ARRAY_SIZE(cs35l32->supplies),
cs35l32->supplies);
if (ret != 0) {
dev_err(&i2c_client->dev,
"Failed to request supplies: %d\n", ret);
return ret;
}
ret = regulator_bulk_enable(ARRAY_SIZE(cs35l32->supplies),
cs35l32->supplies);
if (ret != 0) {
dev_err(&i2c_client->dev,
"Failed to enable supplies: %d\n", ret);
return ret;
}
/* Reset the Device */
cs35l32->reset_gpio = devm_gpiod_get(&i2c_client->dev,
"reset-gpios");
if (IS_ERR(cs35l32->reset_gpio)) {
ret = PTR_ERR(cs35l32->reset_gpio);
if (ret != -ENOENT && ret != -ENOSYS)
return ret;
cs35l32->reset_gpio = NULL;
} else {
ret = gpiod_direction_output(cs35l32->reset_gpio, 0);
if (ret)
return ret;
gpiod_set_value_cansleep(cs35l32->reset_gpio, 1);
}
/* initialize codec */
ret = regmap_read(cs35l32->regmap, CS35L32_DEVID_AB, &reg);
devid = (reg & 0xFF) << 12;
ret = regmap_read(cs35l32->regmap, CS35L32_DEVID_CD, &reg);
devid |= (reg & 0xFF) << 4;
ret = regmap_read(cs35l32->regmap, CS35L32_DEVID_E, &reg);
devid |= (reg & 0xF0) >> 4;
if (devid != CS35L32_CHIP_ID) {
ret = -ENODEV;
dev_err(&i2c_client->dev,
"CS35L32 Device ID (%X). Expected %X\n",
devid, CS35L32_CHIP_ID);
return ret;
}
ret = regmap_read(cs35l32->regmap, CS35L32_REV_ID, &reg);
if (ret < 0) {
dev_err(&i2c_client->dev, "Get Revision ID failed\n");
return ret;
}
ret = regmap_register_patch(cs35l32->regmap, cs35l32_monitor_patch,
ARRAY_SIZE(cs35l32_monitor_patch));
if (ret < 0) {
dev_err(&i2c_client->dev, "Failed to apply errata patch\n");
return ret;
}
dev_info(&i2c_client->dev,
"Cirrus Logic CS35L32, Revision: %02X\n", reg & 0xFF);
/* Setup VBOOST Management */
if (cs35l32->pdata.boost_mng)
regmap_update_bits(cs35l32->regmap, CS35L32_AUDIO_LED_MNGR,
CS35L32_BOOST_MASK,
cs35l32->pdata.boost_mng);
/* Setup ADSP Format Config */
if (cs35l32->pdata.sdout_share)
regmap_update_bits(cs35l32->regmap, CS35L32_ADSP_CTL,
CS35L32_ADSP_SHARE_MASK,
cs35l32->pdata.sdout_share << 3);
/* Setup ADSP Data Configuration */
if (cs35l32->pdata.sdout_datacfg)
regmap_update_bits(cs35l32->regmap, CS35L32_ADSP_CTL,
CS35L32_ADSP_DATACFG_MASK,
cs35l32->pdata.sdout_datacfg << 4);
/* Setup Low Battery Recovery */
if (cs35l32->pdata.batt_recov)
regmap_update_bits(cs35l32->regmap, CS35L32_BATT_THRESHOLD,
CS35L32_BATT_REC_MASK,
cs35l32->pdata.batt_recov << 1);
/* Setup Low Battery Threshold */
if (cs35l32->pdata.batt_thresh)
regmap_update_bits(cs35l32->regmap, CS35L32_BATT_THRESHOLD,
CS35L32_BATT_THRESH_MASK,
cs35l32->pdata.batt_thresh << 4);
/* Power down the AMP */
regmap_update_bits(cs35l32->regmap, CS35L32_PWRCTL1, CS35L32_PDN_AMP,
CS35L32_PDN_AMP);
/* Clear MCLK Error Bit since we don't have the clock yet */
ret = regmap_read(cs35l32->regmap, CS35L32_INT_STATUS_1, &reg);
ret = snd_soc_register_codec(&i2c_client->dev,
&soc_codec_dev_cs35l32, cs35l32_dai,
ARRAY_SIZE(cs35l32_dai));
if (ret < 0)
goto err_disable;
return 0;
err_disable:
regulator_bulk_disable(ARRAY_SIZE(cs35l32->supplies),
cs35l32->supplies);
return ret;
}
static int cs35l32_i2c_remove(struct i2c_client *i2c_client)
{
struct cs35l32_private *cs35l32 = i2c_get_clientdata(i2c_client);
snd_soc_unregister_codec(&i2c_client->dev);
/* Hold down reset */
if (cs35l32->reset_gpio)
gpiod_set_value_cansleep(cs35l32->reset_gpio, 0);
return 0;
}
#ifdef CONFIG_PM_RUNTIME
static int cs35l32_runtime_suspend(struct device *dev)
{
struct cs35l32_private *cs35l32 = dev_get_drvdata(dev);
regcache_cache_only(cs35l32->regmap, true);
regcache_mark_dirty(cs35l32->regmap);
/* Hold down reset */
if (cs35l32->reset_gpio)
gpiod_set_value_cansleep(cs35l32->reset_gpio, 0);
/* remove power */
regulator_bulk_disable(ARRAY_SIZE(cs35l32->supplies),
cs35l32->supplies);
return 0;
}
static int cs35l32_runtime_resume(struct device *dev)
{
struct cs35l32_private *cs35l32 = dev_get_drvdata(dev);
int ret;
/* Enable power */
ret = regulator_bulk_enable(ARRAY_SIZE(cs35l32->supplies),
cs35l32->supplies);
if (ret != 0) {
dev_err(dev, "Failed to enable supplies: %d\n",
ret);
return ret;
}
if (cs35l32->reset_gpio)
gpiod_set_value_cansleep(cs35l32->reset_gpio, 1);
regcache_cache_only(cs35l32->regmap, false);
regcache_sync(cs35l32->regmap);
return 0;
}
#endif
static const struct dev_pm_ops cs35l32_runtime_pm = {
SET_RUNTIME_PM_OPS(cs35l32_runtime_suspend, cs35l32_runtime_resume,
NULL)
};
static const struct of_device_id cs35l32_of_match[] = {
{ .compatible = "cirrus,cs35l32", },
{},
};
MODULE_DEVICE_TABLE(of, cs35l32_of_match);
static const struct i2c_device_id cs35l32_id[] = {
{"cs35l32", 0},
{}
};
MODULE_DEVICE_TABLE(i2c, cs35l32_id);
static struct i2c_driver cs35l32_i2c_driver = {
.driver = {
.name = "cs35l32",
.owner = THIS_MODULE,
.pm = &cs35l32_runtime_pm,
.of_match_table = cs35l32_of_match,
},
.id_table = cs35l32_id,
.probe = cs35l32_i2c_probe,
.remove = cs35l32_i2c_remove,
};
module_i2c_driver(cs35l32_i2c_driver);
MODULE_DESCRIPTION("ASoC CS35L32 driver");
MODULE_AUTHOR("Brian Austin, Cirrus Logic Inc, <brian.austin@cirrus.com>");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,93 @@
/*
* cs35l32.h -- CS35L32 ALSA SoC audio driver
*
* Copyright 2014 CirrusLogic, Inc.
*
* Author: Brian Austin <brian.austin@cirrus.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#ifndef __CS35L32_H__
#define __CS35L32_H__
struct cs35l32_platform_data {
/* Low Battery Threshold */
unsigned int batt_thresh;
/* Low Battery Recovery */
unsigned int batt_recov;
/* LED Current Management*/
unsigned int led_mng;
/* Audio Gain w/ LED */
unsigned int audiogain_mng;
/* Boost Management */
unsigned int boost_mng;
/* Data CFG for DUAL device */
unsigned int sdout_datacfg;
/* SDOUT Sharing */
unsigned int sdout_share;
};
#define CS35L32_CHIP_ID 0x00035A32
#define CS35L32_DEVID_AB 0x01 /* Device ID A & B [RO] */
#define CS35L32_DEVID_CD 0x02 /* Device ID C & D [RO] */
#define CS35L32_DEVID_E 0x03 /* Device ID E [RO] */
#define CS35L32_FAB_ID 0x04 /* Fab ID [RO] */
#define CS35L32_REV_ID 0x05 /* Revision ID [RO] */
#define CS35L32_PWRCTL1 0x06 /* Power Ctl 1 */
#define CS35L32_PWRCTL2 0x07 /* Power Ctl 2 */
#define CS35L32_CLK_CTL 0x08 /* Clock Ctl */
#define CS35L32_BATT_THRESHOLD 0x09 /* Low Battery Threshold */
#define CS35L32_VMON 0x0A /* Voltage Monitor [RO] */
#define CS35L32_BST_CPCP_CTL 0x0B /* Conv Peak Curr Protection CTL */
#define CS35L32_IMON_SCALING 0x0C /* IMON Scaling */
#define CS35L32_AUDIO_LED_MNGR 0x0D /* Audio/LED Pwr Manager */
#define CS35L32_ADSP_CTL 0x0F /* Serial Port Control */
#define CS35L32_CLASSD_CTL 0x10 /* Class D Amp CTL */
#define CS35L32_PROTECT_CTL 0x11 /* Protection Release CTL */
#define CS35L32_INT_MASK_1 0x12 /* Interrupt Mask 1 */
#define CS35L32_INT_MASK_2 0x13 /* Interrupt Mask 2 */
#define CS35L32_INT_MASK_3 0x14 /* Interrupt Mask 3 */
#define CS35L32_INT_STATUS_1 0x15 /* Interrupt Status 1 [RO] */
#define CS35L32_INT_STATUS_2 0x16 /* Interrupt Status 2 [RO] */
#define CS35L32_INT_STATUS_3 0x17 /* Interrupt Status 3 [RO] */
#define CS35L32_LED_STATUS 0x18 /* LED Lighting Status [RO] */
#define CS35L32_FLASH_MODE 0x19 /* LED Flash Mode Current */
#define CS35L32_MOVIE_MODE 0x1A /* LED Movie Mode Current */
#define CS35L32_FLASH_TIMER 0x1B /* LED Flash Timer */
#define CS35L32_FLASH_INHIBIT 0x1C /* LED Flash Inhibit Current */
#define CS35L32_MAX_REGISTER 0x1C
#define CS35L32_MCLK_DIV2 0x01
#define CS35L32_MCLK_RATIO 0x01
#define CS35L32_MCLKDIS 0x80
#define CS35L32_PDN_ALL 0x01
#define CS35L32_PDN_AMP 0x80
#define CS35L32_PDN_BOOST 0x04
#define CS35L32_PDN_IMON 0x40
#define CS35L32_PDN_VMON 0x80
#define CS35L32_PDN_VPMON 0x20
#define CS35L32_PDN_ADSP 0x08
#define CS35L32_MCLK_DIV2_MASK 0x40
#define CS35L32_MCLK_RATIO_MASK 0x01
#define CS35L32_MCLK_MASK 0x41
#define CS35L32_ADSP_MASTER_MASK 0x40
#define CS35L32_BOOST_MASK 0x03
#define CS35L32_GAIN_MGR_MASK 0x08
#define CS35L32_ADSP_SHARE_MASK 0x08
#define CS35L32_ADSP_DATACFG_MASK 0x30
#define CS35L32_SDOUT_3ST 0x80
#define CS35L32_BATT_REC_MASK 0x0E
#define CS35L32_BATT_THRESH_MASK 0x30
#define CS35L32_RATES (SNDRV_PCM_RATE_48000)
#define CS35L32_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S24_LE | \
SNDRV_PCM_FMTBIT_S32_LE)
#endif

683
sound/soc/codecs/cs4265.c Normal file
View file

@ -0,0 +1,683 @@
/*
* cs4265.c -- CS4265 ALSA SoC audio driver
*
* Copyright 2014 Cirrus Logic, Inc.
*
* Author: Paul Handrigan <paul.handrigan@cirrus.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/gpio/consumer.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <sound/initval.h>
#include <sound/tlv.h>
#include "cs4265.h"
struct cs4265_private {
struct device *dev;
struct regmap *regmap;
struct gpio_desc *reset_gpio;
u8 format;
u32 sysclk;
};
static const struct reg_default cs4265_reg_defaults[] = {
{ CS4265_PWRCTL, 0x0F },
{ CS4265_DAC_CTL, 0x08 },
{ CS4265_ADC_CTL, 0x00 },
{ CS4265_MCLK_FREQ, 0x00 },
{ CS4265_SIG_SEL, 0x40 },
{ CS4265_CHB_PGA_CTL, 0x00 },
{ CS4265_CHA_PGA_CTL, 0x00 },
{ CS4265_ADC_CTL2, 0x19 },
{ CS4265_DAC_CHA_VOL, 0x00 },
{ CS4265_DAC_CHB_VOL, 0x00 },
{ CS4265_DAC_CTL2, 0xC0 },
{ CS4265_SPDIF_CTL1, 0x00 },
{ CS4265_SPDIF_CTL2, 0x00 },
{ CS4265_INT_MASK, 0x00 },
{ CS4265_STATUS_MODE_MSB, 0x00 },
{ CS4265_STATUS_MODE_LSB, 0x00 },
};
static bool cs4265_readable_register(struct device *dev, unsigned int reg)
{
switch (reg) {
case CS4265_PWRCTL:
case CS4265_DAC_CTL:
case CS4265_ADC_CTL:
case CS4265_MCLK_FREQ:
case CS4265_SIG_SEL:
case CS4265_CHB_PGA_CTL:
case CS4265_CHA_PGA_CTL:
case CS4265_ADC_CTL2:
case CS4265_DAC_CHA_VOL:
case CS4265_DAC_CHB_VOL:
case CS4265_DAC_CTL2:
case CS4265_SPDIF_CTL1:
case CS4265_SPDIF_CTL2:
case CS4265_INT_MASK:
case CS4265_STATUS_MODE_MSB:
case CS4265_STATUS_MODE_LSB:
case CS4265_CHIP_ID:
return true;
default:
return false;
}
}
static bool cs4265_volatile_register(struct device *dev, unsigned int reg)
{
switch (reg) {
case CS4265_INT_STATUS:
return true;
default:
return false;
}
}
static DECLARE_TLV_DB_SCALE(pga_tlv, -1200, 50, 0);
static DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 0);
static const char * const digital_input_mux_text[] = {
"SDIN1", "SDIN2"
};
static SOC_ENUM_SINGLE_DECL(digital_input_mux_enum, CS4265_SIG_SEL, 7,
digital_input_mux_text);
static const struct snd_kcontrol_new digital_input_mux =
SOC_DAPM_ENUM("Digital Input Mux", digital_input_mux_enum);
static const char * const mic_linein_text[] = {
"MIC", "LINEIN"
};
static SOC_ENUM_SINGLE_DECL(mic_linein_enum, CS4265_ADC_CTL2, 0,
mic_linein_text);
static const char * const cam_mode_text[] = {
"One Byte", "Two Byte"
};
static SOC_ENUM_SINGLE_DECL(cam_mode_enum, CS4265_SPDIF_CTL1, 5,
cam_mode_text);
static const char * const cam_mono_stereo_text[] = {
"Stereo", "Mono"
};
static SOC_ENUM_SINGLE_DECL(spdif_mono_stereo_enum, CS4265_SPDIF_CTL2, 2,
cam_mono_stereo_text);
static const char * const mono_select_text[] = {
"Channel A", "Channel B"
};
static SOC_ENUM_SINGLE_DECL(spdif_mono_select_enum, CS4265_SPDIF_CTL2, 0,
mono_select_text);
static const struct snd_kcontrol_new mic_linein_mux =
SOC_DAPM_ENUM("ADC Input Capture Mux", mic_linein_enum);
static const struct snd_kcontrol_new loopback_ctl =
SOC_DAPM_SINGLE("Switch", CS4265_SIG_SEL, 1, 1, 0);
static const struct snd_kcontrol_new spdif_switch =
SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 0, 0);
static const struct snd_kcontrol_new dac_switch =
SOC_DAPM_SINGLE("Switch", CS4265_PWRCTL, 1, 1, 0);
static const struct snd_kcontrol_new cs4265_snd_controls[] = {
SOC_DOUBLE_R_SX_TLV("PGA Volume", CS4265_CHA_PGA_CTL,
CS4265_CHB_PGA_CTL, 0, 0x28, 0x30, pga_tlv),
SOC_DOUBLE_R_TLV("DAC Volume", CS4265_DAC_CHA_VOL,
CS4265_DAC_CHB_VOL, 0, 0xFF, 1, dac_tlv),
SOC_SINGLE("De-emp 44.1kHz Switch", CS4265_DAC_CTL, 1,
1, 0),
SOC_SINGLE("DAC INV Switch", CS4265_DAC_CTL2, 5,
1, 0),
SOC_SINGLE("DAC Zero Cross Switch", CS4265_DAC_CTL2, 6,
1, 0),
SOC_SINGLE("DAC Soft Ramp Switch", CS4265_DAC_CTL2, 7,
1, 0),
SOC_SINGLE("ADC HPF Switch", CS4265_ADC_CTL, 1,
1, 0),
SOC_SINGLE("ADC Zero Cross Switch", CS4265_ADC_CTL2, 3,
1, 1),
SOC_SINGLE("ADC Soft Ramp Switch", CS4265_ADC_CTL2, 7,
1, 0),
SOC_SINGLE("E to F Buffer Disable Switch", CS4265_SPDIF_CTL1,
6, 1, 0),
SOC_ENUM("C Data Access", cam_mode_enum),
SOC_SINGLE("Validity Bit Control Switch", CS4265_SPDIF_CTL2,
3, 1, 0),
SOC_ENUM("SPDIF Mono/Stereo", spdif_mono_stereo_enum),
SOC_SINGLE("MMTLR Data Switch", 0,
1, 1, 0),
SOC_ENUM("Mono Channel Select", spdif_mono_select_enum),
SND_SOC_BYTES("C Data Buffer", CS4265_C_DATA_BUFF, 24),
};
static const struct snd_soc_dapm_widget cs4265_dapm_widgets[] = {
SND_SOC_DAPM_INPUT("LINEINL"),
SND_SOC_DAPM_INPUT("LINEINR"),
SND_SOC_DAPM_INPUT("MICL"),
SND_SOC_DAPM_INPUT("MICR"),
SND_SOC_DAPM_AIF_OUT("DOUT", NULL, 0,
SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_OUT("SPDIFOUT", NULL, 0,
SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_MUX("ADC Mux", SND_SOC_NOPM, 0, 0, &mic_linein_mux),
SND_SOC_DAPM_ADC("ADC", NULL, CS4265_PWRCTL, 2, 1),
SND_SOC_DAPM_PGA("Pre-amp MIC", CS4265_PWRCTL, 3,
1, NULL, 0),
SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM,
0, 0, &digital_input_mux),
SND_SOC_DAPM_MIXER("SDIN1 Input Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_MIXER("SDIN2 Input Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_MIXER("SPDIF Transmitter", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_SWITCH("Loopback", SND_SOC_NOPM, 0, 0,
&loopback_ctl),
SND_SOC_DAPM_SWITCH("SPDIF", SND_SOC_NOPM, 0, 0,
&spdif_switch),
SND_SOC_DAPM_SWITCH("DAC", CS4265_PWRCTL, 1, 1,
&dac_switch),
SND_SOC_DAPM_AIF_IN("DIN1", NULL, 0,
SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_IN("DIN2", NULL, 0,
SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_IN("TXIN", NULL, 0,
CS4265_SPDIF_CTL2, 5, 1),
SND_SOC_DAPM_OUTPUT("LINEOUTL"),
SND_SOC_DAPM_OUTPUT("LINEOUTR"),
};
static const struct snd_soc_dapm_route cs4265_audio_map[] = {
{"DIN1", NULL, "DAI1 Playback"},
{"DIN2", NULL, "DAI2 Playback"},
{"SDIN1 Input Mixer", NULL, "DIN1"},
{"SDIN2 Input Mixer", NULL, "DIN2"},
{"Input Mux", "SDIN1", "SDIN1 Input Mixer"},
{"Input Mux", "SDIN2", "SDIN2 Input Mixer"},
{"DAC", "Switch", "Input Mux"},
{"SPDIF", "Switch", "Input Mux"},
{"LINEOUTL", NULL, "DAC"},
{"LINEOUTR", NULL, "DAC"},
{"SPDIFOUT", NULL, "SPDIF"},
{"ADC Mux", "LINEIN", "LINEINL"},
{"ADC Mux", "LINEIN", "LINEINR"},
{"ADC Mux", "MIC", "MICL"},
{"ADC Mux", "MIC", "MICR"},
{"ADC", NULL, "ADC Mux"},
{"DOUT", NULL, "ADC"},
{"DAI1 Capture", NULL, "DOUT"},
{"DAI2 Capture", NULL, "DOUT"},
/* Loopback */
{"Loopback", "Switch", "ADC"},
{"DAC", NULL, "Loopback"},
};
struct cs4265_clk_para {
u32 mclk;
u32 rate;
u8 fm_mode; /* values 1, 2, or 4 */
u8 mclkdiv;
};
static const struct cs4265_clk_para clk_map_table[] = {
/*32k*/
{8192000, 32000, 0, 0},
{12288000, 32000, 0, 1},
{16384000, 32000, 0, 2},
{24576000, 32000, 0, 3},
{32768000, 32000, 0, 4},
/*44.1k*/
{11289600, 44100, 0, 0},
{16934400, 44100, 0, 1},
{22579200, 44100, 0, 2},
{33868000, 44100, 0, 3},
{45158400, 44100, 0, 4},
/*48k*/
{12288000, 48000, 0, 0},
{18432000, 48000, 0, 1},
{24576000, 48000, 0, 2},
{36864000, 48000, 0, 3},
{49152000, 48000, 0, 4},
/*64k*/
{8192000, 64000, 1, 0},
{12288000, 64000, 1, 1},
{16934400, 64000, 1, 2},
{24576000, 64000, 1, 3},
{32768000, 64000, 1, 4},
/* 88.2k */
{11289600, 88200, 1, 0},
{16934400, 88200, 1, 1},
{22579200, 88200, 1, 2},
{33868000, 88200, 1, 3},
{45158400, 88200, 1, 4},
/* 96k */
{12288000, 96000, 1, 0},
{18432000, 96000, 1, 1},
{24576000, 96000, 1, 2},
{36864000, 96000, 1, 3},
{49152000, 96000, 1, 4},
/* 128k */
{8192000, 128000, 2, 0},
{12288000, 128000, 2, 1},
{16934400, 128000, 2, 2},
{24576000, 128000, 2, 3},
{32768000, 128000, 2, 4},
/* 176.4k */
{11289600, 176400, 2, 0},
{16934400, 176400, 2, 1},
{22579200, 176400, 2, 2},
{33868000, 176400, 2, 3},
{49152000, 176400, 2, 4},
/* 192k */
{12288000, 192000, 2, 0},
{18432000, 192000, 2, 1},
{24576000, 192000, 2, 2},
{36864000, 192000, 2, 3},
{49152000, 192000, 2, 4},
};
static int cs4265_get_clk_index(int mclk, int rate)
{
int i;
for (i = 0; i < ARRAY_SIZE(clk_map_table); i++) {
if (clk_map_table[i].rate == rate &&
clk_map_table[i].mclk == mclk)
return i;
}
return -EINVAL;
}
static int cs4265_set_sysclk(struct snd_soc_dai *codec_dai, int clk_id,
unsigned int freq, int dir)
{
struct snd_soc_codec *codec = codec_dai->codec;
struct cs4265_private *cs4265 = snd_soc_codec_get_drvdata(codec);
int i;
if (clk_id != 0) {
dev_err(codec->dev, "Invalid clk_id %d\n", clk_id);
return -EINVAL;
}
for (i = 0; i < ARRAY_SIZE(clk_map_table); i++) {
if (clk_map_table[i].mclk == freq) {
cs4265->sysclk = freq;
return 0;
}
}
cs4265->sysclk = 0;
dev_err(codec->dev, "Invalid freq parameter %d\n", freq);
return -EINVAL;
}
static int cs4265_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
struct cs4265_private *cs4265 = snd_soc_codec_get_drvdata(codec);
u8 iface = 0;
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBM_CFM:
snd_soc_update_bits(codec, CS4265_ADC_CTL,
CS4265_ADC_MASTER,
CS4265_ADC_MASTER);
break;
case SND_SOC_DAIFMT_CBS_CFS:
snd_soc_update_bits(codec, CS4265_ADC_CTL,
CS4265_ADC_MASTER,
0);
break;
default:
return -EINVAL;
}
/* interface format */
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
iface |= SND_SOC_DAIFMT_I2S;
break;
case SND_SOC_DAIFMT_RIGHT_J:
iface |= SND_SOC_DAIFMT_RIGHT_J;
break;
case SND_SOC_DAIFMT_LEFT_J:
iface |= SND_SOC_DAIFMT_LEFT_J;
break;
default:
return -EINVAL;
}
cs4265->format = iface;
return 0;
}
static int cs4265_digital_mute(struct snd_soc_dai *dai, int mute)
{
struct snd_soc_codec *codec = dai->codec;
if (mute) {
snd_soc_update_bits(codec, CS4265_DAC_CTL,
CS4265_DAC_CTL_MUTE,
CS4265_DAC_CTL_MUTE);
snd_soc_update_bits(codec, CS4265_SPDIF_CTL2,
CS4265_SPDIF_CTL2_MUTE,
CS4265_SPDIF_CTL2_MUTE);
} else {
snd_soc_update_bits(codec, CS4265_DAC_CTL,
CS4265_DAC_CTL_MUTE,
0);
snd_soc_update_bits(codec, CS4265_SPDIF_CTL2,
CS4265_SPDIF_CTL2_MUTE,
0);
}
return 0;
}
static int cs4265_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
struct cs4265_private *cs4265 = snd_soc_codec_get_drvdata(codec);
int index;
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE &&
((cs4265->format & SND_SOC_DAIFMT_FORMAT_MASK)
== SND_SOC_DAIFMT_RIGHT_J))
return -EINVAL;
index = cs4265_get_clk_index(cs4265->sysclk, params_rate(params));
if (index >= 0) {
snd_soc_update_bits(codec, CS4265_ADC_CTL,
CS4265_ADC_FM, clk_map_table[index].fm_mode << 6);
snd_soc_update_bits(codec, CS4265_MCLK_FREQ,
CS4265_MCLK_FREQ_MASK,
clk_map_table[index].mclkdiv << 4);
} else {
dev_err(codec->dev, "can't get correct mclk\n");
return -EINVAL;
}
switch (cs4265->format & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
snd_soc_update_bits(codec, CS4265_DAC_CTL,
CS4265_DAC_CTL_DIF, (1 << 4));
snd_soc_update_bits(codec, CS4265_ADC_CTL,
CS4265_ADC_DIF, (1 << 4));
snd_soc_update_bits(codec, CS4265_SPDIF_CTL2,
CS4265_SPDIF_CTL2_DIF, (1 << 6));
break;
case SND_SOC_DAIFMT_RIGHT_J:
if (params_width(params) == 16) {
snd_soc_update_bits(codec, CS4265_DAC_CTL,
CS4265_DAC_CTL_DIF, (1 << 5));
snd_soc_update_bits(codec, CS4265_SPDIF_CTL2,
CS4265_SPDIF_CTL2_DIF, (1 << 7));
} else {
snd_soc_update_bits(codec, CS4265_DAC_CTL,
CS4265_DAC_CTL_DIF, (3 << 5));
snd_soc_update_bits(codec, CS4265_SPDIF_CTL2,
CS4265_SPDIF_CTL2_DIF, (1 << 7));
}
break;
case SND_SOC_DAIFMT_LEFT_J:
snd_soc_update_bits(codec, CS4265_DAC_CTL,
CS4265_DAC_CTL_DIF, 0);
snd_soc_update_bits(codec, CS4265_ADC_CTL,
CS4265_ADC_DIF, 0);
snd_soc_update_bits(codec, CS4265_SPDIF_CTL2,
CS4265_SPDIF_CTL2_DIF, (1 << 6));
break;
default:
return -EINVAL;
}
return 0;
}
static int cs4265_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
switch (level) {
case SND_SOC_BIAS_ON:
break;
case SND_SOC_BIAS_PREPARE:
snd_soc_update_bits(codec, CS4265_PWRCTL,
CS4265_PWRCTL_PDN, 0);
break;
case SND_SOC_BIAS_STANDBY:
snd_soc_update_bits(codec, CS4265_PWRCTL,
CS4265_PWRCTL_PDN,
CS4265_PWRCTL_PDN);
break;
case SND_SOC_BIAS_OFF:
snd_soc_update_bits(codec, CS4265_PWRCTL,
CS4265_PWRCTL_PDN,
CS4265_PWRCTL_PDN);
break;
}
codec->dapm.bias_level = level;
return 0;
}
#define CS4265_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_64000 | \
SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | \
SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000)
#define CS4265_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE | \
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_U24_LE)
static const struct snd_soc_dai_ops cs4265_ops = {
.hw_params = cs4265_pcm_hw_params,
.digital_mute = cs4265_digital_mute,
.set_fmt = cs4265_set_fmt,
.set_sysclk = cs4265_set_sysclk,
};
static struct snd_soc_dai_driver cs4265_dai[] = {
{
.name = "cs4265-dai1",
.playback = {
.stream_name = "DAI1 Playback",
.channels_min = 1,
.channels_max = 2,
.rates = CS4265_RATES,
.formats = CS4265_FORMATS,
},
.capture = {
.stream_name = "DAI1 Capture",
.channels_min = 1,
.channels_max = 2,
.rates = CS4265_RATES,
.formats = CS4265_FORMATS,
},
.ops = &cs4265_ops,
},
{
.name = "cs4265-dai2",
.playback = {
.stream_name = "DAI2 Playback",
.channels_min = 1,
.channels_max = 2,
.rates = CS4265_RATES,
.formats = CS4265_FORMATS,
},
.capture = {
.stream_name = "DAI2 Capture",
.channels_min = 1,
.channels_max = 2,
.rates = CS4265_RATES,
.formats = CS4265_FORMATS,
},
.ops = &cs4265_ops,
},
};
static const struct snd_soc_codec_driver soc_codec_cs4265 = {
.set_bias_level = cs4265_set_bias_level,
.dapm_widgets = cs4265_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(cs4265_dapm_widgets),
.dapm_routes = cs4265_audio_map,
.num_dapm_routes = ARRAY_SIZE(cs4265_audio_map),
.controls = cs4265_snd_controls,
.num_controls = ARRAY_SIZE(cs4265_snd_controls),
};
static const struct regmap_config cs4265_regmap = {
.reg_bits = 8,
.val_bits = 8,
.max_register = CS4265_MAX_REGISTER,
.reg_defaults = cs4265_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(cs4265_reg_defaults),
.readable_reg = cs4265_readable_register,
.volatile_reg = cs4265_volatile_register,
.cache_type = REGCACHE_RBTREE,
};
static int cs4265_i2c_probe(struct i2c_client *i2c_client,
const struct i2c_device_id *id)
{
struct cs4265_private *cs4265;
int ret = 0;
unsigned int devid = 0;
unsigned int reg;
cs4265 = devm_kzalloc(&i2c_client->dev, sizeof(struct cs4265_private),
GFP_KERNEL);
if (cs4265 == NULL)
return -ENOMEM;
cs4265->dev = &i2c_client->dev;
cs4265->regmap = devm_regmap_init_i2c(i2c_client, &cs4265_regmap);
if (IS_ERR(cs4265->regmap)) {
ret = PTR_ERR(cs4265->regmap);
dev_err(&i2c_client->dev, "regmap_init() failed: %d\n", ret);
return ret;
}
cs4265->reset_gpio = devm_gpiod_get(&i2c_client->dev,
"reset-gpios");
if (IS_ERR(cs4265->reset_gpio)) {
ret = PTR_ERR(cs4265->reset_gpio);
if (ret != -ENOENT && ret != -ENOSYS)
return ret;
cs4265->reset_gpio = NULL;
} else {
ret = gpiod_direction_output(cs4265->reset_gpio, 0);
if (ret)
return ret;
mdelay(1);
gpiod_set_value_cansleep(cs4265->reset_gpio, 1);
}
i2c_set_clientdata(i2c_client, cs4265);
ret = regmap_read(cs4265->regmap, CS4265_CHIP_ID, &reg);
devid = reg & CS4265_CHIP_ID_MASK;
if (devid != CS4265_CHIP_ID_VAL) {
ret = -ENODEV;
dev_err(&i2c_client->dev,
"CS4265 Device ID (%X). Expected %X\n",
devid, CS4265_CHIP_ID);
return ret;
}
dev_info(&i2c_client->dev,
"CS4265 Version %x\n",
reg & CS4265_REV_ID_MASK);
regmap_write(cs4265->regmap, CS4265_PWRCTL, 0x0F);
ret = snd_soc_register_codec(&i2c_client->dev,
&soc_codec_cs4265, cs4265_dai,
ARRAY_SIZE(cs4265_dai));
return ret;
}
static int cs4265_i2c_remove(struct i2c_client *client)
{
snd_soc_unregister_codec(&client->dev);
return 0;
}
static const struct of_device_id cs4265_of_match[] = {
{ .compatible = "cirrus,cs4265", },
{ }
};
MODULE_DEVICE_TABLE(of, cs4265_of_match);
static const struct i2c_device_id cs4265_id[] = {
{ "cs4265", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, cs4265_id);
static struct i2c_driver cs4265_i2c_driver = {
.driver = {
.name = "cs4265",
.owner = THIS_MODULE,
.of_match_table = cs4265_of_match,
},
.id_table = cs4265_id,
.probe = cs4265_i2c_probe,
.remove = cs4265_i2c_remove,
};
module_i2c_driver(cs4265_i2c_driver);
MODULE_DESCRIPTION("ASoC CS4265 driver");
MODULE_AUTHOR("Paul Handrigan, Cirrus Logic Inc, <paul.handrigan@cirrus.com>");
MODULE_LICENSE("GPL");

64
sound/soc/codecs/cs4265.h Normal file
View file

@ -0,0 +1,64 @@
/*
* cs4265.h -- CS4265 ALSA SoC audio driver
*
* Copyright 2014 Cirrus Logic, Inc.
*
* Author: Paul Handrigan <paul.handrigan@cirrus.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#ifndef __CS4265_H__
#define __CS4265_H__
#define CS4265_CHIP_ID 0x1
#define CS4265_CHIP_ID_VAL 0xD0
#define CS4265_CHIP_ID_MASK 0xF0
#define CS4265_REV_ID_MASK 0x0F
#define CS4265_PWRCTL 0x02
#define CS4265_PWRCTL_PDN 1
#define CS4265_DAC_CTL 0x3
#define CS4265_DAC_CTL_MUTE (1 << 2)
#define CS4265_DAC_CTL_DIF (3 << 4)
#define CS4265_ADC_CTL 0x4
#define CS4265_ADC_MASTER 1
#define CS4265_ADC_DIF (1 << 4)
#define CS4265_ADC_FM (3 << 6)
#define CS4265_MCLK_FREQ 0x5
#define CS4265_MCLK_FREQ_MASK (7 << 4)
#define CS4265_SIG_SEL 0x6
#define CS4265_SIG_SEL_LOOP (1 << 1)
#define CS4265_CHB_PGA_CTL 0x7
#define CS4265_CHA_PGA_CTL 0x8
#define CS4265_ADC_CTL2 0x9
#define CS4265_DAC_CHA_VOL 0xA
#define CS4265_DAC_CHB_VOL 0xB
#define CS4265_DAC_CTL2 0xC
#define CS4265_INT_STATUS 0xD
#define CS4265_INT_MASK 0xE
#define CS4265_STATUS_MODE_MSB 0xF
#define CS4265_STATUS_MODE_LSB 0x10
#define CS4265_SPDIF_CTL1 0x11
#define CS4265_SPDIF_CTL2 0x12
#define CS4265_SPDIF_CTL2_MUTE (1 << 4)
#define CS4265_SPDIF_CTL2_DIF (3 << 6)
#define CS4265_C_DATA_BUFF 0x13
#define CS4265_MAX_REGISTER 0x2A
#endif

766
sound/soc/codecs/cs4270.c Normal file
View file

@ -0,0 +1,766 @@
/*
* CS4270 ALSA SoC (ASoC) codec driver
*
* Author: Timur Tabi <timur@freescale.com>
*
* Copyright 2007-2009 Freescale Semiconductor, Inc. This file is licensed
* under the terms of the GNU General Public License version 2. This
* program is licensed "as is" without any warranty of any kind, whether
* express or implied.
*
* This is an ASoC device driver for the Cirrus Logic CS4270 codec.
*
* Current features/limitations:
*
* - Software mode is supported. Stand-alone mode is not supported.
* - Only I2C is supported, not SPI
* - Support for master and slave mode
* - The machine driver's 'startup' function must call
* cs4270_set_dai_sysclk() with the value of MCLK.
* - Only I2S and left-justified modes are supported
* - Power management is supported
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/soc.h>
#include <sound/initval.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/regulator/consumer.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
/*
* The codec isn't really big-endian or little-endian, since the I2S
* interface requires data to be sent serially with the MSbit first.
* However, to support BE and LE I2S devices, we specify both here. That
* way, ALSA will always match the bit patterns.
*/
#define CS4270_FORMATS (SNDRV_PCM_FMTBIT_S8 | \
SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | \
SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S18_3BE | \
SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE | \
SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE | \
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE)
/* CS4270 registers addresses */
#define CS4270_CHIPID 0x01 /* Chip ID */
#define CS4270_PWRCTL 0x02 /* Power Control */
#define CS4270_MODE 0x03 /* Mode Control */
#define CS4270_FORMAT 0x04 /* Serial Format, ADC/DAC Control */
#define CS4270_TRANS 0x05 /* Transition Control */
#define CS4270_MUTE 0x06 /* Mute Control */
#define CS4270_VOLA 0x07 /* DAC Channel A Volume Control */
#define CS4270_VOLB 0x08 /* DAC Channel B Volume Control */
#define CS4270_FIRSTREG 0x01
#define CS4270_LASTREG 0x08
#define CS4270_NUMREGS (CS4270_LASTREG - CS4270_FIRSTREG + 1)
#define CS4270_I2C_INCR 0x80
/* Bit masks for the CS4270 registers */
#define CS4270_CHIPID_ID 0xF0
#define CS4270_CHIPID_REV 0x0F
#define CS4270_PWRCTL_FREEZE 0x80
#define CS4270_PWRCTL_PDN_ADC 0x20
#define CS4270_PWRCTL_PDN_DAC 0x02
#define CS4270_PWRCTL_PDN 0x01
#define CS4270_PWRCTL_PDN_ALL \
(CS4270_PWRCTL_PDN_ADC | CS4270_PWRCTL_PDN_DAC | CS4270_PWRCTL_PDN)
#define CS4270_MODE_SPEED_MASK 0x30
#define CS4270_MODE_1X 0x00
#define CS4270_MODE_2X 0x10
#define CS4270_MODE_4X 0x20
#define CS4270_MODE_SLAVE 0x30
#define CS4270_MODE_DIV_MASK 0x0E
#define CS4270_MODE_DIV1 0x00
#define CS4270_MODE_DIV15 0x02
#define CS4270_MODE_DIV2 0x04
#define CS4270_MODE_DIV3 0x06
#define CS4270_MODE_DIV4 0x08
#define CS4270_MODE_POPGUARD 0x01
#define CS4270_FORMAT_FREEZE_A 0x80
#define CS4270_FORMAT_FREEZE_B 0x40
#define CS4270_FORMAT_LOOPBACK 0x20
#define CS4270_FORMAT_DAC_MASK 0x18
#define CS4270_FORMAT_DAC_LJ 0x00
#define CS4270_FORMAT_DAC_I2S 0x08
#define CS4270_FORMAT_DAC_RJ16 0x18
#define CS4270_FORMAT_DAC_RJ24 0x10
#define CS4270_FORMAT_ADC_MASK 0x01
#define CS4270_FORMAT_ADC_LJ 0x00
#define CS4270_FORMAT_ADC_I2S 0x01
#define CS4270_TRANS_ONE_VOL 0x80
#define CS4270_TRANS_SOFT 0x40
#define CS4270_TRANS_ZERO 0x20
#define CS4270_TRANS_INV_ADC_A 0x08
#define CS4270_TRANS_INV_ADC_B 0x10
#define CS4270_TRANS_INV_DAC_A 0x02
#define CS4270_TRANS_INV_DAC_B 0x04
#define CS4270_TRANS_DEEMPH 0x01
#define CS4270_MUTE_AUTO 0x20
#define CS4270_MUTE_ADC_A 0x08
#define CS4270_MUTE_ADC_B 0x10
#define CS4270_MUTE_POLARITY 0x04
#define CS4270_MUTE_DAC_A 0x01
#define CS4270_MUTE_DAC_B 0x02
/* Power-on default values for the registers
*
* This array contains the power-on default values of the registers, with the
* exception of the "CHIPID" register (01h). The lower four bits of that
* register contain the hardware revision, so it is treated as volatile.
*/
static const struct reg_default cs4270_reg_defaults[] = {
{ 2, 0x00 },
{ 3, 0x30 },
{ 4, 0x00 },
{ 5, 0x60 },
{ 6, 0x20 },
{ 7, 0x00 },
{ 8, 0x00 },
};
static const char *supply_names[] = {
"va", "vd", "vlc"
};
/* Private data for the CS4270 */
struct cs4270_private {
struct regmap *regmap;
unsigned int mclk; /* Input frequency of the MCLK pin */
unsigned int mode; /* The mode (I2S or left-justified) */
unsigned int slave_mode;
unsigned int manual_mute;
/* power domain regulators */
struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)];
};
static const struct snd_soc_dapm_widget cs4270_dapm_widgets[] = {
SND_SOC_DAPM_INPUT("AINL"),
SND_SOC_DAPM_INPUT("AINR"),
SND_SOC_DAPM_OUTPUT("AOUTL"),
SND_SOC_DAPM_OUTPUT("AOUTR"),
};
static const struct snd_soc_dapm_route cs4270_dapm_routes[] = {
{ "Capture", NULL, "AINA" },
{ "Capture", NULL, "AINB" },
{ "AOUTA", NULL, "Playback" },
{ "AOUTB", NULL, "Playback" },
};
/**
* struct cs4270_mode_ratios - clock ratio tables
* @ratio: the ratio of MCLK to the sample rate
* @speed_mode: the Speed Mode bits to set in the Mode Control register for
* this ratio
* @mclk: the Ratio Select bits to set in the Mode Control register for this
* ratio
*
* The data for this chart is taken from Table 5 of the CS4270 reference
* manual.
*
* This table is used to determine how to program the Mode Control register.
* It is also used by cs4270_set_dai_sysclk() to tell ALSA which sampling
* rates the CS4270 currently supports.
*
* @speed_mode is the corresponding bit pattern to be written to the
* MODE bits of the Mode Control Register
*
* @mclk is the corresponding bit pattern to be wirten to the MCLK bits of
* the Mode Control Register.
*
* In situations where a single ratio is represented by multiple speed
* modes, we favor the slowest speed. E.g, for a ratio of 128, we pick
* double-speed instead of quad-speed. However, the CS4270 errata states
* that divide-By-1.5 can cause failures, so we avoid that mode where
* possible.
*
* Errata: There is an errata for the CS4270 where divide-by-1.5 does not
* work if Vd is 3.3V. If this effects you, select the
* CONFIG_SND_SOC_CS4270_VD33_ERRATA Kconfig option, and the driver will
* never select any sample rates that require divide-by-1.5.
*/
struct cs4270_mode_ratios {
unsigned int ratio;
u8 speed_mode;
u8 mclk;
};
static struct cs4270_mode_ratios cs4270_mode_ratios[] = {
{64, CS4270_MODE_4X, CS4270_MODE_DIV1},
#ifndef CONFIG_SND_SOC_CS4270_VD33_ERRATA
{96, CS4270_MODE_4X, CS4270_MODE_DIV15},
#endif
{128, CS4270_MODE_2X, CS4270_MODE_DIV1},
{192, CS4270_MODE_4X, CS4270_MODE_DIV3},
{256, CS4270_MODE_1X, CS4270_MODE_DIV1},
{384, CS4270_MODE_2X, CS4270_MODE_DIV3},
{512, CS4270_MODE_1X, CS4270_MODE_DIV2},
{768, CS4270_MODE_1X, CS4270_MODE_DIV3},
{1024, CS4270_MODE_1X, CS4270_MODE_DIV4}
};
/* The number of MCLK/LRCK ratios supported by the CS4270 */
#define NUM_MCLK_RATIOS ARRAY_SIZE(cs4270_mode_ratios)
static bool cs4270_reg_is_readable(struct device *dev, unsigned int reg)
{
return (reg >= CS4270_FIRSTREG) && (reg <= CS4270_LASTREG);
}
static bool cs4270_reg_is_volatile(struct device *dev, unsigned int reg)
{
/* Unreadable registers are considered volatile */
if ((reg < CS4270_FIRSTREG) || (reg > CS4270_LASTREG))
return 1;
return reg == CS4270_CHIPID;
}
/**
* cs4270_set_dai_sysclk - determine the CS4270 samples rates.
* @codec_dai: the codec DAI
* @clk_id: the clock ID (ignored)
* @freq: the MCLK input frequency
* @dir: the clock direction (ignored)
*
* This function is used to tell the codec driver what the input MCLK
* frequency is.
*
* The value of MCLK is used to determine which sample rates are supported
* by the CS4270. The ratio of MCLK / Fs must be equal to one of nine
* supported values - 64, 96, 128, 192, 256, 384, 512, 768, and 1024.
*
* This function calculates the nine ratios and determines which ones match
* a standard sample rate. If there's a match, then it is added to the list
* of supported sample rates.
*
* This function must be called by the machine driver's 'startup' function,
* otherwise the list of supported sample rates will not be available in
* time for ALSA.
*
* For setups with variable MCLKs, pass 0 as 'freq' argument. This will cause
* theoretically possible sample rates to be enabled. Call it again with a
* proper value set one the external clock is set (most probably you would do
* that from a machine's driver 'hw_param' hook.
*/
static int cs4270_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = codec_dai->codec;
struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec);
cs4270->mclk = freq;
return 0;
}
/**
* cs4270_set_dai_fmt - configure the codec for the selected audio format
* @codec_dai: the codec DAI
* @format: a SND_SOC_DAIFMT_x value indicating the data format
*
* This function takes a bitmask of SND_SOC_DAIFMT_x bits and programs the
* codec accordingly.
*
* Currently, this function only supports SND_SOC_DAIFMT_I2S and
* SND_SOC_DAIFMT_LEFT_J. The CS4270 codec also supports right-justified
* data for playback only, but ASoC currently does not support different
* formats for playback vs. record.
*/
static int cs4270_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int format)
{
struct snd_soc_codec *codec = codec_dai->codec;
struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec);
/* set DAI format */
switch (format & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
case SND_SOC_DAIFMT_LEFT_J:
cs4270->mode = format & SND_SOC_DAIFMT_FORMAT_MASK;
break;
default:
dev_err(codec->dev, "invalid dai format\n");
return -EINVAL;
}
/* set master/slave audio interface */
switch (format & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBS_CFS:
cs4270->slave_mode = 1;
break;
case SND_SOC_DAIFMT_CBM_CFM:
cs4270->slave_mode = 0;
break;
default:
/* all other modes are unsupported by the hardware */
dev_err(codec->dev, "Unknown master/slave configuration\n");
return -EINVAL;
}
return 0;
}
/**
* cs4270_hw_params - program the CS4270 with the given hardware parameters.
* @substream: the audio stream
* @params: the hardware parameters to set
* @dai: the SOC DAI (ignored)
*
* This function programs the hardware with the values provided.
* Specifically, the sample rate and the data format.
*
* The .ops functions are used to provide board-specific data, like input
* frequencies, to this driver. This function takes that information,
* combines it with the hardware parameters provided, and programs the
* hardware accordingly.
*/
static int cs4270_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec);
int ret;
unsigned int i;
unsigned int rate;
unsigned int ratio;
int reg;
/* Figure out which MCLK/LRCK ratio to use */
rate = params_rate(params); /* Sampling rate, in Hz */
ratio = cs4270->mclk / rate; /* MCLK/LRCK ratio */
for (i = 0; i < NUM_MCLK_RATIOS; i++) {
if (cs4270_mode_ratios[i].ratio == ratio)
break;
}
if (i == NUM_MCLK_RATIOS) {
/* We did not find a matching ratio */
dev_err(codec->dev, "could not find matching ratio\n");
return -EINVAL;
}
/* Set the sample rate */
reg = snd_soc_read(codec, CS4270_MODE);
reg &= ~(CS4270_MODE_SPEED_MASK | CS4270_MODE_DIV_MASK);
reg |= cs4270_mode_ratios[i].mclk;
if (cs4270->slave_mode)
reg |= CS4270_MODE_SLAVE;
else
reg |= cs4270_mode_ratios[i].speed_mode;
ret = snd_soc_write(codec, CS4270_MODE, reg);
if (ret < 0) {
dev_err(codec->dev, "i2c write failed\n");
return ret;
}
/* Set the DAI format */
reg = snd_soc_read(codec, CS4270_FORMAT);
reg &= ~(CS4270_FORMAT_DAC_MASK | CS4270_FORMAT_ADC_MASK);
switch (cs4270->mode) {
case SND_SOC_DAIFMT_I2S:
reg |= CS4270_FORMAT_DAC_I2S | CS4270_FORMAT_ADC_I2S;
break;
case SND_SOC_DAIFMT_LEFT_J:
reg |= CS4270_FORMAT_DAC_LJ | CS4270_FORMAT_ADC_LJ;
break;
default:
dev_err(codec->dev, "unknown dai format\n");
return -EINVAL;
}
ret = snd_soc_write(codec, CS4270_FORMAT, reg);
if (ret < 0) {
dev_err(codec->dev, "i2c write failed\n");
return ret;
}
return ret;
}
/**
* cs4270_dai_mute - enable/disable the CS4270 external mute
* @dai: the SOC DAI
* @mute: 0 = disable mute, 1 = enable mute
*
* This function toggles the mute bits in the MUTE register. The CS4270's
* mute capability is intended for external muting circuitry, so if the
* board does not have the MUTEA or MUTEB pins connected to such circuitry,
* then this function will do nothing.
*/
static int cs4270_dai_mute(struct snd_soc_dai *dai, int mute)
{
struct snd_soc_codec *codec = dai->codec;
struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec);
int reg6;
reg6 = snd_soc_read(codec, CS4270_MUTE);
if (mute)
reg6 |= CS4270_MUTE_DAC_A | CS4270_MUTE_DAC_B;
else {
reg6 &= ~(CS4270_MUTE_DAC_A | CS4270_MUTE_DAC_B);
reg6 |= cs4270->manual_mute;
}
return snd_soc_write(codec, CS4270_MUTE, reg6);
}
/**
* cs4270_soc_put_mute - put callback for the 'Master Playback switch'
* alsa control.
* @kcontrol: mixer control
* @ucontrol: control element information
*
* This function basically passes the arguments on to the generic
* snd_soc_put_volsw() function and saves the mute information in
* our private data structure. This is because we want to prevent
* cs4270_dai_mute() neglecting the user's decision to manually
* mute the codec's output.
*
* Returns 0 for success.
*/
static int cs4270_soc_put_mute(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec);
int left = !ucontrol->value.integer.value[0];
int right = !ucontrol->value.integer.value[1];
cs4270->manual_mute = (left ? CS4270_MUTE_DAC_A : 0) |
(right ? CS4270_MUTE_DAC_B : 0);
return snd_soc_put_volsw(kcontrol, ucontrol);
}
/* A list of non-DAPM controls that the CS4270 supports */
static const struct snd_kcontrol_new cs4270_snd_controls[] = {
SOC_DOUBLE_R("Master Playback Volume",
CS4270_VOLA, CS4270_VOLB, 0, 0xFF, 1),
SOC_SINGLE("Digital Sidetone Switch", CS4270_FORMAT, 5, 1, 0),
SOC_SINGLE("Soft Ramp Switch", CS4270_TRANS, 6, 1, 0),
SOC_SINGLE("Zero Cross Switch", CS4270_TRANS, 5, 1, 0),
SOC_SINGLE("De-emphasis filter", CS4270_TRANS, 0, 1, 0),
SOC_SINGLE("Popguard Switch", CS4270_MODE, 0, 1, 1),
SOC_SINGLE("Auto-Mute Switch", CS4270_MUTE, 5, 1, 0),
SOC_DOUBLE("Master Capture Switch", CS4270_MUTE, 3, 4, 1, 1),
SOC_DOUBLE_EXT("Master Playback Switch", CS4270_MUTE, 0, 1, 1, 1,
snd_soc_get_volsw, cs4270_soc_put_mute),
};
static const struct snd_soc_dai_ops cs4270_dai_ops = {
.hw_params = cs4270_hw_params,
.set_sysclk = cs4270_set_dai_sysclk,
.set_fmt = cs4270_set_dai_fmt,
.digital_mute = cs4270_dai_mute,
};
static struct snd_soc_dai_driver cs4270_dai = {
.name = "cs4270-hifi",
.playback = {
.stream_name = "Playback",
.channels_min = 2,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_CONTINUOUS,
.rate_min = 4000,
.rate_max = 216000,
.formats = CS4270_FORMATS,
},
.capture = {
.stream_name = "Capture",
.channels_min = 2,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_CONTINUOUS,
.rate_min = 4000,
.rate_max = 216000,
.formats = CS4270_FORMATS,
},
.ops = &cs4270_dai_ops,
};
/**
* cs4270_probe - ASoC probe function
* @pdev: platform device
*
* This function is called when ASoC has all the pieces it needs to
* instantiate a sound driver.
*/
static int cs4270_probe(struct snd_soc_codec *codec)
{
struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec);
int ret;
/* Disable auto-mute. This feature appears to be buggy. In some
* situations, auto-mute will not deactivate when it should, so we want
* this feature disabled by default. An application (e.g. alsactl) can
* re-enabled it by using the controls.
*/
ret = snd_soc_update_bits(codec, CS4270_MUTE, CS4270_MUTE_AUTO, 0);
if (ret < 0) {
dev_err(codec->dev, "i2c write failed\n");
return ret;
}
/* Disable automatic volume control. The hardware enables, and it
* causes volume change commands to be delayed, sometimes until after
* playback has started. An application (e.g. alsactl) can
* re-enabled it by using the controls.
*/
ret = snd_soc_update_bits(codec, CS4270_TRANS,
CS4270_TRANS_SOFT | CS4270_TRANS_ZERO, 0);
if (ret < 0) {
dev_err(codec->dev, "i2c write failed\n");
return ret;
}
ret = regulator_bulk_enable(ARRAY_SIZE(cs4270->supplies),
cs4270->supplies);
return ret;
}
/**
* cs4270_remove - ASoC remove function
* @pdev: platform device
*
* This function is the counterpart to cs4270_probe().
*/
static int cs4270_remove(struct snd_soc_codec *codec)
{
struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec);
regulator_bulk_disable(ARRAY_SIZE(cs4270->supplies), cs4270->supplies);
return 0;
};
#ifdef CONFIG_PM
/* This suspend/resume implementation can handle both - a simple standby
* where the codec remains powered, and a full suspend, where the voltage
* domain the codec is connected to is teared down and/or any other hardware
* reset condition is asserted.
*
* The codec's own power saving features are enabled in the suspend callback,
* and all registers are written back to the hardware when resuming.
*/
static int cs4270_soc_suspend(struct snd_soc_codec *codec)
{
struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec);
int reg, ret;
reg = snd_soc_read(codec, CS4270_PWRCTL) | CS4270_PWRCTL_PDN_ALL;
if (reg < 0)
return reg;
ret = snd_soc_write(codec, CS4270_PWRCTL, reg);
if (ret < 0)
return ret;
regulator_bulk_disable(ARRAY_SIZE(cs4270->supplies),
cs4270->supplies);
return 0;
}
static int cs4270_soc_resume(struct snd_soc_codec *codec)
{
struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec);
int reg, ret;
ret = regulator_bulk_enable(ARRAY_SIZE(cs4270->supplies),
cs4270->supplies);
if (ret != 0)
return ret;
/* In case the device was put to hard reset during sleep, we need to
* wait 500ns here before any I2C communication. */
ndelay(500);
/* first restore the entire register cache ... */
regcache_sync(cs4270->regmap);
/* ... then disable the power-down bits */
reg = snd_soc_read(codec, CS4270_PWRCTL);
reg &= ~CS4270_PWRCTL_PDN_ALL;
return snd_soc_write(codec, CS4270_PWRCTL, reg);
}
#else
#define cs4270_soc_suspend NULL
#define cs4270_soc_resume NULL
#endif /* CONFIG_PM */
/*
* ASoC codec driver structure
*/
static const struct snd_soc_codec_driver soc_codec_device_cs4270 = {
.probe = cs4270_probe,
.remove = cs4270_remove,
.suspend = cs4270_soc_suspend,
.resume = cs4270_soc_resume,
.controls = cs4270_snd_controls,
.num_controls = ARRAY_SIZE(cs4270_snd_controls),
.dapm_widgets = cs4270_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(cs4270_dapm_widgets),
.dapm_routes = cs4270_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(cs4270_dapm_routes),
};
/*
* cs4270_of_match - the device tree bindings
*/
static const struct of_device_id cs4270_of_match[] = {
{ .compatible = "cirrus,cs4270", },
{ }
};
MODULE_DEVICE_TABLE(of, cs4270_of_match);
static const struct regmap_config cs4270_regmap = {
.reg_bits = 8,
.val_bits = 8,
.max_register = CS4270_LASTREG,
.reg_defaults = cs4270_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(cs4270_reg_defaults),
.cache_type = REGCACHE_RBTREE,
.readable_reg = cs4270_reg_is_readable,
.volatile_reg = cs4270_reg_is_volatile,
};
/**
* cs4270_i2c_probe - initialize the I2C interface of the CS4270
* @i2c_client: the I2C client object
* @id: the I2C device ID (ignored)
*
* This function is called whenever the I2C subsystem finds a device that
* matches the device ID given via a prior call to i2c_add_driver().
*/
static int cs4270_i2c_probe(struct i2c_client *i2c_client,
const struct i2c_device_id *id)
{
struct device_node *np = i2c_client->dev.of_node;
struct cs4270_private *cs4270;
unsigned int val;
int ret, i;
cs4270 = devm_kzalloc(&i2c_client->dev, sizeof(struct cs4270_private),
GFP_KERNEL);
if (!cs4270)
return -ENOMEM;
/* get the power supply regulators */
for (i = 0; i < ARRAY_SIZE(supply_names); i++)
cs4270->supplies[i].supply = supply_names[i];
ret = devm_regulator_bulk_get(&i2c_client->dev,
ARRAY_SIZE(cs4270->supplies),
cs4270->supplies);
if (ret < 0)
return ret;
/* See if we have a way to bring the codec out of reset */
if (np) {
enum of_gpio_flags flags;
int gpio = of_get_named_gpio_flags(np, "reset-gpio", 0, &flags);
if (gpio_is_valid(gpio)) {
ret = devm_gpio_request_one(&i2c_client->dev, gpio,
flags & OF_GPIO_ACTIVE_LOW ?
GPIOF_OUT_INIT_LOW : GPIOF_OUT_INIT_HIGH,
"cs4270 reset");
if (ret < 0)
return ret;
}
}
cs4270->regmap = devm_regmap_init_i2c(i2c_client, &cs4270_regmap);
if (IS_ERR(cs4270->regmap))
return PTR_ERR(cs4270->regmap);
/* Verify that we have a CS4270 */
ret = regmap_read(cs4270->regmap, CS4270_CHIPID, &val);
if (ret < 0) {
dev_err(&i2c_client->dev, "failed to read i2c at addr %X\n",
i2c_client->addr);
return ret;
}
/* The top four bits of the chip ID should be 1100. */
if ((val & 0xF0) != 0xC0) {
dev_err(&i2c_client->dev, "device at addr %X is not a CS4270\n",
i2c_client->addr);
return -ENODEV;
}
dev_info(&i2c_client->dev, "found device at i2c address %X\n",
i2c_client->addr);
dev_info(&i2c_client->dev, "hardware revision %X\n", val & 0xF);
i2c_set_clientdata(i2c_client, cs4270);
ret = snd_soc_register_codec(&i2c_client->dev,
&soc_codec_device_cs4270, &cs4270_dai, 1);
return ret;
}
/**
* cs4270_i2c_remove - remove an I2C device
* @i2c_client: the I2C client object
*
* This function is the counterpart to cs4270_i2c_probe().
*/
static int cs4270_i2c_remove(struct i2c_client *i2c_client)
{
snd_soc_unregister_codec(&i2c_client->dev);
return 0;
}
/*
* cs4270_id - I2C device IDs supported by this driver
*/
static const struct i2c_device_id cs4270_id[] = {
{"cs4270", 0},
{}
};
MODULE_DEVICE_TABLE(i2c, cs4270_id);
/*
* cs4270_i2c_driver - I2C device identification
*
* This structure tells the I2C subsystem how to identify and support a
* given I2C device type.
*/
static struct i2c_driver cs4270_i2c_driver = {
.driver = {
.name = "cs4270",
.owner = THIS_MODULE,
.of_match_table = cs4270_of_match,
},
.id_table = cs4270_id,
.probe = cs4270_i2c_probe,
.remove = cs4270_i2c_remove,
};
module_i2c_driver(cs4270_i2c_driver);
MODULE_AUTHOR("Timur Tabi <timur@freescale.com>");
MODULE_DESCRIPTION("Cirrus Logic CS4270 ALSA SoC Codec Driver");
MODULE_LICENSE("GPL");

797
sound/soc/codecs/cs4271.c Normal file
View file

@ -0,0 +1,797 @@
/*
* CS4271 ASoC codec driver
*
* Copyright (c) 2010 Alexander Sverdlin <subaparts@yandex.ru>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* This driver support CS4271 codec being master or slave, working
* in control port mode, connected either via SPI or I2C.
* The data format accepted is I2S or left-justified.
* DAPM support not implemented.
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/i2c.h>
#include <linux/spi/spi.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#include <sound/pcm.h>
#include <sound/soc.h>
#include <sound/tlv.h>
#include <sound/cs4271.h>
#define CS4271_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S24_LE | \
SNDRV_PCM_FMTBIT_S32_LE)
#define CS4271_PCM_RATES SNDRV_PCM_RATE_8000_192000
/*
* CS4271 registers
*/
#define CS4271_MODE1 0x01 /* Mode Control 1 */
#define CS4271_DACCTL 0x02 /* DAC Control */
#define CS4271_DACVOL 0x03 /* DAC Volume & Mixing Control */
#define CS4271_VOLA 0x04 /* DAC Channel A Volume Control */
#define CS4271_VOLB 0x05 /* DAC Channel B Volume Control */
#define CS4271_ADCCTL 0x06 /* ADC Control */
#define CS4271_MODE2 0x07 /* Mode Control 2 */
#define CS4271_CHIPID 0x08 /* Chip ID */
#define CS4271_FIRSTREG CS4271_MODE1
#define CS4271_LASTREG CS4271_MODE2
#define CS4271_NR_REGS ((CS4271_LASTREG & 0xFF) + 1)
/* Bit masks for the CS4271 registers */
#define CS4271_MODE1_MODE_MASK 0xC0
#define CS4271_MODE1_MODE_1X 0x00
#define CS4271_MODE1_MODE_2X 0x80
#define CS4271_MODE1_MODE_4X 0xC0
#define CS4271_MODE1_DIV_MASK 0x30
#define CS4271_MODE1_DIV_1 0x00
#define CS4271_MODE1_DIV_15 0x10
#define CS4271_MODE1_DIV_2 0x20
#define CS4271_MODE1_DIV_3 0x30
#define CS4271_MODE1_MASTER 0x08
#define CS4271_MODE1_DAC_DIF_MASK 0x07
#define CS4271_MODE1_DAC_DIF_LJ 0x00
#define CS4271_MODE1_DAC_DIF_I2S 0x01
#define CS4271_MODE1_DAC_DIF_RJ16 0x02
#define CS4271_MODE1_DAC_DIF_RJ24 0x03
#define CS4271_MODE1_DAC_DIF_RJ20 0x04
#define CS4271_MODE1_DAC_DIF_RJ18 0x05
#define CS4271_DACCTL_AMUTE 0x80
#define CS4271_DACCTL_IF_SLOW 0x40
#define CS4271_DACCTL_DEM_MASK 0x30
#define CS4271_DACCTL_DEM_DIS 0x00
#define CS4271_DACCTL_DEM_441 0x10
#define CS4271_DACCTL_DEM_48 0x20
#define CS4271_DACCTL_DEM_32 0x30
#define CS4271_DACCTL_SVRU 0x08
#define CS4271_DACCTL_SRD 0x04
#define CS4271_DACCTL_INVA 0x02
#define CS4271_DACCTL_INVB 0x01
#define CS4271_DACVOL_BEQUA 0x40
#define CS4271_DACVOL_SOFT 0x20
#define CS4271_DACVOL_ZEROC 0x10
#define CS4271_DACVOL_ATAPI_MASK 0x0F
#define CS4271_DACVOL_ATAPI_M_M 0x00
#define CS4271_DACVOL_ATAPI_M_BR 0x01
#define CS4271_DACVOL_ATAPI_M_BL 0x02
#define CS4271_DACVOL_ATAPI_M_BLR2 0x03
#define CS4271_DACVOL_ATAPI_AR_M 0x04
#define CS4271_DACVOL_ATAPI_AR_BR 0x05
#define CS4271_DACVOL_ATAPI_AR_BL 0x06
#define CS4271_DACVOL_ATAPI_AR_BLR2 0x07
#define CS4271_DACVOL_ATAPI_AL_M 0x08
#define CS4271_DACVOL_ATAPI_AL_BR 0x09
#define CS4271_DACVOL_ATAPI_AL_BL 0x0A
#define CS4271_DACVOL_ATAPI_AL_BLR2 0x0B
#define CS4271_DACVOL_ATAPI_ALR2_M 0x0C
#define CS4271_DACVOL_ATAPI_ALR2_BR 0x0D
#define CS4271_DACVOL_ATAPI_ALR2_BL 0x0E
#define CS4271_DACVOL_ATAPI_ALR2_BLR2 0x0F
#define CS4271_VOLA_MUTE 0x80
#define CS4271_VOLA_VOL_MASK 0x7F
#define CS4271_VOLB_MUTE 0x80
#define CS4271_VOLB_VOL_MASK 0x7F
#define CS4271_ADCCTL_DITHER16 0x20
#define CS4271_ADCCTL_ADC_DIF_MASK 0x10
#define CS4271_ADCCTL_ADC_DIF_LJ 0x00
#define CS4271_ADCCTL_ADC_DIF_I2S 0x10
#define CS4271_ADCCTL_MUTEA 0x08
#define CS4271_ADCCTL_MUTEB 0x04
#define CS4271_ADCCTL_HPFDA 0x02
#define CS4271_ADCCTL_HPFDB 0x01
#define CS4271_MODE2_LOOP 0x10
#define CS4271_MODE2_MUTECAEQUB 0x08
#define CS4271_MODE2_FREEZE 0x04
#define CS4271_MODE2_CPEN 0x02
#define CS4271_MODE2_PDN 0x01
#define CS4271_CHIPID_PART_MASK 0xF0
#define CS4271_CHIPID_REV_MASK 0x0F
/*
* Default CS4271 power-up configuration
* Array contains non-existing in hw register at address 0
* Array do not include Chip ID, as codec driver does not use
* registers read operations at all
*/
static const struct reg_default cs4271_reg_defaults[] = {
{ CS4271_MODE1, 0, },
{ CS4271_DACCTL, CS4271_DACCTL_AMUTE, },
{ CS4271_DACVOL, CS4271_DACVOL_SOFT | CS4271_DACVOL_ATAPI_AL_BR, },
{ CS4271_VOLA, 0, },
{ CS4271_VOLB, 0, },
{ CS4271_ADCCTL, 0, },
{ CS4271_MODE2, 0, },
};
static bool cs4271_volatile_reg(struct device *dev, unsigned int reg)
{
return reg == CS4271_CHIPID;
}
struct cs4271_private {
unsigned int mclk;
bool master;
bool deemph;
struct regmap *regmap;
/* Current sample rate for de-emphasis control */
int rate;
/* GPIO driving Reset pin, if any */
int gpio_nreset;
/* GPIO that disable serial bus, if any */
int gpio_disable;
/* enable soft reset workaround */
bool enable_soft_reset;
};
static const struct snd_soc_dapm_widget cs4271_dapm_widgets[] = {
SND_SOC_DAPM_INPUT("AINA"),
SND_SOC_DAPM_INPUT("AINB"),
SND_SOC_DAPM_OUTPUT("AOUTA+"),
SND_SOC_DAPM_OUTPUT("AOUTA-"),
SND_SOC_DAPM_OUTPUT("AOUTB+"),
SND_SOC_DAPM_OUTPUT("AOUTB-"),
};
static const struct snd_soc_dapm_route cs4271_dapm_routes[] = {
{ "Capture", NULL, "AINA" },
{ "Capture", NULL, "AINB" },
{ "AOUTA+", NULL, "Playback" },
{ "AOUTA-", NULL, "Playback" },
{ "AOUTB+", NULL, "Playback" },
{ "AOUTB-", NULL, "Playback" },
};
/*
* @freq is the desired MCLK rate
* MCLK rate should (c) be the sample rate, multiplied by one of the
* ratios listed in cs4271_mclk_fs_ratios table
*/
static int cs4271_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = codec_dai->codec;
struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
cs4271->mclk = freq;
return 0;
}
static int cs4271_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int format)
{
struct snd_soc_codec *codec = codec_dai->codec;
struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
unsigned int val = 0;
int ret;
switch (format & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBS_CFS:
cs4271->master = 0;
break;
case SND_SOC_DAIFMT_CBM_CFM:
cs4271->master = 1;
val |= CS4271_MODE1_MASTER;
break;
default:
dev_err(codec->dev, "Invalid DAI format\n");
return -EINVAL;
}
switch (format & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_LEFT_J:
val |= CS4271_MODE1_DAC_DIF_LJ;
ret = regmap_update_bits(cs4271->regmap, CS4271_ADCCTL,
CS4271_ADCCTL_ADC_DIF_MASK, CS4271_ADCCTL_ADC_DIF_LJ);
if (ret < 0)
return ret;
break;
case SND_SOC_DAIFMT_I2S:
val |= CS4271_MODE1_DAC_DIF_I2S;
ret = regmap_update_bits(cs4271->regmap, CS4271_ADCCTL,
CS4271_ADCCTL_ADC_DIF_MASK, CS4271_ADCCTL_ADC_DIF_I2S);
if (ret < 0)
return ret;
break;
default:
dev_err(codec->dev, "Invalid DAI format\n");
return -EINVAL;
}
ret = regmap_update_bits(cs4271->regmap, CS4271_MODE1,
CS4271_MODE1_DAC_DIF_MASK | CS4271_MODE1_MASTER, val);
if (ret < 0)
return ret;
return 0;
}
static int cs4271_deemph[] = {0, 44100, 48000, 32000};
static int cs4271_set_deemph(struct snd_soc_codec *codec)
{
struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
int i, ret;
int val = CS4271_DACCTL_DEM_DIS;
if (cs4271->deemph) {
/* Find closest de-emphasis freq */
val = 1;
for (i = 2; i < ARRAY_SIZE(cs4271_deemph); i++)
if (abs(cs4271_deemph[i] - cs4271->rate) <
abs(cs4271_deemph[val] - cs4271->rate))
val = i;
val <<= 4;
}
ret = regmap_update_bits(cs4271->regmap, CS4271_DACCTL,
CS4271_DACCTL_DEM_MASK, val);
if (ret < 0)
return ret;
return 0;
}
static int cs4271_get_deemph(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
ucontrol->value.integer.value[0] = cs4271->deemph;
return 0;
}
static int cs4271_put_deemph(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
cs4271->deemph = ucontrol->value.integer.value[0];
return cs4271_set_deemph(codec);
}
struct cs4271_clk_cfg {
bool master; /* codec mode */
u8 speed_mode; /* codec speed mode: 1x, 2x, 4x */
unsigned short ratio; /* MCLK / sample rate */
u8 ratio_mask; /* ratio bit mask for Master mode */
};
static struct cs4271_clk_cfg cs4271_clk_tab[] = {
{1, CS4271_MODE1_MODE_1X, 256, CS4271_MODE1_DIV_1},
{1, CS4271_MODE1_MODE_1X, 384, CS4271_MODE1_DIV_15},
{1, CS4271_MODE1_MODE_1X, 512, CS4271_MODE1_DIV_2},
{1, CS4271_MODE1_MODE_1X, 768, CS4271_MODE1_DIV_3},
{1, CS4271_MODE1_MODE_2X, 128, CS4271_MODE1_DIV_1},
{1, CS4271_MODE1_MODE_2X, 192, CS4271_MODE1_DIV_15},
{1, CS4271_MODE1_MODE_2X, 256, CS4271_MODE1_DIV_2},
{1, CS4271_MODE1_MODE_2X, 384, CS4271_MODE1_DIV_3},
{1, CS4271_MODE1_MODE_4X, 64, CS4271_MODE1_DIV_1},
{1, CS4271_MODE1_MODE_4X, 96, CS4271_MODE1_DIV_15},
{1, CS4271_MODE1_MODE_4X, 128, CS4271_MODE1_DIV_2},
{1, CS4271_MODE1_MODE_4X, 192, CS4271_MODE1_DIV_3},
{0, CS4271_MODE1_MODE_1X, 256, CS4271_MODE1_DIV_1},
{0, CS4271_MODE1_MODE_1X, 384, CS4271_MODE1_DIV_1},
{0, CS4271_MODE1_MODE_1X, 512, CS4271_MODE1_DIV_1},
{0, CS4271_MODE1_MODE_1X, 768, CS4271_MODE1_DIV_2},
{0, CS4271_MODE1_MODE_1X, 1024, CS4271_MODE1_DIV_2},
{0, CS4271_MODE1_MODE_2X, 128, CS4271_MODE1_DIV_1},
{0, CS4271_MODE1_MODE_2X, 192, CS4271_MODE1_DIV_1},
{0, CS4271_MODE1_MODE_2X, 256, CS4271_MODE1_DIV_1},
{0, CS4271_MODE1_MODE_2X, 384, CS4271_MODE1_DIV_2},
{0, CS4271_MODE1_MODE_2X, 512, CS4271_MODE1_DIV_2},
{0, CS4271_MODE1_MODE_4X, 64, CS4271_MODE1_DIV_1},
{0, CS4271_MODE1_MODE_4X, 96, CS4271_MODE1_DIV_1},
{0, CS4271_MODE1_MODE_4X, 128, CS4271_MODE1_DIV_1},
{0, CS4271_MODE1_MODE_4X, 192, CS4271_MODE1_DIV_2},
{0, CS4271_MODE1_MODE_4X, 256, CS4271_MODE1_DIV_2},
};
#define CS4171_NR_RATIOS ARRAY_SIZE(cs4271_clk_tab)
static int cs4271_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
int i, ret;
unsigned int ratio, val;
if (cs4271->enable_soft_reset) {
/*
* Put the codec in soft reset and back again in case it's not
* currently streaming data. This way of bringing the codec in
* sync to the current clocks is not explicitly documented in
* the data sheet, but it seems to work fine, and in contrast
* to a read hardware reset, we don't have to sync back all
* registers every time.
*/
if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
!dai->capture_active) ||
(substream->stream == SNDRV_PCM_STREAM_CAPTURE &&
!dai->playback_active)) {
ret = regmap_update_bits(cs4271->regmap, CS4271_MODE2,
CS4271_MODE2_PDN,
CS4271_MODE2_PDN);
if (ret < 0)
return ret;
ret = regmap_update_bits(cs4271->regmap, CS4271_MODE2,
CS4271_MODE2_PDN, 0);
if (ret < 0)
return ret;
}
}
cs4271->rate = params_rate(params);
/* Configure DAC */
if (cs4271->rate < 50000)
val = CS4271_MODE1_MODE_1X;
else if (cs4271->rate < 100000)
val = CS4271_MODE1_MODE_2X;
else
val = CS4271_MODE1_MODE_4X;
ratio = cs4271->mclk / cs4271->rate;
for (i = 0; i < CS4171_NR_RATIOS; i++)
if ((cs4271_clk_tab[i].master == cs4271->master) &&
(cs4271_clk_tab[i].speed_mode == val) &&
(cs4271_clk_tab[i].ratio == ratio))
break;
if (i == CS4171_NR_RATIOS) {
dev_err(codec->dev, "Invalid sample rate\n");
return -EINVAL;
}
val |= cs4271_clk_tab[i].ratio_mask;
ret = regmap_update_bits(cs4271->regmap, CS4271_MODE1,
CS4271_MODE1_MODE_MASK | CS4271_MODE1_DIV_MASK, val);
if (ret < 0)
return ret;
return cs4271_set_deemph(codec);
}
static int cs4271_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
{
struct snd_soc_codec *codec = dai->codec;
struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
int ret;
int val_a = 0;
int val_b = 0;
if (stream != SNDRV_PCM_STREAM_PLAYBACK)
return 0;
if (mute) {
val_a = CS4271_VOLA_MUTE;
val_b = CS4271_VOLB_MUTE;
}
ret = regmap_update_bits(cs4271->regmap, CS4271_VOLA,
CS4271_VOLA_MUTE, val_a);
if (ret < 0)
return ret;
ret = regmap_update_bits(cs4271->regmap, CS4271_VOLB,
CS4271_VOLB_MUTE, val_b);
if (ret < 0)
return ret;
return 0;
}
/* CS4271 controls */
static DECLARE_TLV_DB_SCALE(cs4271_dac_tlv, -12700, 100, 0);
static const struct snd_kcontrol_new cs4271_snd_controls[] = {
SOC_DOUBLE_R_TLV("Master Playback Volume", CS4271_VOLA, CS4271_VOLB,
0, 0x7F, 1, cs4271_dac_tlv),
SOC_SINGLE("Digital Loopback Switch", CS4271_MODE2, 4, 1, 0),
SOC_SINGLE("Soft Ramp Switch", CS4271_DACVOL, 5, 1, 0),
SOC_SINGLE("Zero Cross Switch", CS4271_DACVOL, 4, 1, 0),
SOC_SINGLE_BOOL_EXT("De-emphasis Switch", 0,
cs4271_get_deemph, cs4271_put_deemph),
SOC_SINGLE("Auto-Mute Switch", CS4271_DACCTL, 7, 1, 0),
SOC_SINGLE("Slow Roll Off Filter Switch", CS4271_DACCTL, 6, 1, 0),
SOC_SINGLE("Soft Volume Ramp-Up Switch", CS4271_DACCTL, 3, 1, 0),
SOC_SINGLE("Soft Ramp-Down Switch", CS4271_DACCTL, 2, 1, 0),
SOC_SINGLE("Left Channel Inversion Switch", CS4271_DACCTL, 1, 1, 0),
SOC_SINGLE("Right Channel Inversion Switch", CS4271_DACCTL, 0, 1, 0),
SOC_DOUBLE("Master Capture Switch", CS4271_ADCCTL, 3, 2, 1, 1),
SOC_SINGLE("Dither 16-Bit Data Switch", CS4271_ADCCTL, 5, 1, 0),
SOC_DOUBLE("High Pass Filter Switch", CS4271_ADCCTL, 1, 0, 1, 1),
SOC_DOUBLE_R("Master Playback Switch", CS4271_VOLA, CS4271_VOLB,
7, 1, 1),
};
static const struct snd_soc_dai_ops cs4271_dai_ops = {
.hw_params = cs4271_hw_params,
.set_sysclk = cs4271_set_dai_sysclk,
.set_fmt = cs4271_set_dai_fmt,
.mute_stream = cs4271_mute_stream,
};
static struct snd_soc_dai_driver cs4271_dai = {
.name = "cs4271-hifi",
.playback = {
.stream_name = "Playback",
.channels_min = 2,
.channels_max = 2,
.rates = CS4271_PCM_RATES,
.formats = CS4271_PCM_FORMATS,
},
.capture = {
.stream_name = "Capture",
.channels_min = 2,
.channels_max = 2,
.rates = CS4271_PCM_RATES,
.formats = CS4271_PCM_FORMATS,
},
.ops = &cs4271_dai_ops,
.symmetric_rates = 1,
};
#ifdef CONFIG_PM
static int cs4271_soc_suspend(struct snd_soc_codec *codec)
{
int ret;
struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
/* Set power-down bit */
ret = regmap_update_bits(cs4271->regmap, CS4271_MODE2,
CS4271_MODE2_PDN, CS4271_MODE2_PDN);
if (ret < 0)
return ret;
return 0;
}
static int cs4271_soc_resume(struct snd_soc_codec *codec)
{
int ret;
struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
/* Restore codec state */
ret = regcache_sync(cs4271->regmap);
if (ret < 0)
return ret;
/* then disable the power-down bit */
ret = regmap_update_bits(cs4271->regmap, CS4271_MODE2,
CS4271_MODE2_PDN, 0);
if (ret < 0)
return ret;
return 0;
}
#else
#define cs4271_soc_suspend NULL
#define cs4271_soc_resume NULL
#endif /* CONFIG_PM */
#ifdef CONFIG_OF
static const struct of_device_id cs4271_dt_ids[] = {
{ .compatible = "cirrus,cs4271", },
{ }
};
MODULE_DEVICE_TABLE(of, cs4271_dt_ids);
#endif
static int cs4271_probe(struct snd_soc_codec *codec)
{
struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
struct cs4271_platform_data *cs4271plat = codec->dev->platform_data;
int ret;
bool amutec_eq_bmutec = false;
#ifdef CONFIG_OF
if (of_match_device(cs4271_dt_ids, codec->dev)) {
if (of_get_property(codec->dev->of_node,
"cirrus,amutec-eq-bmutec", NULL))
amutec_eq_bmutec = true;
if (of_get_property(codec->dev->of_node,
"cirrus,enable-soft-reset", NULL))
cs4271->enable_soft_reset = true;
}
#endif
if (cs4271plat) {
amutec_eq_bmutec = cs4271plat->amutec_eq_bmutec;
cs4271->enable_soft_reset = cs4271plat->enable_soft_reset;
}
if (gpio_is_valid(cs4271->gpio_nreset)) {
/* Reset codec */
gpio_direction_output(cs4271->gpio_nreset, 0);
mdelay(1);
gpio_set_value(cs4271->gpio_nreset, 1);
/* Give the codec time to wake up */
mdelay(1);
}
ret = regmap_update_bits(cs4271->regmap, CS4271_MODE2,
CS4271_MODE2_PDN | CS4271_MODE2_CPEN,
CS4271_MODE2_PDN | CS4271_MODE2_CPEN);
if (ret < 0)
return ret;
ret = regmap_update_bits(cs4271->regmap, CS4271_MODE2,
CS4271_MODE2_PDN, 0);
if (ret < 0)
return ret;
/* Power-up sequence requires 85 uS */
udelay(85);
if (amutec_eq_bmutec)
regmap_update_bits(cs4271->regmap, CS4271_MODE2,
CS4271_MODE2_MUTECAEQUB,
CS4271_MODE2_MUTECAEQUB);
return 0;
}
static int cs4271_remove(struct snd_soc_codec *codec)
{
struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
if (gpio_is_valid(cs4271->gpio_nreset))
/* Set codec to the reset state */
gpio_set_value(cs4271->gpio_nreset, 0);
return 0;
};
static struct snd_soc_codec_driver soc_codec_dev_cs4271 = {
.probe = cs4271_probe,
.remove = cs4271_remove,
.suspend = cs4271_soc_suspend,
.resume = cs4271_soc_resume,
.controls = cs4271_snd_controls,
.num_controls = ARRAY_SIZE(cs4271_snd_controls),
.dapm_widgets = cs4271_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(cs4271_dapm_widgets),
.dapm_routes = cs4271_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(cs4271_dapm_routes),
};
static int cs4271_common_probe(struct device *dev,
struct cs4271_private **c)
{
struct cs4271_platform_data *cs4271plat = dev->platform_data;
struct cs4271_private *cs4271;
cs4271 = devm_kzalloc(dev, sizeof(*cs4271), GFP_KERNEL);
if (!cs4271)
return -ENOMEM;
if (of_match_device(cs4271_dt_ids, dev))
cs4271->gpio_nreset =
of_get_named_gpio(dev->of_node, "reset-gpio", 0);
if (cs4271plat)
cs4271->gpio_nreset = cs4271plat->gpio_nreset;
if (gpio_is_valid(cs4271->gpio_nreset)) {
int ret;
ret = devm_gpio_request(dev, cs4271->gpio_nreset,
"CS4271 Reset");
if (ret < 0)
return ret;
}
*c = cs4271;
return 0;
}
#if defined(CONFIG_SPI_MASTER)
static const struct regmap_config cs4271_spi_regmap = {
.reg_bits = 16,
.val_bits = 8,
.max_register = CS4271_LASTREG,
.read_flag_mask = 0x21,
.write_flag_mask = 0x20,
.reg_defaults = cs4271_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(cs4271_reg_defaults),
.cache_type = REGCACHE_RBTREE,
.volatile_reg = cs4271_volatile_reg,
};
static int cs4271_spi_probe(struct spi_device *spi)
{
struct cs4271_private *cs4271;
int ret;
ret = cs4271_common_probe(&spi->dev, &cs4271);
if (ret < 0)
return ret;
spi_set_drvdata(spi, cs4271);
cs4271->regmap = devm_regmap_init_spi(spi, &cs4271_spi_regmap);
if (IS_ERR(cs4271->regmap))
return PTR_ERR(cs4271->regmap);
return snd_soc_register_codec(&spi->dev, &soc_codec_dev_cs4271,
&cs4271_dai, 1);
}
static int cs4271_spi_remove(struct spi_device *spi)
{
snd_soc_unregister_codec(&spi->dev);
return 0;
}
static struct spi_driver cs4271_spi_driver = {
.driver = {
.name = "cs4271",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(cs4271_dt_ids),
},
.probe = cs4271_spi_probe,
.remove = cs4271_spi_remove,
};
#endif /* defined(CONFIG_SPI_MASTER) */
#if IS_ENABLED(CONFIG_I2C)
static const struct i2c_device_id cs4271_i2c_id[] = {
{"cs4271", 0},
{}
};
MODULE_DEVICE_TABLE(i2c, cs4271_i2c_id);
static const struct regmap_config cs4271_i2c_regmap = {
.reg_bits = 8,
.val_bits = 8,
.max_register = CS4271_LASTREG,
.reg_defaults = cs4271_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(cs4271_reg_defaults),
.cache_type = REGCACHE_RBTREE,
.volatile_reg = cs4271_volatile_reg,
};
static int cs4271_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct cs4271_private *cs4271;
int ret;
ret = cs4271_common_probe(&client->dev, &cs4271);
if (ret < 0)
return ret;
i2c_set_clientdata(client, cs4271);
cs4271->regmap = devm_regmap_init_i2c(client, &cs4271_i2c_regmap);
if (IS_ERR(cs4271->regmap))
return PTR_ERR(cs4271->regmap);
return snd_soc_register_codec(&client->dev, &soc_codec_dev_cs4271,
&cs4271_dai, 1);
}
static int cs4271_i2c_remove(struct i2c_client *client)
{
snd_soc_unregister_codec(&client->dev);
return 0;
}
static struct i2c_driver cs4271_i2c_driver = {
.driver = {
.name = "cs4271",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(cs4271_dt_ids),
},
.id_table = cs4271_i2c_id,
.probe = cs4271_i2c_probe,
.remove = cs4271_i2c_remove,
};
#endif /* IS_ENABLED(CONFIG_I2C) */
/*
* We only register our serial bus driver here without
* assignment to particular chip. So if any of the below
* fails, there is some problem with I2C or SPI subsystem.
* In most cases this module will be compiled with support
* of only one serial bus.
*/
static int __init cs4271_modinit(void)
{
int ret;
#if IS_ENABLED(CONFIG_I2C)
ret = i2c_add_driver(&cs4271_i2c_driver);
if (ret) {
pr_err("Failed to register CS4271 I2C driver: %d\n", ret);
return ret;
}
#endif
#if defined(CONFIG_SPI_MASTER)
ret = spi_register_driver(&cs4271_spi_driver);
if (ret) {
pr_err("Failed to register CS4271 SPI driver: %d\n", ret);
return ret;
}
#endif
return 0;
}
module_init(cs4271_modinit);
static void __exit cs4271_modexit(void)
{
#if defined(CONFIG_SPI_MASTER)
spi_unregister_driver(&cs4271_spi_driver);
#endif
#if IS_ENABLED(CONFIG_I2C)
i2c_del_driver(&cs4271_i2c_driver);
#endif
}
module_exit(cs4271_modexit);
MODULE_AUTHOR("Alexander Sverdlin <subaparts@yandex.ru>");
MODULE_DESCRIPTION("Cirrus Logic CS4271 ALSA SoC Codec Driver");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,60 @@
/*
* cs42l56.c -- CS42L51 ALSA SoC I2C audio driver
*
* Copyright 2014 CirrusLogic, Inc.
*
* Author: Brian Austin <brian.austin@cirrus.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#include <linux/i2c.h>
#include <linux/module.h>
#include <sound/soc.h>
#include "cs42l51.h"
static struct i2c_device_id cs42l51_i2c_id[] = {
{"cs42l51", 0},
{}
};
MODULE_DEVICE_TABLE(i2c, cs42l51_i2c_id);
static int cs42l51_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct regmap_config config;
config = cs42l51_regmap;
config.val_bits = 8;
config.reg_bits = 8;
return cs42l51_probe(&i2c->dev, devm_regmap_init_i2c(i2c, &config));
}
static int cs42l51_i2c_remove(struct i2c_client *i2c)
{
snd_soc_unregister_codec(&i2c->dev);
return 0;
}
static struct i2c_driver cs42l51_i2c_driver = {
.driver = {
.name = "cs42l51",
.owner = THIS_MODULE,
.of_match_table = cs42l51_of_match,
},
.probe = cs42l51_i2c_probe,
.remove = cs42l51_i2c_remove,
.id_table = cs42l51_i2c_id,
};
module_i2c_driver(cs42l51_i2c_driver);
MODULE_DESCRIPTION("ASoC CS42L51 I2C Driver");
MODULE_AUTHOR("Brian Austin, Cirrus Logic Inc, <brian.austin@cirrus.com>");
MODULE_LICENSE("GPL");

570
sound/soc/codecs/cs42l51.c Normal file
View file

@ -0,0 +1,570 @@
/*
* cs42l51.c
*
* ASoC Driver for Cirrus Logic CS42L51 codecs
*
* Copyright (c) 2010 Arnaud Patard <apatard@mandriva.com>
*
* Based on cs4270.c - Copyright (c) Freescale Semiconductor
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* For now:
* - Only I2C is support. Not SPI
* - master mode *NOT* supported
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/soc.h>
#include <sound/tlv.h>
#include <sound/initval.h>
#include <sound/pcm_params.h>
#include <sound/pcm.h>
#include <linux/regmap.h>
#include "cs42l51.h"
enum master_slave_mode {
MODE_SLAVE,
MODE_SLAVE_AUTO,
MODE_MASTER,
};
struct cs42l51_private {
unsigned int mclk;
unsigned int audio_mode; /* The mode (I2S or left-justified) */
enum master_slave_mode func;
};
#define CS42L51_FORMATS ( \
SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | \
SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S18_3BE | \
SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE | \
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE)
static int cs42l51_get_chan_mix(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
unsigned long value = snd_soc_read(codec, CS42L51_PCM_MIXER)&3;
switch (value) {
default:
case 0:
ucontrol->value.integer.value[0] = 0;
break;
/* same value : (L+R)/2 and (R+L)/2 */
case 1:
case 2:
ucontrol->value.integer.value[0] = 1;
break;
case 3:
ucontrol->value.integer.value[0] = 2;
break;
}
return 0;
}
#define CHAN_MIX_NORMAL 0x00
#define CHAN_MIX_BOTH 0x55
#define CHAN_MIX_SWAP 0xFF
static int cs42l51_set_chan_mix(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
unsigned char val;
switch (ucontrol->value.integer.value[0]) {
default:
case 0:
val = CHAN_MIX_NORMAL;
break;
case 1:
val = CHAN_MIX_BOTH;
break;
case 2:
val = CHAN_MIX_SWAP;
break;
}
snd_soc_write(codec, CS42L51_PCM_MIXER, val);
return 1;
}
static const DECLARE_TLV_DB_SCALE(adc_pcm_tlv, -5150, 50, 0);
static const DECLARE_TLV_DB_SCALE(tone_tlv, -1050, 150, 0);
static const DECLARE_TLV_DB_SCALE(aout_tlv, -10200, 50, 0);
static const DECLARE_TLV_DB_SCALE(boost_tlv, 1600, 1600, 0);
static const char *chan_mix[] = {
"L R",
"L+R",
"R L",
};
static SOC_ENUM_SINGLE_EXT_DECL(cs42l51_chan_mix, chan_mix);
static const struct snd_kcontrol_new cs42l51_snd_controls[] = {
SOC_DOUBLE_R_SX_TLV("PCM Playback Volume",
CS42L51_PCMA_VOL, CS42L51_PCMB_VOL,
0, 0x19, 0x7F, adc_pcm_tlv),
SOC_DOUBLE_R("PCM Playback Switch",
CS42L51_PCMA_VOL, CS42L51_PCMB_VOL, 7, 1, 1),
SOC_DOUBLE_R_SX_TLV("Analog Playback Volume",
CS42L51_AOUTA_VOL, CS42L51_AOUTB_VOL,
0, 0x34, 0xE4, aout_tlv),
SOC_DOUBLE_R_SX_TLV("ADC Mixer Volume",
CS42L51_ADCA_VOL, CS42L51_ADCB_VOL,
0, 0x19, 0x7F, adc_pcm_tlv),
SOC_DOUBLE_R("ADC Mixer Switch",
CS42L51_ADCA_VOL, CS42L51_ADCB_VOL, 7, 1, 1),
SOC_SINGLE("Playback Deemphasis Switch", CS42L51_DAC_CTL, 3, 1, 0),
SOC_SINGLE("Auto-Mute Switch", CS42L51_DAC_CTL, 2, 1, 0),
SOC_SINGLE("Soft Ramp Switch", CS42L51_DAC_CTL, 1, 1, 0),
SOC_SINGLE("Zero Cross Switch", CS42L51_DAC_CTL, 0, 0, 0),
SOC_DOUBLE_TLV("Mic Boost Volume",
CS42L51_MIC_CTL, 0, 1, 1, 0, boost_tlv),
SOC_SINGLE_TLV("Bass Volume", CS42L51_TONE_CTL, 0, 0xf, 1, tone_tlv),
SOC_SINGLE_TLV("Treble Volume", CS42L51_TONE_CTL, 4, 0xf, 1, tone_tlv),
SOC_ENUM_EXT("PCM channel mixer",
cs42l51_chan_mix,
cs42l51_get_chan_mix, cs42l51_set_chan_mix),
};
/*
* to power down, one must:
* 1.) Enable the PDN bit
* 2.) enable power-down for the select channels
* 3.) disable the PDN bit.
*/
static int cs42l51_pdn_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
switch (event) {
case SND_SOC_DAPM_PRE_PMD:
snd_soc_update_bits(w->codec, CS42L51_POWER_CTL1,
CS42L51_POWER_CTL1_PDN,
CS42L51_POWER_CTL1_PDN);
break;
default:
case SND_SOC_DAPM_POST_PMD:
snd_soc_update_bits(w->codec, CS42L51_POWER_CTL1,
CS42L51_POWER_CTL1_PDN, 0);
break;
}
return 0;
}
static const char *cs42l51_dac_names[] = {"Direct PCM",
"DSP PCM", "ADC"};
static SOC_ENUM_SINGLE_DECL(cs42l51_dac_mux_enum,
CS42L51_DAC_CTL, 6, cs42l51_dac_names);
static const struct snd_kcontrol_new cs42l51_dac_mux_controls =
SOC_DAPM_ENUM("Route", cs42l51_dac_mux_enum);
static const char *cs42l51_adcl_names[] = {"AIN1 Left", "AIN2 Left",
"MIC Left", "MIC+preamp Left"};
static SOC_ENUM_SINGLE_DECL(cs42l51_adcl_mux_enum,
CS42L51_ADC_INPUT, 4, cs42l51_adcl_names);
static const struct snd_kcontrol_new cs42l51_adcl_mux_controls =
SOC_DAPM_ENUM("Route", cs42l51_adcl_mux_enum);
static const char *cs42l51_adcr_names[] = {"AIN1 Right", "AIN2 Right",
"MIC Right", "MIC+preamp Right"};
static SOC_ENUM_SINGLE_DECL(cs42l51_adcr_mux_enum,
CS42L51_ADC_INPUT, 6, cs42l51_adcr_names);
static const struct snd_kcontrol_new cs42l51_adcr_mux_controls =
SOC_DAPM_ENUM("Route", cs42l51_adcr_mux_enum);
static const struct snd_soc_dapm_widget cs42l51_dapm_widgets[] = {
SND_SOC_DAPM_MICBIAS("Mic Bias", CS42L51_MIC_POWER_CTL, 1, 1),
SND_SOC_DAPM_PGA_E("Left PGA", CS42L51_POWER_CTL1, 3, 1, NULL, 0,
cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD),
SND_SOC_DAPM_PGA_E("Right PGA", CS42L51_POWER_CTL1, 4, 1, NULL, 0,
cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD),
SND_SOC_DAPM_ADC_E("Left ADC", "Left HiFi Capture",
CS42L51_POWER_CTL1, 1, 1,
cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD),
SND_SOC_DAPM_ADC_E("Right ADC", "Right HiFi Capture",
CS42L51_POWER_CTL1, 2, 1,
cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD),
SND_SOC_DAPM_DAC_E("Left DAC", "Left HiFi Playback",
CS42L51_POWER_CTL1, 5, 1,
cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD),
SND_SOC_DAPM_DAC_E("Right DAC", "Right HiFi Playback",
CS42L51_POWER_CTL1, 6, 1,
cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD),
/* analog/mic */
SND_SOC_DAPM_INPUT("AIN1L"),
SND_SOC_DAPM_INPUT("AIN1R"),
SND_SOC_DAPM_INPUT("AIN2L"),
SND_SOC_DAPM_INPUT("AIN2R"),
SND_SOC_DAPM_INPUT("MICL"),
SND_SOC_DAPM_INPUT("MICR"),
SND_SOC_DAPM_MIXER("Mic Preamp Left",
CS42L51_MIC_POWER_CTL, 2, 1, NULL, 0),
SND_SOC_DAPM_MIXER("Mic Preamp Right",
CS42L51_MIC_POWER_CTL, 3, 1, NULL, 0),
/* HP */
SND_SOC_DAPM_OUTPUT("HPL"),
SND_SOC_DAPM_OUTPUT("HPR"),
/* mux */
SND_SOC_DAPM_MUX("DAC Mux", SND_SOC_NOPM, 0, 0,
&cs42l51_dac_mux_controls),
SND_SOC_DAPM_MUX("PGA-ADC Mux Left", SND_SOC_NOPM, 0, 0,
&cs42l51_adcl_mux_controls),
SND_SOC_DAPM_MUX("PGA-ADC Mux Right", SND_SOC_NOPM, 0, 0,
&cs42l51_adcr_mux_controls),
};
static const struct snd_soc_dapm_route cs42l51_routes[] = {
{"HPL", NULL, "Left DAC"},
{"HPR", NULL, "Right DAC"},
{"Left ADC", NULL, "Left PGA"},
{"Right ADC", NULL, "Right PGA"},
{"Mic Preamp Left", NULL, "MICL"},
{"Mic Preamp Right", NULL, "MICR"},
{"PGA-ADC Mux Left", "AIN1 Left", "AIN1L" },
{"PGA-ADC Mux Left", "AIN2 Left", "AIN2L" },
{"PGA-ADC Mux Left", "MIC Left", "MICL" },
{"PGA-ADC Mux Left", "MIC+preamp Left", "Mic Preamp Left" },
{"PGA-ADC Mux Right", "AIN1 Right", "AIN1R" },
{"PGA-ADC Mux Right", "AIN2 Right", "AIN2R" },
{"PGA-ADC Mux Right", "MIC Right", "MICR" },
{"PGA-ADC Mux Right", "MIC+preamp Right", "Mic Preamp Right" },
{"Left PGA", NULL, "PGA-ADC Mux Left"},
{"Right PGA", NULL, "PGA-ADC Mux Right"},
};
static int cs42l51_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int format)
{
struct snd_soc_codec *codec = codec_dai->codec;
struct cs42l51_private *cs42l51 = snd_soc_codec_get_drvdata(codec);
switch (format & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
case SND_SOC_DAIFMT_LEFT_J:
case SND_SOC_DAIFMT_RIGHT_J:
cs42l51->audio_mode = format & SND_SOC_DAIFMT_FORMAT_MASK;
break;
default:
dev_err(codec->dev, "invalid DAI format\n");
return -EINVAL;
}
switch (format & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBM_CFM:
cs42l51->func = MODE_MASTER;
break;
case SND_SOC_DAIFMT_CBS_CFS:
cs42l51->func = MODE_SLAVE_AUTO;
break;
default:
dev_err(codec->dev, "Unknown master/slave configuration\n");
return -EINVAL;
}
return 0;
}
struct cs42l51_ratios {
unsigned int ratio;
unsigned char speed_mode;
unsigned char mclk;
};
static struct cs42l51_ratios slave_ratios[] = {
{ 512, CS42L51_QSM_MODE, 0 }, { 768, CS42L51_QSM_MODE, 0 },
{ 1024, CS42L51_QSM_MODE, 0 }, { 1536, CS42L51_QSM_MODE, 0 },
{ 2048, CS42L51_QSM_MODE, 0 }, { 3072, CS42L51_QSM_MODE, 0 },
{ 256, CS42L51_HSM_MODE, 0 }, { 384, CS42L51_HSM_MODE, 0 },
{ 512, CS42L51_HSM_MODE, 0 }, { 768, CS42L51_HSM_MODE, 0 },
{ 1024, CS42L51_HSM_MODE, 0 }, { 1536, CS42L51_HSM_MODE, 0 },
{ 128, CS42L51_SSM_MODE, 0 }, { 192, CS42L51_SSM_MODE, 0 },
{ 256, CS42L51_SSM_MODE, 0 }, { 384, CS42L51_SSM_MODE, 0 },
{ 512, CS42L51_SSM_MODE, 0 }, { 768, CS42L51_SSM_MODE, 0 },
{ 128, CS42L51_DSM_MODE, 0 }, { 192, CS42L51_DSM_MODE, 0 },
{ 256, CS42L51_DSM_MODE, 0 }, { 384, CS42L51_DSM_MODE, 0 },
};
static struct cs42l51_ratios slave_auto_ratios[] = {
{ 1024, CS42L51_QSM_MODE, 0 }, { 1536, CS42L51_QSM_MODE, 0 },
{ 2048, CS42L51_QSM_MODE, 1 }, { 3072, CS42L51_QSM_MODE, 1 },
{ 512, CS42L51_HSM_MODE, 0 }, { 768, CS42L51_HSM_MODE, 0 },
{ 1024, CS42L51_HSM_MODE, 1 }, { 1536, CS42L51_HSM_MODE, 1 },
{ 256, CS42L51_SSM_MODE, 0 }, { 384, CS42L51_SSM_MODE, 0 },
{ 512, CS42L51_SSM_MODE, 1 }, { 768, CS42L51_SSM_MODE, 1 },
{ 128, CS42L51_DSM_MODE, 0 }, { 192, CS42L51_DSM_MODE, 0 },
{ 256, CS42L51_DSM_MODE, 1 }, { 384, CS42L51_DSM_MODE, 1 },
};
static int cs42l51_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = codec_dai->codec;
struct cs42l51_private *cs42l51 = snd_soc_codec_get_drvdata(codec);
cs42l51->mclk = freq;
return 0;
}
static int cs42l51_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
struct cs42l51_private *cs42l51 = snd_soc_codec_get_drvdata(codec);
int ret;
unsigned int i;
unsigned int rate;
unsigned int ratio;
struct cs42l51_ratios *ratios = NULL;
int nr_ratios = 0;
int intf_ctl, power_ctl, fmt;
switch (cs42l51->func) {
case MODE_MASTER:
return -EINVAL;
case MODE_SLAVE:
ratios = slave_ratios;
nr_ratios = ARRAY_SIZE(slave_ratios);
break;
case MODE_SLAVE_AUTO:
ratios = slave_auto_ratios;
nr_ratios = ARRAY_SIZE(slave_auto_ratios);
break;
}
/* Figure out which MCLK/LRCK ratio to use */
rate = params_rate(params); /* Sampling rate, in Hz */
ratio = cs42l51->mclk / rate; /* MCLK/LRCK ratio */
for (i = 0; i < nr_ratios; i++) {
if (ratios[i].ratio == ratio)
break;
}
if (i == nr_ratios) {
/* We did not find a matching ratio */
dev_err(codec->dev, "could not find matching ratio\n");
return -EINVAL;
}
intf_ctl = snd_soc_read(codec, CS42L51_INTF_CTL);
power_ctl = snd_soc_read(codec, CS42L51_MIC_POWER_CTL);
intf_ctl &= ~(CS42L51_INTF_CTL_MASTER | CS42L51_INTF_CTL_ADC_I2S
| CS42L51_INTF_CTL_DAC_FORMAT(7));
power_ctl &= ~(CS42L51_MIC_POWER_CTL_SPEED(3)
| CS42L51_MIC_POWER_CTL_MCLK_DIV2);
switch (cs42l51->func) {
case MODE_MASTER:
intf_ctl |= CS42L51_INTF_CTL_MASTER;
power_ctl |= CS42L51_MIC_POWER_CTL_SPEED(ratios[i].speed_mode);
break;
case MODE_SLAVE:
power_ctl |= CS42L51_MIC_POWER_CTL_SPEED(ratios[i].speed_mode);
break;
case MODE_SLAVE_AUTO:
power_ctl |= CS42L51_MIC_POWER_CTL_AUTO;
break;
}
switch (cs42l51->audio_mode) {
case SND_SOC_DAIFMT_I2S:
intf_ctl |= CS42L51_INTF_CTL_ADC_I2S;
intf_ctl |= CS42L51_INTF_CTL_DAC_FORMAT(CS42L51_DAC_DIF_I2S);
break;
case SND_SOC_DAIFMT_LEFT_J:
intf_ctl |= CS42L51_INTF_CTL_DAC_FORMAT(CS42L51_DAC_DIF_LJ24);
break;
case SND_SOC_DAIFMT_RIGHT_J:
switch (params_width(params)) {
case 16:
fmt = CS42L51_DAC_DIF_RJ16;
break;
case 18:
fmt = CS42L51_DAC_DIF_RJ18;
break;
case 20:
fmt = CS42L51_DAC_DIF_RJ20;
break;
case 24:
fmt = CS42L51_DAC_DIF_RJ24;
break;
default:
dev_err(codec->dev, "unknown format\n");
return -EINVAL;
}
intf_ctl |= CS42L51_INTF_CTL_DAC_FORMAT(fmt);
break;
default:
dev_err(codec->dev, "unknown format\n");
return -EINVAL;
}
if (ratios[i].mclk)
power_ctl |= CS42L51_MIC_POWER_CTL_MCLK_DIV2;
ret = snd_soc_write(codec, CS42L51_INTF_CTL, intf_ctl);
if (ret < 0)
return ret;
ret = snd_soc_write(codec, CS42L51_MIC_POWER_CTL, power_ctl);
if (ret < 0)
return ret;
return 0;
}
static int cs42l51_dai_mute(struct snd_soc_dai *dai, int mute)
{
struct snd_soc_codec *codec = dai->codec;
int reg;
int mask = CS42L51_DAC_OUT_CTL_DACA_MUTE|CS42L51_DAC_OUT_CTL_DACB_MUTE;
reg = snd_soc_read(codec, CS42L51_DAC_OUT_CTL);
if (mute)
reg |= mask;
else
reg &= ~mask;
return snd_soc_write(codec, CS42L51_DAC_OUT_CTL, reg);
}
static const struct snd_soc_dai_ops cs42l51_dai_ops = {
.hw_params = cs42l51_hw_params,
.set_sysclk = cs42l51_set_dai_sysclk,
.set_fmt = cs42l51_set_dai_fmt,
.digital_mute = cs42l51_dai_mute,
};
static struct snd_soc_dai_driver cs42l51_dai = {
.name = "cs42l51-hifi",
.playback = {
.stream_name = "Playback",
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_96000,
.formats = CS42L51_FORMATS,
},
.capture = {
.stream_name = "Capture",
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_96000,
.formats = CS42L51_FORMATS,
},
.ops = &cs42l51_dai_ops,
};
static int cs42l51_codec_probe(struct snd_soc_codec *codec)
{
int ret, reg;
/*
* DAC configuration
* - Use signal processor
* - auto mute
* - vol changes immediate
* - no de-emphasize
*/
reg = CS42L51_DAC_CTL_DATA_SEL(1)
| CS42L51_DAC_CTL_AMUTE | CS42L51_DAC_CTL_DACSZ(0);
ret = snd_soc_write(codec, CS42L51_DAC_CTL, reg);
if (ret < 0)
return ret;
return 0;
}
static struct snd_soc_codec_driver soc_codec_device_cs42l51 = {
.probe = cs42l51_codec_probe,
.controls = cs42l51_snd_controls,
.num_controls = ARRAY_SIZE(cs42l51_snd_controls),
.dapm_widgets = cs42l51_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(cs42l51_dapm_widgets),
.dapm_routes = cs42l51_routes,
.num_dapm_routes = ARRAY_SIZE(cs42l51_routes),
};
const struct regmap_config cs42l51_regmap = {
.max_register = CS42L51_CHARGE_FREQ,
.cache_type = REGCACHE_RBTREE,
};
EXPORT_SYMBOL_GPL(cs42l51_regmap);
int cs42l51_probe(struct device *dev, struct regmap *regmap)
{
struct cs42l51_private *cs42l51;
unsigned int val;
int ret;
if (IS_ERR(regmap))
return PTR_ERR(regmap);
cs42l51 = devm_kzalloc(dev, sizeof(struct cs42l51_private),
GFP_KERNEL);
if (!cs42l51)
return -ENOMEM;
dev_set_drvdata(dev, cs42l51);
/* Verify that we have a CS42L51 */
ret = regmap_read(regmap, CS42L51_CHIP_REV_ID, &val);
if (ret < 0) {
dev_err(dev, "failed to read I2C\n");
goto error;
}
if ((val != CS42L51_MK_CHIP_REV(CS42L51_CHIP_ID, CS42L51_CHIP_REV_A)) &&
(val != CS42L51_MK_CHIP_REV(CS42L51_CHIP_ID, CS42L51_CHIP_REV_B))) {
dev_err(dev, "Invalid chip id: %x\n", val);
ret = -ENODEV;
goto error;
}
dev_info(dev, "Cirrus Logic CS42L51, Revision: %02X\n",
val & CS42L51_CHIP_REV_MASK);
ret = snd_soc_register_codec(dev,
&soc_codec_device_cs42l51, &cs42l51_dai, 1);
error:
return ret;
}
EXPORT_SYMBOL_GPL(cs42l51_probe);
const struct of_device_id cs42l51_of_match[] = {
{ .compatible = "cirrus,cs42l51", },
{ }
};
MODULE_DEVICE_TABLE(of, cs42l51_of_match);
EXPORT_SYMBOL_GPL(cs42l51_of_match);
MODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>");
MODULE_DESCRIPTION("Cirrus Logic CS42L51 ALSA SoC Codec Driver");
MODULE_LICENSE("GPL");

168
sound/soc/codecs/cs42l51.h Normal file
View file

@ -0,0 +1,168 @@
/*
* cs42l51.h
*
* ASoC Driver for Cirrus Logic CS42L51 codecs
*
* Copyright (c) 2010 Arnaud Patard <apatard@mandriva.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef _CS42L51_H
#define _CS42L51_H
struct device;
extern const struct regmap_config cs42l51_regmap;
int cs42l51_probe(struct device *dev, struct regmap *regmap);
extern const struct of_device_id cs42l51_of_match[];
#define CS42L51_CHIP_ID 0x1B
#define CS42L51_CHIP_REV_A 0x00
#define CS42L51_CHIP_REV_B 0x01
#define CS42L51_CHIP_REV_MASK 0x07
#define CS42L51_CHIP_REV_ID 0x01
#define CS42L51_MK_CHIP_REV(a, b) ((a)<<3|(b))
#define CS42L51_POWER_CTL1 0x02
#define CS42L51_POWER_CTL1_PDN_DACB (1<<6)
#define CS42L51_POWER_CTL1_PDN_DACA (1<<5)
#define CS42L51_POWER_CTL1_PDN_PGAB (1<<4)
#define CS42L51_POWER_CTL1_PDN_PGAA (1<<3)
#define CS42L51_POWER_CTL1_PDN_ADCB (1<<2)
#define CS42L51_POWER_CTL1_PDN_ADCA (1<<1)
#define CS42L51_POWER_CTL1_PDN (1<<0)
#define CS42L51_MIC_POWER_CTL 0x03
#define CS42L51_MIC_POWER_CTL_AUTO (1<<7)
#define CS42L51_MIC_POWER_CTL_SPEED(x) (((x)&3)<<5)
#define CS42L51_QSM_MODE 3
#define CS42L51_HSM_MODE 2
#define CS42L51_SSM_MODE 1
#define CS42L51_DSM_MODE 0
#define CS42L51_MIC_POWER_CTL_3ST_SP (1<<4)
#define CS42L51_MIC_POWER_CTL_PDN_MICB (1<<3)
#define CS42L51_MIC_POWER_CTL_PDN_MICA (1<<2)
#define CS42L51_MIC_POWER_CTL_PDN_BIAS (1<<1)
#define CS42L51_MIC_POWER_CTL_MCLK_DIV2 (1<<0)
#define CS42L51_INTF_CTL 0x04
#define CS42L51_INTF_CTL_LOOPBACK (1<<7)
#define CS42L51_INTF_CTL_MASTER (1<<6)
#define CS42L51_INTF_CTL_DAC_FORMAT(x) (((x)&7)<<3)
#define CS42L51_DAC_DIF_LJ24 0x00
#define CS42L51_DAC_DIF_I2S 0x01
#define CS42L51_DAC_DIF_RJ24 0x02
#define CS42L51_DAC_DIF_RJ20 0x03
#define CS42L51_DAC_DIF_RJ18 0x04
#define CS42L51_DAC_DIF_RJ16 0x05
#define CS42L51_INTF_CTL_ADC_I2S (1<<2)
#define CS42L51_INTF_CTL_DIGMIX (1<<1)
#define CS42L51_INTF_CTL_MICMIX (1<<0)
#define CS42L51_MIC_CTL 0x05
#define CS42L51_MIC_CTL_ADC_SNGVOL (1<<7)
#define CS42L51_MIC_CTL_ADCD_DBOOST (1<<6)
#define CS42L51_MIC_CTL_ADCA_DBOOST (1<<5)
#define CS42L51_MIC_CTL_MICBIAS_SEL (1<<4)
#define CS42L51_MIC_CTL_MICBIAS_LVL(x) (((x)&3)<<2)
#define CS42L51_MIC_CTL_MICB_BOOST (1<<1)
#define CS42L51_MIC_CTL_MICA_BOOST (1<<0)
#define CS42L51_ADC_CTL 0x06
#define CS42L51_ADC_CTL_ADCB_HPFEN (1<<7)
#define CS42L51_ADC_CTL_ADCB_HPFRZ (1<<6)
#define CS42L51_ADC_CTL_ADCA_HPFEN (1<<5)
#define CS42L51_ADC_CTL_ADCA_HPFRZ (1<<4)
#define CS42L51_ADC_CTL_SOFTB (1<<3)
#define CS42L51_ADC_CTL_ZCROSSB (1<<2)
#define CS42L51_ADC_CTL_SOFTA (1<<1)
#define CS42L51_ADC_CTL_ZCROSSA (1<<0)
#define CS42L51_ADC_INPUT 0x07
#define CS42L51_ADC_INPUT_AINB_MUX(x) (((x)&3)<<6)
#define CS42L51_ADC_INPUT_AINA_MUX(x) (((x)&3)<<4)
#define CS42L51_ADC_INPUT_INV_ADCB (1<<3)
#define CS42L51_ADC_INPUT_INV_ADCA (1<<2)
#define CS42L51_ADC_INPUT_ADCB_MUTE (1<<1)
#define CS42L51_ADC_INPUT_ADCA_MUTE (1<<0)
#define CS42L51_DAC_OUT_CTL 0x08
#define CS42L51_DAC_OUT_CTL_HP_GAIN(x) (((x)&7)<<5)
#define CS42L51_DAC_OUT_CTL_DAC_SNGVOL (1<<4)
#define CS42L51_DAC_OUT_CTL_INV_PCMB (1<<3)
#define CS42L51_DAC_OUT_CTL_INV_PCMA (1<<2)
#define CS42L51_DAC_OUT_CTL_DACB_MUTE (1<<1)
#define CS42L51_DAC_OUT_CTL_DACA_MUTE (1<<0)
#define CS42L51_DAC_CTL 0x09
#define CS42L51_DAC_CTL_DATA_SEL(x) (((x)&3)<<6)
#define CS42L51_DAC_CTL_FREEZE (1<<5)
#define CS42L51_DAC_CTL_DEEMPH (1<<3)
#define CS42L51_DAC_CTL_AMUTE (1<<2)
#define CS42L51_DAC_CTL_DACSZ(x) (((x)&3)<<0)
#define CS42L51_ALC_PGA_CTL 0x0A
#define CS42L51_ALC_PGB_CTL 0x0B
#define CS42L51_ALC_PGX_ALCX_SRDIS (1<<7)
#define CS42L51_ALC_PGX_ALCX_ZCDIS (1<<6)
#define CS42L51_ALC_PGX_PGX_VOL(x) (((x)&0x1f)<<0)
#define CS42L51_ADCA_ATT 0x0C
#define CS42L51_ADCB_ATT 0x0D
#define CS42L51_ADCA_VOL 0x0E
#define CS42L51_ADCB_VOL 0x0F
#define CS42L51_PCMA_VOL 0x10
#define CS42L51_PCMB_VOL 0x11
#define CS42L51_MIX_MUTE_ADCMIX (1<<7)
#define CS42L51_MIX_VOLUME(x) (((x)&0x7f)<<0)
#define CS42L51_BEEP_FREQ 0x12
#define CS42L51_BEEP_VOL 0x13
#define CS42L51_BEEP_CONF 0x14
#define CS42L51_TONE_CTL 0x15
#define CS42L51_TONE_CTL_TREB(x) (((x)&0xf)<<4)
#define CS42L51_TONE_CTL_BASS(x) (((x)&0xf)<<0)
#define CS42L51_AOUTA_VOL 0x16
#define CS42L51_AOUTB_VOL 0x17
#define CS42L51_PCM_MIXER 0x18
#define CS42L51_LIMIT_THRES_DIS 0x19
#define CS42L51_LIMIT_REL 0x1A
#define CS42L51_LIMIT_ATT 0x1B
#define CS42L51_ALC_EN 0x1C
#define CS42L51_ALC_REL 0x1D
#define CS42L51_ALC_THRES 0x1E
#define CS42L51_NOISE_CONF 0x1F
#define CS42L51_STATUS 0x20
#define CS42L51_STATUS_SP_CLKERR (1<<6)
#define CS42L51_STATUS_SPEA_OVFL (1<<5)
#define CS42L51_STATUS_SPEB_OVFL (1<<4)
#define CS42L51_STATUS_PCMA_OVFL (1<<3)
#define CS42L51_STATUS_PCMB_OVFL (1<<2)
#define CS42L51_STATUS_ADCA_OVFL (1<<1)
#define CS42L51_STATUS_ADCB_OVFL (1<<0)
#define CS42L51_CHARGE_FREQ 0x21
#define CS42L51_FIRSTREG 0x01
/*
* Hack: with register 0x21, it makes 33 registers. Looks like someone in the
* i2c layer doesn't like i2c smbus block read of 33 regs. Workaround by using
* 32 regs
*/
#define CS42L51_LASTREG 0x20
#define CS42L51_NUMREGS (CS42L51_LASTREG - CS42L51_FIRSTREG + 1)
#endif

1302
sound/soc/codecs/cs42l52.c Normal file

File diff suppressed because it is too large Load diff

274
sound/soc/codecs/cs42l52.h Normal file
View file

@ -0,0 +1,274 @@
/*
* cs42l52.h -- CS42L52 ALSA SoC audio driver
*
* Copyright 2012 CirrusLogic, Inc.
*
* Author: Georgi Vlaev <joe@nucleusys.com>
* Author: Brian Austin <brian.austin@cirrus.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#ifndef __CS42L52_H__
#define __CS42L52_H__
#define CS42L52_NAME "CS42L52"
#define CS42L52_DEFAULT_CLK 12000000
#define CS42L52_MIN_CLK 11000000
#define CS42L52_MAX_CLK 27000000
#define CS42L52_DEFAULT_FORMAT SNDRV_PCM_FMTBIT_S16_LE
#define CS42L52_DEFAULT_MAX_CHANS 2
#define CS42L52_SYSCLK 1
#define CS42L52_CHIP_SWICTH (1 << 17)
#define CS42L52_ALL_IN_ONE (1 << 16)
#define CS42L52_CHIP_ONE 0x00
#define CS42L52_CHIP_TWO 0x01
#define CS42L52_CHIP_THR 0x02
#define CS42L52_CHIP_MASK 0x0f
#define CS42L52_FIX_BITS_CTL 0x00
#define CS42L52_CHIP 0x01
#define CS42L52_CHIP_ID 0xE0
#define CS42L52_CHIP_ID_MASK 0xF8
#define CS42L52_CHIP_REV_A0 0x00
#define CS42L52_CHIP_REV_A1 0x01
#define CS42L52_CHIP_REV_B0 0x02
#define CS42L52_CHIP_REV_MASK 0x07
#define CS42L52_PWRCTL1 0x02
#define CS42L52_PWRCTL1_PDN_ALL 0x9F
#define CS42L52_PWRCTL1_PDN_CHRG 0x80
#define CS42L52_PWRCTL1_PDN_PGAB 0x10
#define CS42L52_PWRCTL1_PDN_PGAA 0x08
#define CS42L52_PWRCTL1_PDN_ADCB 0x04
#define CS42L52_PWRCTL1_PDN_ADCA 0x02
#define CS42L52_PWRCTL1_PDN_CODEC 0x01
#define CS42L52_PWRCTL2 0x03
#define CS42L52_PWRCTL2_OVRDB (1 << 4)
#define CS42L52_PWRCTL2_OVRDA (1 << 3)
#define CS42L52_PWRCTL2_PDN_MICB (1 << 2)
#define CS42L52_PWRCTL2_PDN_MICB_SHIFT 2
#define CS42L52_PWRCTL2_PDN_MICA (1 << 1)
#define CS42L52_PWRCTL2_PDN_MICA_SHIFT 1
#define CS42L52_PWRCTL2_PDN_MICBIAS (1 << 0)
#define CS42L52_PWRCTL2_PDN_MICBIAS_SHIFT 0
#define CS42L52_PWRCTL3 0x04
#define CS42L52_PWRCTL3_HPB_PDN_SHIFT 6
#define CS42L52_PWRCTL3_HPB_ON_LOW 0x00
#define CS42L52_PWRCTL3_HPB_ON_HIGH 0x01
#define CS42L52_PWRCTL3_HPB_ALWAYS_ON 0x02
#define CS42L52_PWRCTL3_HPB_ALWAYS_OFF 0x03
#define CS42L52_PWRCTL3_HPA_PDN_SHIFT 4
#define CS42L52_PWRCTL3_HPA_ON_LOW 0x00
#define CS42L52_PWRCTL3_HPA_ON_HIGH 0x01
#define CS42L52_PWRCTL3_HPA_ALWAYS_ON 0x02
#define CS42L52_PWRCTL3_HPA_ALWAYS_OFF 0x03
#define CS42L52_PWRCTL3_SPKB_PDN_SHIFT 2
#define CS42L52_PWRCTL3_SPKB_ON_LOW 0x00
#define CS42L52_PWRCTL3_SPKB_ON_HIGH 0x01
#define CS42L52_PWRCTL3_SPKB_ALWAYS_ON 0x02
#define CS42L52_PWRCTL3_PDN_SPKB (1 << 2)
#define CS42L52_PWRCTL3_PDN_SPKA (1 << 0)
#define CS42L52_PWRCTL3_SPKA_PDN_SHIFT 0
#define CS42L52_PWRCTL3_SPKA_ON_LOW 0x00
#define CS42L52_PWRCTL3_SPKA_ON_HIGH 0x01
#define CS42L52_PWRCTL3_SPKA_ALWAYS_ON 0x02
#define CS42L52_DEFAULT_OUTPUT_STATE 0x05
#define CS42L52_PWRCTL3_CONF_MASK 0x03
#define CS42L52_CLK_CTL 0x05
#define CLK_AUTODECT_ENABLE (1 << 7)
#define CLK_SPEED_SHIFT 5
#define CLK_DS_MODE 0x00
#define CLK_SS_MODE 0x01
#define CLK_HS_MODE 0x02
#define CLK_QS_MODE 0x03
#define CLK_32K_SR_SHIFT 4
#define CLK_32K 0x01
#define CLK_NO_32K 0x00
#define CLK_27M_MCLK_SHIFT 3
#define CLK_27M_MCLK 0x01
#define CLK_NO_27M 0x00
#define CLK_RATIO_SHIFT 1
#define CLK_R_128 0x00
#define CLK_R_125 0x01
#define CLK_R_132 0x02
#define CLK_R_136 0x03
#define CS42L52_IFACE_CTL1 0x06
#define CS42L52_IFACE_CTL1_MASTER (1 << 7)
#define CS42L52_IFACE_CTL1_SLAVE (0 << 7)
#define CS42L52_IFACE_CTL1_INV_SCLK (1 << 6)
#define CS42L52_IFACE_CTL1_ADC_FMT_I2S (1 << 5)
#define CS42L52_IFACE_CTL1_ADC_FMT_LEFT_J (0 << 5)
#define CS42L52_IFACE_CTL1_DSP_MODE_EN (1 << 4)
#define CS42L52_IFACE_CTL1_DAC_FMT_LEFT_J (0 << 2)
#define CS42L52_IFACE_CTL1_DAC_FMT_I2S (1 << 2)
#define CS42L52_IFACE_CTL1_DAC_FMT_RIGHT_J (2 << 2)
#define CS42L52_IFACE_CTL1_WL_32BIT (0x00)
#define CS42L52_IFACE_CTL1_WL_24BIT (0x01)
#define CS42L52_IFACE_CTL1_WL_20BIT (0x02)
#define CS42L52_IFACE_CTL1_WL_16BIT (0x03)
#define CS42L52_IFACE_CTL1_WL_MASK 0xFFFF
#define CS42L52_IFACE_CTL2 0x07
#define CS42L52_IFACE_CTL2_SC_MC_EQ (1 << 6)
#define CS42L52_IFACE_CTL2_LOOPBACK (1 << 5)
#define CS42L52_IFACE_CTL2_S_MODE_OUTPUT_EN (0 << 4)
#define CS42L52_IFACE_CTL2_S_MODE_OUTPUT_HIZ (1 << 4)
#define CS42L52_IFACE_CTL2_HP_SW_INV (1 << 3)
#define CS42L52_IFACE_CTL2_BIAS_LVL 0x07
#define CS42L52_ADC_PGA_A 0x08
#define CS42L52_ADC_PGA_B 0x09
#define CS42L52_ADC_SEL_SHIFT 5
#define CS42L52_ADC_SEL_AIN1 0x00
#define CS42L52_ADC_SEL_AIN2 0x01
#define CS42L52_ADC_SEL_AIN3 0x02
#define CS42L52_ADC_SEL_AIN4 0x03
#define CS42L52_ADC_SEL_PGA 0x04
#define CS42L52_ANALOG_HPF_CTL 0x0A
#define CS42L52_HPF_CTL_ANLGSFTB (1 << 3)
#define CS42L52_HPF_CTL_ANLGSFTA (1 << 0)
#define CS42L52_ADC_HPF_FREQ 0x0B
#define CS42L52_ADC_MISC_CTL 0x0C
#define CS42L52_ADC_MISC_CTL_SOURCE_DSP (1 << 6)
#define CS42L52_PB_CTL1 0x0D
#define CS42L52_PB_CTL1_HP_GAIN_SHIFT 5
#define CS42L52_PB_CTL1_HP_GAIN_03959 0x00
#define CS42L52_PB_CTL1_HP_GAIN_04571 0x01
#define CS42L52_PB_CTL1_HP_GAIN_05111 0x02
#define CS42L52_PB_CTL1_HP_GAIN_06047 0x03
#define CS42L52_PB_CTL1_HP_GAIN_07099 0x04
#define CS42L52_PB_CTL1_HP_GAIN_08399 0x05
#define CS42L52_PB_CTL1_HP_GAIN_10000 0x06
#define CS42L52_PB_CTL1_HP_GAIN_11430 0x07
#define CS42L52_PB_CTL1_INV_PCMB (1 << 3)
#define CS42L52_PB_CTL1_INV_PCMA (1 << 2)
#define CS42L52_PB_CTL1_MSTB_MUTE (1 << 1)
#define CS42L52_PB_CTL1_MSTA_MUTE (1 << 0)
#define CS42L52_PB_CTL1_MUTE_MASK 0x03
#define CS42L52_PB_CTL1_MUTE 3
#define CS42L52_PB_CTL1_UNMUTE 0
#define CS42L52_MISC_CTL 0x0E
#define CS42L52_MISC_CTL_DEEMPH (1 << 2)
#define CS42L52_MISC_CTL_DIGSFT (1 << 1)
#define CS42L52_MISC_CTL_DIGZC (1 << 0)
#define CS42L52_PB_CTL2 0x0F
#define CS42L52_PB_CTL2_HPB_MUTE (1 << 7)
#define CS42L52_PB_CTL2_HPA_MUTE (1 << 6)
#define CS42L52_PB_CTL2_SPKB_MUTE (1 << 5)
#define CS42L52_PB_CTL2_SPKA_MUTE (1 << 4)
#define CS42L52_PB_CTL2_SPK_SWAP (1 << 2)
#define CS42L52_PB_CTL2_SPK_MONO (1 << 1)
#define CS42L52_PB_CTL2_SPK_MUTE50 (1 << 0)
#define CS42L52_MICA_CTL 0x10
#define CS42L52_MICB_CTL 0x11
#define CS42L52_MIC_CTL_MIC_SEL_MASK 0xBF
#define CS42L52_MIC_CTL_MIC_SEL_SHIFT 6
#define CS42L52_MIC_CTL_TYPE_MASK 0x20
#define CS42L52_MIC_CTL_TYPE_SHIFT 5
#define CS42L52_PGAA_CTL 0x12
#define CS42L52_PGAB_CTL 0x13
#define CS42L52_PGAX_CTL_VOL_12DB 24
#define CS42L52_PGAX_CTL_VOL_6DB 12 /*step size 0.5db*/
#define CS42L52_PASSTHRUA_VOL 0x14
#define CS42L52_PASSTHRUB_VOL 0x15
#define CS42L52_ADCA_VOL 0x16
#define CS42L52_ADCB_VOL 0x17
#define CS42L52_ADCX_VOL_24DB 24 /*step size 1db*/
#define CS42L52_ADCX_VOL_12DB 12
#define CS42L52_ADCX_VOL_6DB 6
#define CS42L52_ADCA_MIXER_VOL 0x18
#define CS42L52_ADCB_MIXER_VOL 0x19
#define CS42L52_ADC_MIXER_VOL_12DB 0x18
#define CS42L52_PCMA_MIXER_VOL 0x1A
#define CS42L52_PCMB_MIXER_VOL 0x1B
#define CS42L52_BEEP_FREQ 0x1C
#define CS42L52_BEEP_VOL 0x1D
#define CS42L52_BEEP_TONE_CTL 0x1E
#define CS42L52_BEEP_RATE_SHIFT 4
#define CS42L52_BEEP_RATE_MASK 0x0F
#define CS42L52_TONE_CTL 0x1F
#define CS42L52_BEEP_EN_MASK 0x3F
#define CS42L52_MASTERA_VOL 0x20
#define CS42L52_MASTERB_VOL 0x21
#define CS42L52_HPA_VOL 0x22
#define CS42L52_HPB_VOL 0x23
#define CS42L52_DEFAULT_HP_VOL 0xF0
#define CS42L52_SPKA_VOL 0x24
#define CS42L52_SPKB_VOL 0x25
#define CS42L52_DEFAULT_SPK_VOL 0xF0
#define CS42L52_ADC_PCM_MIXER 0x26
#define CS42L52_LIMITER_CTL1 0x27
#define CS42L52_LIMITER_CTL2 0x28
#define CS42L52_LIMITER_AT_RATE 0x29
#define CS42L52_ALC_CTL 0x2A
#define CS42L52_ALC_CTL_ALCB_ENABLE_SHIFT 7
#define CS42L52_ALC_CTL_ALCA_ENABLE_SHIFT 6
#define CS42L52_ALC_CTL_FASTEST_ATTACK 0
#define CS42L52_ALC_RATE 0x2B
#define CS42L52_ALC_SLOWEST_RELEASE 0x3F
#define CS42L52_ALC_THRESHOLD 0x2C
#define CS42L52_ALC_MAX_RATE_SHIFT 5
#define CS42L52_ALC_MIN_RATE_SHIFT 2
#define CS42L52_ALC_RATE_0DB 0
#define CS42L52_ALC_RATE_3DB 1
#define CS42L52_ALC_RATE_6DB 2
#define CS42L52_NOISE_GATE_CTL 0x2D
#define CS42L52_NG_ENABLE_SHIFT 6
#define CS42L52_NG_THRESHOLD_SHIFT 2
#define CS42L52_NG_MIN_70DB 2
#define CS42L52_NG_DELAY_SHIFT 0
#define CS42L52_NG_DELAY_100MS 1
#define CS42L52_CLK_STATUS 0x2E
#define CS42L52_BATT_COMPEN 0x2F
#define CS42L52_BATT_LEVEL 0x30
#define CS42L52_SPK_STATUS 0x31
#define CS42L52_SPK_STATUS_PIN_SHIFT 3
#define CS42L52_SPK_STATUS_PIN_HIGH 1
#define CS42L52_TEM_CTL 0x32
#define CS42L52_TEM_CTL_SET 0x80
#define CS42L52_THE_FOLDBACK 0x33
#define CS42L52_CHARGE_PUMP 0x34
#define CS42L52_CHARGE_PUMP_MASK 0xF0
#define CS42L52_CHARGE_PUMP_SHIFT 4
#define CS42L52_FIX_BITS1 0x3E
#define CS42L52_FIX_BITS2 0x47
#define CS42L52_MAX_REGISTER 0x47
#endif

1424
sound/soc/codecs/cs42l56.c Normal file

File diff suppressed because it is too large Load diff

177
sound/soc/codecs/cs42l56.h Normal file
View file

@ -0,0 +1,177 @@
/*
* cs42l52.h -- CS42L56 ALSA SoC audio driver
*
* Copyright 2014 CirrusLogic, Inc.
*
* Author: Brian Austin <brian.austin@cirrus.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#ifndef __CS42L56_H__
#define __CS42L56_H__
#define CS42L56_CHIP_ID_1 0x01
#define CS42L56_CHIP_ID_2 0x02
#define CS42L56_PWRCTL_1 0x03
#define CS42L56_PWRCTL_2 0x04
#define CS42L56_CLKCTL_1 0x05
#define CS42L56_CLKCTL_2 0x06
#define CS42L56_SERIAL_FMT 0x07
#define CS42L56_CLASSH_CTL 0x08
#define CS42L56_MISC_CTL 0x09
#define CS42L56_INT_STATUS 0x0a
#define CS42L56_PLAYBACK_CTL 0x0b
#define CS42L56_DSP_MUTE_CTL 0x0c
#define CS42L56_ADCA_MIX_VOLUME 0x0d
#define CS42L56_ADCB_MIX_VOLUME 0x0e
#define CS42L56_PCMA_MIX_VOLUME 0x0f
#define CS42L56_PCMB_MIX_VOLUME 0x10
#define CS42L56_ANAINPUT_ADV_VOLUME 0x11
#define CS42L56_DIGINPUT_ADV_VOLUME 0x12
#define CS42L56_MASTER_A_VOLUME 0x13
#define CS42L56_MASTER_B_VOLUME 0x14
#define CS42L56_BEEP_FREQ_ONTIME 0x15
#define CS42L56_BEEP_FREQ_OFFTIME 0x16
#define CS42L56_BEEP_TONE_CFG 0x17
#define CS42L56_TONE_CTL 0x18
#define CS42L56_CHAN_MIX_SWAP 0x19
#define CS42L56_AIN_REFCFG_ADC_MUX 0x1a
#define CS42L56_HPF_CTL 0x1b
#define CS42L56_MISC_ADC_CTL 0x1c
#define CS42L56_GAIN_BIAS_CTL 0x1d
#define CS42L56_PGAA_MUX_VOLUME 0x1e
#define CS42L56_PGAB_MUX_VOLUME 0x1f
#define CS42L56_ADCA_ATTENUATOR 0x20
#define CS42L56_ADCB_ATTENUATOR 0x21
#define CS42L56_ALC_EN_ATTACK_RATE 0x22
#define CS42L56_ALC_RELEASE_RATE 0x23
#define CS42L56_ALC_THRESHOLD 0x24
#define CS42L56_NOISE_GATE_CTL 0x25
#define CS42L56_ALC_LIM_SFT_ZC 0x26
#define CS42L56_AMUTE_HPLO_MUX 0x27
#define CS42L56_HPA_VOLUME 0x28
#define CS42L56_HPB_VOLUME 0x29
#define CS42L56_LOA_VOLUME 0x2a
#define CS42L56_LOB_VOLUME 0x2b
#define CS42L56_LIM_THRESHOLD_CTL 0x2c
#define CS42L56_LIM_CTL_RELEASE_RATE 0x2d
#define CS42L56_LIM_ATTACK_RATE 0x2e
/* Device ID and Rev ID Masks */
#define CS42L56_DEVID 0x56
#define CS42L56_CHIP_ID_MASK 0xff
#define CS42L56_AREV_MASK 0x1c
#define CS42L56_MTLREV_MASK 0x03
/* Power bit masks */
#define CS42L56_PDN_ALL_MASK 0x01
#define CS42L56_PDN_ADCA_MASK 0x02
#define CS42L56_PDN_ADCB_MASK 0x04
#define CS42L56_PDN_CHRG_MASK 0x08
#define CS42L56_PDN_BIAS_MASK 0x10
#define CS42L56_PDN_VBUF_MASK 0x20
#define CS42L56_PDN_LOA_MASK 0x03
#define CS42L56_PDN_LOB_MASK 0x0c
#define CS42L56_PDN_HPA_MASK 0x30
#define CS42L56_PDN_HPB_MASK 0xc0
/* serial port and clk masks */
#define CS42L56_MASTER_MODE 0x40
#define CS42L56_SLAVE_MODE 0
#define CS42L56_MS_MODE_MASK 0x40
#define CS42L56_SCLK_INV 0x20
#define CS42L56_SCLK_INV_MASK 0x20
#define CS42L56_SCLK_MCLK_MASK 0x18
#define CS42L56_MCLK_PREDIV 0x04
#define CS42L56_MCLK_PREDIV_MASK 0x04
#define CS42L56_MCLK_DIV2 0x02
#define CS42L56_MCLK_DIV2_MASK 0x02
#define CS42L56_MCLK_DIS_MASK 0x01
#define CS42L56_CLK_AUTO_MASK 0x20
#define CS42L56_CLK_RATIO_MASK 0x1f
#define CS42L56_DIG_FMT_I2S 0
#define CS42L56_DIG_FMT_LEFT_J 0x08
#define CS42L56_DIG_FMT_MASK 0x08
/* Class H and misc ctl masks */
#define CS42L56_ADAPT_PWR_MASK 0xc0
#define CS42L56_CHRG_FREQ_MASK 0x0f
#define CS42L56_DIG_MUX_MASK 0x80
#define CS42L56_ANLGSFT_MASK 0x10
#define CS42L56_ANLGZC_MASK 0x08
#define CS42L56_DIGSFT_MASK 0x04
#define CS42L56_FREEZE_MASK 0x01
#define CS42L56_MIC_BIAS_MASK 0x03
#define CS42L56_HPFA_FREQ_MASK 0x03
#define CS42L56_HPFB_FREQ_MASK 0xc0
#define CS42L56_AIN1A_REF_MASK 0x10
#define CS42L56_AIN2A_REF_MASK 0x40
#define CS42L56_AIN1B_REF_MASK 0x20
#define CS42L56_AIN2B_REF_MASK 0x80
/* Playback Capture ctl masks */
#define CS42L56_PDN_DSP_MASK 0x80
#define CS42L56_DEEMPH_MASK 0x40
#define CS42L56_PLYBCK_GANG_MASK 0x10
#define CS42L56_PCM_INV_MASK 0x0c
#define CS42L56_MUTE_ALL 0xff
#define CS42L56_UNMUTE 0
#define CS42L56_ADCAMIX_MUTE_MASK 0x40
#define CS42L56_ADCBMIX_MUTE_MASK 0x80
#define CS42L56_PCMAMIX_MUTE_MASK 0x10
#define CS42L56_PCMBMIX_MUTE_MASK 0x20
#define CS42L56_MSTB_MUTE_MASK 0x02
#define CS42L56_MSTA_MUTE_MASK 0x01
#define CS42L56_ADCA_MUTE_MASK 0x01
#define CS42L56_ADCB_MUTE_MASK 0x02
#define CS42L56_HP_MUTE_MASK 0x80
#define CS42L56_LO_MUTE_MASK 0x80
/* Beep masks */
#define CS42L56_BEEP_FREQ_MASK 0xf0
#define CS42L56_BEEP_ONTIME_MASK 0x0f
#define CS42L56_BEEP_OFFTIME_MASK 0xe0
#define CS42L56_BEEP_CFG_MASK 0xc0
#define CS42L56_BEEP_TREBCF_MASK 0x18
#define CS42L56_BEEP_BASSCF_MASK 0x06
#define CS42L56_BEEP_TCEN_MASK 0x01
#define CS42L56_BEEP_RATE_SHIFT 4
#define CS42L56_BEEP_EN_MASK 0x3f
/* Supported MCLKS */
#define CS42L56_MCLK_5P6448MHZ 5644800
#define CS42L56_MCLK_6MHZ 6000000
#define CS42L56_MCLK_6P144MHZ 6144000
#define CS42L56_MCLK_11P2896MHZ 11289600
#define CS42L56_MCLK_12MHZ 12000000
#define CS42L56_MCLK_12P288MHZ 12288000
#define CS42L56_MCLK_22P5792MHZ 22579200
#define CS42L56_MCLK_24MHZ 24000000
#define CS42L56_MCLK_24P576MHZ 24576000
/* Clock ratios */
#define CS42L56_MCLK_LRCLK_128 0x08
#define CS42L56_MCLK_LRCLK_125 0x09
#define CS42L56_MCLK_LRCLK_136 0x0b
#define CS42L56_MCLK_LRCLK_192 0x0c
#define CS42L56_MCLK_LRCLK_187P5 0x0d
#define CS42L56_MCLK_LRCLK_256 0x10
#define CS42L56_MCLK_LRCLK_250 0x11
#define CS42L56_MCLK_LRCLK_272 0x13
#define CS42L56_MCLK_LRCLK_384 0x14
#define CS42L56_MCLK_LRCLK_375 0x15
#define CS42L56_MCLK_LRCLK_512 0x18
#define CS42L56_MCLK_LRCLK_500 0x19
#define CS42L56_MCLK_LRCLK_544 0x1b
#define CS42L56_MCLK_LRCLK_750 0x1c
#define CS42L56_MCLK_LRCLK_768 0x1d
#define CS42L56_MAX_REGISTER 0x34
#endif

1509
sound/soc/codecs/cs42l73.c Normal file

File diff suppressed because it is too large Load diff

226
sound/soc/codecs/cs42l73.h Normal file
View file

@ -0,0 +1,226 @@
/*
* ALSA SoC CS42L73 codec driver
*
* Copyright 2011 Cirrus Logic, Inc.
*
* Author: Georgi Vlaev <joe@nucleusys.com>
* Brian Austin <brian.austin@cirrus.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#ifndef __CS42L73_H__
#define __CS42L73_H__
/* I2C Registers */
/* I2C Address: 1001010[R/W] - 10010100 = 0x94(Write); 10010101 = 0x95(Read) */
#define CS42L73_CHIP_ID 0x4a
#define CS42L73_DEVID_AB 0x01 /* Device ID A & B [RO]. */
#define CS42L73_DEVID_CD 0x02 /* Device ID C & D [RO]. */
#define CS42L73_DEVID_E 0x03 /* Device ID E [RO]. */
#define CS42L73_REVID 0x05 /* Revision ID [RO]. */
#define CS42L73_PWRCTL1 0x06 /* Power Control 1. */
#define CS42L73_PWRCTL2 0x07 /* Power Control 2. */
#define CS42L73_PWRCTL3 0x08 /* Power Control 3. */
#define CS42L73_CPFCHC 0x09 /* Charge Pump Freq. Class H Ctl. */
#define CS42L73_OLMBMSDC 0x0A /* Output Load, MIC Bias, MIC2 SDT */
#define CS42L73_DMMCC 0x0B /* Digital MIC & Master Clock Ctl. */
#define CS42L73_XSPC 0x0C /* Auxiliary Serial Port (XSP) Ctl. */
#define CS42L73_XSPMMCC 0x0D /* XSP Master Mode Clocking Control. */
#define CS42L73_ASPC 0x0E /* Audio Serial Port (ASP) Control. */
#define CS42L73_ASPMMCC 0x0F /* ASP Master Mode Clocking Control. */
#define CS42L73_VSPC 0x10 /* Voice Serial Port (VSP) Control. */
#define CS42L73_VSPMMCC 0x11 /* VSP Master Mode Clocking Control. */
#define CS42L73_VXSPFS 0x12 /* VSP & XSP Sample Rate. */
#define CS42L73_MIOPC 0x13 /* Misc. Input & Output Path Control. */
#define CS42L73_ADCIPC 0x14 /* ADC/IP Control. */
#define CS42L73_MICAPREPGAAVOL 0x15 /* MIC 1 [A] PreAmp, PGAA Vol. */
#define CS42L73_MICBPREPGABVOL 0x16 /* MIC 2 [B] PreAmp, PGAB Vol. */
#define CS42L73_IPADVOL 0x17 /* Input Pat7h A Digital Volume. */
#define CS42L73_IPBDVOL 0x18 /* Input Path B Digital Volume. */
#define CS42L73_PBDC 0x19 /* Playback Digital Control. */
#define CS42L73_HLADVOL 0x1A /* HP/Line A Out Digital Vol. */
#define CS42L73_HLBDVOL 0x1B /* HP/Line B Out Digital Vol. */
#define CS42L73_SPKDVOL 0x1C /* Spkphone Out [A] Digital Vol. */
#define CS42L73_ESLDVOL 0x1D /* Ear/Spkphone LO [B] Digital */
#define CS42L73_HPAAVOL 0x1E /* HP A Analog Volume. */
#define CS42L73_HPBAVOL 0x1F /* HP B Analog Volume. */
#define CS42L73_LOAAVOL 0x20 /* Line Out A Analog Volume. */
#define CS42L73_LOBAVOL 0x21 /* Line Out B Analog Volume. */
#define CS42L73_STRINV 0x22 /* Stereo Input Path Adv. Vol. */
#define CS42L73_XSPINV 0x23 /* Auxiliary Port Input Advisory Vol. */
#define CS42L73_ASPINV 0x24 /* Audio Port Input Advisory Vol. */
#define CS42L73_VSPINV 0x25 /* Voice Port Input Advisory Vol. */
#define CS42L73_LIMARATEHL 0x26 /* Lmtr Attack Rate HP/Line. */
#define CS42L73_LIMRRATEHL 0x27 /* Lmtr Ctl, Rel.Rate HP/Line. */
#define CS42L73_LMAXHL 0x28 /* Lmtr Thresholds HP/Line. */
#define CS42L73_LIMARATESPK 0x29 /* Lmtr Attack Rate Spkphone [A]. */
#define CS42L73_LIMRRATESPK 0x2A /* Lmtr Ctl,Release Rate Spk. [A]. */
#define CS42L73_LMAXSPK 0x2B /* Lmtr Thresholds Spkphone [A]. */
#define CS42L73_LIMARATEESL 0x2C /* Lmtr Attack Rate */
#define CS42L73_LIMRRATEESL 0x2D /* Lmtr Ctl,Release Rate */
#define CS42L73_LMAXESL 0x2E /* Lmtr Thresholds */
#define CS42L73_ALCARATE 0x2F /* ALC Enable, Attack Rate AB. */
#define CS42L73_ALCRRATE 0x30 /* ALC Release Rate AB. */
#define CS42L73_ALCMINMAX 0x31 /* ALC Thresholds AB. */
#define CS42L73_NGCAB 0x32 /* Noise Gate Ctl AB. */
#define CS42L73_ALCNGMC 0x33 /* ALC & Noise Gate Misc Ctl. */
#define CS42L73_MIXERCTL 0x34 /* Mixer Control. */
#define CS42L73_HLAIPAA 0x35 /* HP/LO Left Mixer: L. */
#define CS42L73_HLBIPBA 0x36 /* HP/LO Right Mixer: R. */
#define CS42L73_HLAXSPAA 0x37 /* HP/LO Left Mixer: XSP L */
#define CS42L73_HLBXSPBA 0x38 /* HP/LO Right Mixer: XSP R */
#define CS42L73_HLAASPAA 0x39 /* HP/LO Left Mixer: ASP L */
#define CS42L73_HLBASPBA 0x3A /* HP/LO Right Mixer: ASP R */
#define CS42L73_HLAVSPMA 0x3B /* HP/LO Left Mixer: VSP. */
#define CS42L73_HLBVSPMA 0x3C /* HP/LO Right Mixer: VSP */
#define CS42L73_XSPAIPAA 0x3D /* XSP Left Mixer: Left */
#define CS42L73_XSPBIPBA 0x3E /* XSP Rt. Mixer: Right */
#define CS42L73_XSPAXSPAA 0x3F /* XSP Left Mixer: XSP L */
#define CS42L73_XSPBXSPBA 0x40 /* XSP Rt. Mixer: XSP R */
#define CS42L73_XSPAASPAA 0x41 /* XSP Left Mixer: ASP L */
#define CS42L73_XSPAASPBA 0x42 /* XSP Rt. Mixer: ASP R */
#define CS42L73_XSPAVSPMA 0x43 /* XSP Left Mixer: VSP */
#define CS42L73_XSPBVSPMA 0x44 /* XSP Rt. Mixer: VSP */
#define CS42L73_ASPAIPAA 0x45 /* ASP Left Mixer: Left */
#define CS42L73_ASPBIPBA 0x46 /* ASP Rt. Mixer: Right */
#define CS42L73_ASPAXSPAA 0x47 /* ASP Left Mixer: XSP L */
#define CS42L73_ASPBXSPBA 0x48 /* ASP Rt. Mixer: XSP R */
#define CS42L73_ASPAASPAA 0x49 /* ASP Left Mixer: ASP L */
#define CS42L73_ASPBASPBA 0x4A /* ASP Rt. Mixer: ASP R */
#define CS42L73_ASPAVSPMA 0x4B /* ASP Left Mixer: VSP */
#define CS42L73_ASPBVSPMA 0x4C /* ASP Rt. Mixer: VSP */
#define CS42L73_VSPAIPAA 0x4D /* VSP Left Mixer: Left */
#define CS42L73_VSPBIPBA 0x4E /* VSP Rt. Mixer: Right */
#define CS42L73_VSPAXSPAA 0x4F /* VSP Left Mixer: XSP L */
#define CS42L73_VSPBXSPBA 0x50 /* VSP Rt. Mixer: XSP R */
#define CS42L73_VSPAASPAA 0x51 /* VSP Left Mixer: ASP Left */
#define CS42L73_VSPBASPBA 0x52 /* VSP Rt. Mixer: ASP Right */
#define CS42L73_VSPAVSPMA 0x53 /* VSP Left Mixer: VSP */
#define CS42L73_VSPBVSPMA 0x54 /* VSP Rt. Mixer: VSP */
#define CS42L73_MMIXCTL 0x55 /* Mono Mixer Controls. */
#define CS42L73_SPKMIPMA 0x56 /* SPK Mono Mixer: In. Path */
#define CS42L73_SPKMXSPA 0x57 /* SPK Mono Mixer: XSP Mono/L/R Att. */
#define CS42L73_SPKMASPA 0x58 /* SPK Mono Mixer: ASP Mono/L/R Att. */
#define CS42L73_SPKMVSPMA 0x59 /* SPK Mono Mixer: VSP Mono Atten. */
#define CS42L73_ESLMIPMA 0x5A /* Ear/SpLO Mono Mixer: */
#define CS42L73_ESLMXSPA 0x5B /* Ear/SpLO Mono Mixer: XSP */
#define CS42L73_ESLMASPA 0x5C /* Ear/SpLO Mono Mixer: ASP */
#define CS42L73_ESLMVSPMA 0x5D /* Ear/SpLO Mono Mixer: VSP */
#define CS42L73_IM1 0x5E /* Interrupt Mask 1. */
#define CS42L73_IM2 0x5F /* Interrupt Mask 2. */
#define CS42L73_IS1 0x60 /* Interrupt Status 1 [RO]. */
#define CS42L73_IS2 0x61 /* Interrupt Status 2 [RO]. */
#define CS42L73_MAX_REGISTER 0x61 /* Total Registers */
/* Bitfield Definitions */
/* CS42L73_PWRCTL1 */
#define CS42L73_PDN_ADCB (1 << 7)
#define CS42L73_PDN_DMICB (1 << 6)
#define CS42L73_PDN_ADCA (1 << 5)
#define CS42L73_PDN_DMICA (1 << 4)
#define CS42L73_PDN_LDO (1 << 2)
#define CS42L73_DISCHG_FILT (1 << 1)
#define CS42L73_PDN (1 << 0)
/* CS42L73_PWRCTL2 */
#define CS42L73_PDN_MIC2_BIAS (1 << 7)
#define CS42L73_PDN_MIC1_BIAS (1 << 6)
#define CS42L73_PDN_VSP (1 << 4)
#define CS42L73_PDN_ASP_SDOUT (1 << 3)
#define CS42L73_PDN_ASP_SDIN (1 << 2)
#define CS42L73_PDN_XSP_SDOUT (1 << 1)
#define CS42L73_PDN_XSP_SDIN (1 << 0)
/* CS42L73_PWRCTL3 */
#define CS42L73_PDN_THMS (1 << 5)
#define CS42L73_PDN_SPKLO (1 << 4)
#define CS42L73_PDN_EAR (1 << 3)
#define CS42L73_PDN_SPK (1 << 2)
#define CS42L73_PDN_LO (1 << 1)
#define CS42L73_PDN_HP (1 << 0)
/* Thermal Overload Detect. Requires interrupt ... */
#define CS42L73_THMOVLD_150C 0
#define CS42L73_THMOVLD_132C 1
#define CS42L73_THMOVLD_115C 2
#define CS42L73_THMOVLD_098C 3
#define CS42L73_CHARGEPUMP_MASK (0xF0)
/* CS42L73_ASPC, CS42L73_XSPC, CS42L73_VSPC */
#define CS42L73_SP_3ST (1 << 7)
#define CS42L73_SPDIF_I2S (0 << 6)
#define CS42L73_SPDIF_PCM (1 << 6)
#define CS42L73_PCM_MODE0 (0 << 4)
#define CS42L73_PCM_MODE1 (1 << 4)
#define CS42L73_PCM_MODE2 (2 << 4)
#define CS42L73_PCM_MODE_MASK (3 << 4)
#define CS42L73_PCM_BIT_ORDER (1 << 3)
#define CS42L73_MCK_SCLK_64FS (0 << 0)
#define CS42L73_MCK_SCLK_MCLK (2 << 0)
#define CS42L73_MCK_SCLK_PREMCLK (3 << 0)
/* CS42L73_xSPMMCC */
#define CS42L73_MS_MASTER (1 << 7)
/* CS42L73_DMMCC */
#define CS42L73_MCLKDIS (1 << 0)
#define CS42L73_MCLKSEL_MCLK2 (1 << 4)
#define CS42L73_MCLKSEL_MCLK1 (0 << 4)
/* CS42L73 MCLK derived from MCLK1 or MCLK2 */
#define CS42L73_CLKID_MCLK1 0
#define CS42L73_CLKID_MCLK2 1
#define CS42L73_MCLKXDIV 0
#define CS42L73_MMCCDIV 1
#define CS42L73_XSP 0
#define CS42L73_ASP 1
#define CS42L73_VSP 2
/* IS1, IM1 */
#define CS42L73_MIC2_SDET (1 << 6)
#define CS42L73_THMOVLD (1 << 4)
#define CS42L73_DIGMIXOVFL (1 << 3)
#define CS42L73_IPBOVFL (1 << 1)
#define CS42L73_IPAOVFL (1 << 0)
/* Analog Softramp */
#define CS42L73_ANLGOSFT (1 << 0)
/* HP A/B Analog Mute */
#define CS42L73_HPA_MUTE (1 << 7)
/* LO A/B Analog Mute */
#define CS42L73_LOA_MUTE (1 << 7)
/* Digital Mute */
#define CS42L73_HLAD_MUTE (1 << 0)
#define CS42L73_HLBD_MUTE (1 << 1)
#define CS42L73_SPKD_MUTE (1 << 2)
#define CS42L73_ESLD_MUTE (1 << 3)
/* Misc defines for codec */
#define CS42L73_DEVID 0x00042A73
#define CS42L73_MCLKX_MIN 5644800
#define CS42L73_MCLKX_MAX 38400000
#define CS42L73_SPC(id) (CS42L73_XSPC + (id << 1))
#define CS42L73_MMCC(id) (CS42L73_XSPMMCC + (id << 1))
#define CS42L73_SPFS(id) ((id == CS42L73_ASP) ? CS42L73_ASPC : CS42L73_VXSPFS)
#endif /* __CS42L73_H__ */

View file

@ -0,0 +1,64 @@
/*
* Cirrus Logic CS42448/CS42888 Audio CODEC DAI I2C driver
*
* Copyright (C) 2014 Freescale Semiconductor, Inc.
*
* Author: Nicolin Chen <Guangyu.Chen@freescale.com>
*
* This file is licensed under the terms of the GNU General Public License
* version 2. This program is licensed "as is" without any warranty of any
* kind, whether express or implied.
*/
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/pm_runtime.h>
#include <sound/soc.h>
#include "cs42xx8.h"
static int cs42xx8_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
u32 ret = cs42xx8_probe(&i2c->dev,
devm_regmap_init_i2c(i2c, &cs42xx8_regmap_config));
if (ret)
return ret;
pm_runtime_enable(&i2c->dev);
pm_request_idle(&i2c->dev);
return 0;
}
static int cs42xx8_i2c_remove(struct i2c_client *i2c)
{
snd_soc_unregister_codec(&i2c->dev);
pm_runtime_disable(&i2c->dev);
return 0;
}
static struct i2c_device_id cs42xx8_i2c_id[] = {
{"cs42448", (kernel_ulong_t)&cs42448_data},
{"cs42888", (kernel_ulong_t)&cs42888_data},
{}
};
MODULE_DEVICE_TABLE(i2c, cs42xx8_i2c_id);
static struct i2c_driver cs42xx8_i2c_driver = {
.driver = {
.name = "cs42xx8",
.owner = THIS_MODULE,
.pm = &cs42xx8_pm,
},
.probe = cs42xx8_i2c_probe,
.remove = cs42xx8_i2c_remove,
.id_table = cs42xx8_i2c_id,
};
module_i2c_driver(cs42xx8_i2c_driver);
MODULE_DESCRIPTION("Cirrus Logic CS42448/CS42888 ALSA SoC Codec I2C Driver");
MODULE_AUTHOR("Freescale Semiconductor, Inc.");
MODULE_LICENSE("GPL");

603
sound/soc/codecs/cs42xx8.c Normal file
View file

@ -0,0 +1,603 @@
/*
* Cirrus Logic CS42448/CS42888 Audio CODEC Digital Audio Interface (DAI) driver
*
* Copyright (C) 2014 Freescale Semiconductor, Inc.
*
* Author: Nicolin Chen <Guangyu.Chen@freescale.com>
*
* This file is licensed under the terms of the GNU General Public License
* version 2. This program is licensed "as is" without any warranty of any
* kind, whether express or implied.
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/tlv.h>
#include "cs42xx8.h"
#define CS42XX8_NUM_SUPPLIES 4
static const char *const cs42xx8_supply_names[CS42XX8_NUM_SUPPLIES] = {
"VA",
"VD",
"VLS",
"VLC",
};
#define CS42XX8_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S24_LE | \
SNDRV_PCM_FMTBIT_S32_LE)
/* codec private data */
struct cs42xx8_priv {
struct regulator_bulk_data supplies[CS42XX8_NUM_SUPPLIES];
const struct cs42xx8_driver_data *drvdata;
struct regmap *regmap;
struct clk *clk;
bool slave_mode;
unsigned long sysclk;
};
/* -127.5dB to 0dB with step of 0.5dB */
static const DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 1);
/* -64dB to 24dB with step of 0.5dB */
static const DECLARE_TLV_DB_SCALE(adc_tlv, -6400, 50, 0);
static const char *const cs42xx8_adc_single[] = { "Differential", "Single-Ended" };
static const char *const cs42xx8_szc[] = { "Immediate Change", "Zero Cross",
"Soft Ramp", "Soft Ramp on Zero Cross" };
static const struct soc_enum adc1_single_enum =
SOC_ENUM_SINGLE(CS42XX8_ADCCTL, 4, 2, cs42xx8_adc_single);
static const struct soc_enum adc2_single_enum =
SOC_ENUM_SINGLE(CS42XX8_ADCCTL, 3, 2, cs42xx8_adc_single);
static const struct soc_enum adc3_single_enum =
SOC_ENUM_SINGLE(CS42XX8_ADCCTL, 2, 2, cs42xx8_adc_single);
static const struct soc_enum dac_szc_enum =
SOC_ENUM_SINGLE(CS42XX8_TXCTL, 5, 4, cs42xx8_szc);
static const struct soc_enum adc_szc_enum =
SOC_ENUM_SINGLE(CS42XX8_TXCTL, 0, 4, cs42xx8_szc);
static const struct snd_kcontrol_new cs42xx8_snd_controls[] = {
SOC_DOUBLE_R_TLV("DAC1 Playback Volume", CS42XX8_VOLAOUT1,
CS42XX8_VOLAOUT2, 0, 0xff, 1, dac_tlv),
SOC_DOUBLE_R_TLV("DAC2 Playback Volume", CS42XX8_VOLAOUT3,
CS42XX8_VOLAOUT4, 0, 0xff, 1, dac_tlv),
SOC_DOUBLE_R_TLV("DAC3 Playback Volume", CS42XX8_VOLAOUT5,
CS42XX8_VOLAOUT6, 0, 0xff, 1, dac_tlv),
SOC_DOUBLE_R_TLV("DAC4 Playback Volume", CS42XX8_VOLAOUT7,
CS42XX8_VOLAOUT8, 0, 0xff, 1, dac_tlv),
SOC_DOUBLE_R_S_TLV("ADC1 Capture Volume", CS42XX8_VOLAIN1,
CS42XX8_VOLAIN2, 0, -0x80, 0x30, 7, 0, adc_tlv),
SOC_DOUBLE_R_S_TLV("ADC2 Capture Volume", CS42XX8_VOLAIN3,
CS42XX8_VOLAIN4, 0, -0x80, 0x30, 7, 0, adc_tlv),
SOC_DOUBLE("DAC1 Invert Switch", CS42XX8_DACINV, 0, 1, 1, 0),
SOC_DOUBLE("DAC2 Invert Switch", CS42XX8_DACINV, 2, 3, 1, 0),
SOC_DOUBLE("DAC3 Invert Switch", CS42XX8_DACINV, 4, 5, 1, 0),
SOC_DOUBLE("DAC4 Invert Switch", CS42XX8_DACINV, 6, 7, 1, 0),
SOC_DOUBLE("ADC1 Invert Switch", CS42XX8_ADCINV, 0, 1, 1, 0),
SOC_DOUBLE("ADC2 Invert Switch", CS42XX8_ADCINV, 2, 3, 1, 0),
SOC_SINGLE("ADC High-Pass Filter Switch", CS42XX8_ADCCTL, 7, 1, 1),
SOC_SINGLE("DAC De-emphasis Switch", CS42XX8_ADCCTL, 5, 1, 0),
SOC_ENUM("ADC1 Single Ended Mode Switch", adc1_single_enum),
SOC_ENUM("ADC2 Single Ended Mode Switch", adc2_single_enum),
SOC_SINGLE("DAC Single Volume Control Switch", CS42XX8_TXCTL, 7, 1, 0),
SOC_ENUM("DAC Soft Ramp & Zero Cross Control Switch", dac_szc_enum),
SOC_SINGLE("DAC Auto Mute Switch", CS42XX8_TXCTL, 4, 1, 0),
SOC_SINGLE("Mute ADC Serial Port Switch", CS42XX8_TXCTL, 3, 1, 0),
SOC_SINGLE("ADC Single Volume Control Switch", CS42XX8_TXCTL, 2, 1, 0),
SOC_ENUM("ADC Soft Ramp & Zero Cross Control Switch", adc_szc_enum),
};
static const struct snd_kcontrol_new cs42xx8_adc3_snd_controls[] = {
SOC_DOUBLE_R_S_TLV("ADC3 Capture Volume", CS42XX8_VOLAIN5,
CS42XX8_VOLAIN6, 0, -0x80, 0x30, 7, 0, adc_tlv),
SOC_DOUBLE("ADC3 Invert Switch", CS42XX8_ADCINV, 4, 5, 1, 0),
SOC_ENUM("ADC3 Single Ended Mode Switch", adc3_single_enum),
};
static const struct snd_soc_dapm_widget cs42xx8_dapm_widgets[] = {
SND_SOC_DAPM_DAC("DAC1", "Playback", CS42XX8_PWRCTL, 1, 1),
SND_SOC_DAPM_DAC("DAC2", "Playback", CS42XX8_PWRCTL, 2, 1),
SND_SOC_DAPM_DAC("DAC3", "Playback", CS42XX8_PWRCTL, 3, 1),
SND_SOC_DAPM_DAC("DAC4", "Playback", CS42XX8_PWRCTL, 4, 1),
SND_SOC_DAPM_OUTPUT("AOUT1L"),
SND_SOC_DAPM_OUTPUT("AOUT1R"),
SND_SOC_DAPM_OUTPUT("AOUT2L"),
SND_SOC_DAPM_OUTPUT("AOUT2R"),
SND_SOC_DAPM_OUTPUT("AOUT3L"),
SND_SOC_DAPM_OUTPUT("AOUT3R"),
SND_SOC_DAPM_OUTPUT("AOUT4L"),
SND_SOC_DAPM_OUTPUT("AOUT4R"),
SND_SOC_DAPM_ADC("ADC1", "Capture", CS42XX8_PWRCTL, 5, 1),
SND_SOC_DAPM_ADC("ADC2", "Capture", CS42XX8_PWRCTL, 6, 1),
SND_SOC_DAPM_INPUT("AIN1L"),
SND_SOC_DAPM_INPUT("AIN1R"),
SND_SOC_DAPM_INPUT("AIN2L"),
SND_SOC_DAPM_INPUT("AIN2R"),
SND_SOC_DAPM_SUPPLY("PWR", CS42XX8_PWRCTL, 0, 1, NULL, 0),
};
static const struct snd_soc_dapm_widget cs42xx8_adc3_dapm_widgets[] = {
SND_SOC_DAPM_ADC("ADC3", "Capture", CS42XX8_PWRCTL, 7, 1),
SND_SOC_DAPM_INPUT("AIN3L"),
SND_SOC_DAPM_INPUT("AIN3R"),
};
static const struct snd_soc_dapm_route cs42xx8_dapm_routes[] = {
/* Playback */
{ "AOUT1L", NULL, "DAC1" },
{ "AOUT1R", NULL, "DAC1" },
{ "DAC1", NULL, "PWR" },
{ "AOUT2L", NULL, "DAC2" },
{ "AOUT2R", NULL, "DAC2" },
{ "DAC2", NULL, "PWR" },
{ "AOUT3L", NULL, "DAC3" },
{ "AOUT3R", NULL, "DAC3" },
{ "DAC3", NULL, "PWR" },
{ "AOUT4L", NULL, "DAC4" },
{ "AOUT4R", NULL, "DAC4" },
{ "DAC4", NULL, "PWR" },
/* Capture */
{ "ADC1", NULL, "AIN1L" },
{ "ADC1", NULL, "AIN1R" },
{ "ADC1", NULL, "PWR" },
{ "ADC2", NULL, "AIN2L" },
{ "ADC2", NULL, "AIN2R" },
{ "ADC2", NULL, "PWR" },
};
static const struct snd_soc_dapm_route cs42xx8_adc3_dapm_routes[] = {
/* Capture */
{ "ADC3", NULL, "AIN3L" },
{ "ADC3", NULL, "AIN3R" },
{ "ADC3", NULL, "PWR" },
};
struct cs42xx8_ratios {
unsigned int ratio;
unsigned char speed;
unsigned char mclk;
};
static const struct cs42xx8_ratios cs42xx8_ratios[] = {
{ 64, CS42XX8_FM_QUAD, CS42XX8_FUNCMOD_MFREQ_256(4) },
{ 96, CS42XX8_FM_QUAD, CS42XX8_FUNCMOD_MFREQ_384(4) },
{ 128, CS42XX8_FM_QUAD, CS42XX8_FUNCMOD_MFREQ_512(4) },
{ 192, CS42XX8_FM_QUAD, CS42XX8_FUNCMOD_MFREQ_768(4) },
{ 256, CS42XX8_FM_SINGLE, CS42XX8_FUNCMOD_MFREQ_256(1) },
{ 384, CS42XX8_FM_SINGLE, CS42XX8_FUNCMOD_MFREQ_384(1) },
{ 512, CS42XX8_FM_SINGLE, CS42XX8_FUNCMOD_MFREQ_512(1) },
{ 768, CS42XX8_FM_SINGLE, CS42XX8_FUNCMOD_MFREQ_768(1) },
{ 1024, CS42XX8_FM_SINGLE, CS42XX8_FUNCMOD_MFREQ_1024(1) }
};
static int cs42xx8_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = codec_dai->codec;
struct cs42xx8_priv *cs42xx8 = snd_soc_codec_get_drvdata(codec);
cs42xx8->sysclk = freq;
return 0;
}
static int cs42xx8_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int format)
{
struct snd_soc_codec *codec = codec_dai->codec;
struct cs42xx8_priv *cs42xx8 = snd_soc_codec_get_drvdata(codec);
u32 val;
/* Set DAI format */
switch (format & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_LEFT_J:
val = CS42XX8_INTF_DAC_DIF_LEFTJ | CS42XX8_INTF_ADC_DIF_LEFTJ;
break;
case SND_SOC_DAIFMT_I2S:
val = CS42XX8_INTF_DAC_DIF_I2S | CS42XX8_INTF_ADC_DIF_I2S;
break;
case SND_SOC_DAIFMT_RIGHT_J:
val = CS42XX8_INTF_DAC_DIF_RIGHTJ | CS42XX8_INTF_ADC_DIF_RIGHTJ;
break;
case SND_SOC_DAIFMT_DSP_A:
val = CS42XX8_INTF_DAC_DIF_TDM | CS42XX8_INTF_ADC_DIF_TDM;
break;
default:
dev_err(codec->dev, "unsupported dai format\n");
return -EINVAL;
}
regmap_update_bits(cs42xx8->regmap, CS42XX8_INTF,
CS42XX8_INTF_DAC_DIF_MASK |
CS42XX8_INTF_ADC_DIF_MASK, val);
/* Set master/slave audio interface */
switch (format & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBS_CFS:
cs42xx8->slave_mode = true;
break;
case SND_SOC_DAIFMT_CBM_CFM:
cs42xx8->slave_mode = false;
break;
default:
dev_err(codec->dev, "unsupported master/slave mode\n");
return -EINVAL;
}
return 0;
}
static int cs42xx8_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
struct cs42xx8_priv *cs42xx8 = snd_soc_codec_get_drvdata(codec);
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
u32 ratio = cs42xx8->sysclk / params_rate(params);
u32 i, fm, val, mask;
for (i = 0; i < ARRAY_SIZE(cs42xx8_ratios); i++) {
if (cs42xx8_ratios[i].ratio == ratio)
break;
}
if (i == ARRAY_SIZE(cs42xx8_ratios)) {
dev_err(codec->dev, "unsupported sysclk ratio\n");
return -EINVAL;
}
mask = CS42XX8_FUNCMOD_MFREQ_MASK;
val = cs42xx8_ratios[i].mclk;
fm = cs42xx8->slave_mode ? CS42XX8_FM_AUTO : cs42xx8_ratios[i].speed;
regmap_update_bits(cs42xx8->regmap, CS42XX8_FUNCMOD,
CS42XX8_FUNCMOD_xC_FM_MASK(tx) | mask,
CS42XX8_FUNCMOD_xC_FM(tx, fm) | val);
return 0;
}
static int cs42xx8_digital_mute(struct snd_soc_dai *dai, int mute)
{
struct snd_soc_codec *codec = dai->codec;
struct cs42xx8_priv *cs42xx8 = snd_soc_codec_get_drvdata(codec);
regmap_update_bits(cs42xx8->regmap, CS42XX8_DACMUTE,
CS42XX8_DACMUTE_ALL, mute ? CS42XX8_DACMUTE_ALL : 0);
return 0;
}
static const struct snd_soc_dai_ops cs42xx8_dai_ops = {
.set_fmt = cs42xx8_set_dai_fmt,
.set_sysclk = cs42xx8_set_dai_sysclk,
.hw_params = cs42xx8_hw_params,
.digital_mute = cs42xx8_digital_mute,
};
static struct snd_soc_dai_driver cs42xx8_dai = {
.playback = {
.stream_name = "Playback",
.channels_min = 1,
.channels_max = 8,
.rates = SNDRV_PCM_RATE_8000_192000,
.formats = CS42XX8_FORMATS,
},
.capture = {
.stream_name = "Capture",
.channels_min = 1,
.rates = SNDRV_PCM_RATE_8000_192000,
.formats = CS42XX8_FORMATS,
},
.ops = &cs42xx8_dai_ops,
};
static const struct reg_default cs42xx8_reg[] = {
{ 0x01, 0x01 }, /* Chip I.D. and Revision Register */
{ 0x02, 0x00 }, /* Power Control */
{ 0x03, 0xF0 }, /* Functional Mode */
{ 0x04, 0x46 }, /* Interface Formats */
{ 0x05, 0x00 }, /* ADC Control & DAC De-Emphasis */
{ 0x06, 0x10 }, /* Transition Control */
{ 0x07, 0x00 }, /* DAC Channel Mute */
{ 0x08, 0x00 }, /* Volume Control AOUT1 */
{ 0x09, 0x00 }, /* Volume Control AOUT2 */
{ 0x0a, 0x00 }, /* Volume Control AOUT3 */
{ 0x0b, 0x00 }, /* Volume Control AOUT4 */
{ 0x0c, 0x00 }, /* Volume Control AOUT5 */
{ 0x0d, 0x00 }, /* Volume Control AOUT6 */
{ 0x0e, 0x00 }, /* Volume Control AOUT7 */
{ 0x0f, 0x00 }, /* Volume Control AOUT8 */
{ 0x10, 0x00 }, /* DAC Channel Invert */
{ 0x11, 0x00 }, /* Volume Control AIN1 */
{ 0x12, 0x00 }, /* Volume Control AIN2 */
{ 0x13, 0x00 }, /* Volume Control AIN3 */
{ 0x14, 0x00 }, /* Volume Control AIN4 */
{ 0x15, 0x00 }, /* Volume Control AIN5 */
{ 0x16, 0x00 }, /* Volume Control AIN6 */
{ 0x17, 0x00 }, /* ADC Channel Invert */
{ 0x18, 0x00 }, /* Status Control */
{ 0x1a, 0x00 }, /* Status Mask */
{ 0x1b, 0x00 }, /* MUTEC Pin Control */
};
static bool cs42xx8_volatile_register(struct device *dev, unsigned int reg)
{
switch (reg) {
case CS42XX8_STATUS:
return true;
default:
return false;
}
}
static bool cs42xx8_writeable_register(struct device *dev, unsigned int reg)
{
switch (reg) {
case CS42XX8_CHIPID:
case CS42XX8_STATUS:
return false;
default:
return true;
}
}
const struct regmap_config cs42xx8_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = CS42XX8_LASTREG,
.reg_defaults = cs42xx8_reg,
.num_reg_defaults = ARRAY_SIZE(cs42xx8_reg),
.volatile_reg = cs42xx8_volatile_register,
.writeable_reg = cs42xx8_writeable_register,
.cache_type = REGCACHE_RBTREE,
};
EXPORT_SYMBOL_GPL(cs42xx8_regmap_config);
static int cs42xx8_codec_probe(struct snd_soc_codec *codec)
{
struct cs42xx8_priv *cs42xx8 = snd_soc_codec_get_drvdata(codec);
struct snd_soc_dapm_context *dapm = &codec->dapm;
switch (cs42xx8->drvdata->num_adcs) {
case 3:
snd_soc_add_codec_controls(codec, cs42xx8_adc3_snd_controls,
ARRAY_SIZE(cs42xx8_adc3_snd_controls));
snd_soc_dapm_new_controls(dapm, cs42xx8_adc3_dapm_widgets,
ARRAY_SIZE(cs42xx8_adc3_dapm_widgets));
snd_soc_dapm_add_routes(dapm, cs42xx8_adc3_dapm_routes,
ARRAY_SIZE(cs42xx8_adc3_dapm_routes));
break;
default:
break;
}
/* Mute all DAC channels */
regmap_write(cs42xx8->regmap, CS42XX8_DACMUTE, CS42XX8_DACMUTE_ALL);
return 0;
}
static const struct snd_soc_codec_driver cs42xx8_driver = {
.probe = cs42xx8_codec_probe,
.idle_bias_off = true,
.controls = cs42xx8_snd_controls,
.num_controls = ARRAY_SIZE(cs42xx8_snd_controls),
.dapm_widgets = cs42xx8_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(cs42xx8_dapm_widgets),
.dapm_routes = cs42xx8_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(cs42xx8_dapm_routes),
};
const struct cs42xx8_driver_data cs42448_data = {
.name = "cs42448",
.num_adcs = 3,
};
EXPORT_SYMBOL_GPL(cs42448_data);
const struct cs42xx8_driver_data cs42888_data = {
.name = "cs42888",
.num_adcs = 2,
};
EXPORT_SYMBOL_GPL(cs42888_data);
static const struct of_device_id cs42xx8_of_match[] = {
{ .compatible = "cirrus,cs42448", .data = &cs42448_data, },
{ .compatible = "cirrus,cs42888", .data = &cs42888_data, },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, cs42xx8_of_match);
EXPORT_SYMBOL_GPL(cs42xx8_of_match);
int cs42xx8_probe(struct device *dev, struct regmap *regmap)
{
const struct of_device_id *of_id = of_match_device(cs42xx8_of_match, dev);
struct cs42xx8_priv *cs42xx8;
int ret, val, i;
cs42xx8 = devm_kzalloc(dev, sizeof(*cs42xx8), GFP_KERNEL);
if (cs42xx8 == NULL)
return -ENOMEM;
dev_set_drvdata(dev, cs42xx8);
if (of_id)
cs42xx8->drvdata = of_id->data;
if (!cs42xx8->drvdata) {
dev_err(dev, "failed to find driver data\n");
return -EINVAL;
}
cs42xx8->clk = devm_clk_get(dev, "mclk");
if (IS_ERR(cs42xx8->clk)) {
dev_err(dev, "failed to get the clock: %ld\n",
PTR_ERR(cs42xx8->clk));
return -EINVAL;
}
cs42xx8->sysclk = clk_get_rate(cs42xx8->clk);
for (i = 0; i < ARRAY_SIZE(cs42xx8->supplies); i++)
cs42xx8->supplies[i].supply = cs42xx8_supply_names[i];
ret = devm_regulator_bulk_get(dev,
ARRAY_SIZE(cs42xx8->supplies), cs42xx8->supplies);
if (ret) {
dev_err(dev, "failed to request supplies: %d\n", ret);
return ret;
}
ret = regulator_bulk_enable(ARRAY_SIZE(cs42xx8->supplies),
cs42xx8->supplies);
if (ret) {
dev_err(dev, "failed to enable supplies: %d\n", ret);
return ret;
}
/* Make sure hardware reset done */
msleep(5);
cs42xx8->regmap = regmap;
if (IS_ERR(cs42xx8->regmap)) {
ret = PTR_ERR(cs42xx8->regmap);
dev_err(dev, "failed to allocate regmap: %d\n", ret);
goto err_enable;
}
/*
* We haven't marked the chip revision as volatile due to
* sharing a register with the right input volume; explicitly
* bypass the cache to read it.
*/
regcache_cache_bypass(cs42xx8->regmap, true);
/* Validate the chip ID */
ret = regmap_read(cs42xx8->regmap, CS42XX8_CHIPID, &val);
if (ret < 0) {
dev_err(dev, "failed to get device ID, ret = %d", ret);
goto err_enable;
}
/* The top four bits of the chip ID should be 0000 */
if (((val & CS42XX8_CHIPID_CHIP_ID_MASK) >> 4) != 0x00) {
dev_err(dev, "unmatched chip ID: %d\n",
(val & CS42XX8_CHIPID_CHIP_ID_MASK) >> 4);
ret = -EINVAL;
goto err_enable;
}
dev_info(dev, "found device, revision %X\n",
val & CS42XX8_CHIPID_REV_ID_MASK);
regcache_cache_bypass(cs42xx8->regmap, false);
cs42xx8_dai.name = cs42xx8->drvdata->name;
/* Each adc supports stereo input */
cs42xx8_dai.capture.channels_max = cs42xx8->drvdata->num_adcs * 2;
ret = snd_soc_register_codec(dev, &cs42xx8_driver, &cs42xx8_dai, 1);
if (ret) {
dev_err(dev, "failed to register codec:%d\n", ret);
goto err_enable;
}
regcache_cache_only(cs42xx8->regmap, true);
err_enable:
regulator_bulk_disable(ARRAY_SIZE(cs42xx8->supplies),
cs42xx8->supplies);
return ret;
}
EXPORT_SYMBOL_GPL(cs42xx8_probe);
#ifdef CONFIG_PM_RUNTIME
static int cs42xx8_runtime_resume(struct device *dev)
{
struct cs42xx8_priv *cs42xx8 = dev_get_drvdata(dev);
int ret;
ret = clk_prepare_enable(cs42xx8->clk);
if (ret) {
dev_err(dev, "failed to enable mclk: %d\n", ret);
return ret;
}
ret = regulator_bulk_enable(ARRAY_SIZE(cs42xx8->supplies),
cs42xx8->supplies);
if (ret) {
dev_err(dev, "failed to enable supplies: %d\n", ret);
goto err_clk;
}
/* Make sure hardware reset done */
msleep(5);
regcache_cache_only(cs42xx8->regmap, false);
ret = regcache_sync(cs42xx8->regmap);
if (ret) {
dev_err(dev, "failed to sync regmap: %d\n", ret);
goto err_bulk;
}
return 0;
err_bulk:
regulator_bulk_disable(ARRAY_SIZE(cs42xx8->supplies),
cs42xx8->supplies);
err_clk:
clk_disable_unprepare(cs42xx8->clk);
return ret;
}
static int cs42xx8_runtime_suspend(struct device *dev)
{
struct cs42xx8_priv *cs42xx8 = dev_get_drvdata(dev);
regcache_cache_only(cs42xx8->regmap, true);
regulator_bulk_disable(ARRAY_SIZE(cs42xx8->supplies),
cs42xx8->supplies);
clk_disable_unprepare(cs42xx8->clk);
return 0;
}
#endif
const struct dev_pm_ops cs42xx8_pm = {
SET_RUNTIME_PM_OPS(cs42xx8_runtime_suspend, cs42xx8_runtime_resume, NULL)
};
EXPORT_SYMBOL_GPL(cs42xx8_pm);
MODULE_DESCRIPTION("Cirrus Logic CS42448/CS42888 ALSA SoC Codec Driver");
MODULE_AUTHOR("Freescale Semiconductor, Inc.");
MODULE_LICENSE("GPL");

238
sound/soc/codecs/cs42xx8.h Normal file
View file

@ -0,0 +1,238 @@
/*
* cs42xx8.h - Cirrus Logic CS42448/CS42888 Audio CODEC driver header file
*
* Copyright (C) 2014 Freescale Semiconductor, Inc.
*
* Author: Nicolin Chen <Guangyu.Chen@freescale.com>
*
* This file is licensed under the terms of the GNU General Public License
* version 2. This program is licensed "as is" without any warranty of any
* kind, whether express or implied.
*/
#ifndef _CS42XX8_H
#define _CS42XX8_H
struct cs42xx8_driver_data {
char name[32];
int num_adcs;
};
extern const struct dev_pm_ops cs42xx8_pm;
extern const struct cs42xx8_driver_data cs42448_data;
extern const struct cs42xx8_driver_data cs42888_data;
extern const struct regmap_config cs42xx8_regmap_config;
int cs42xx8_probe(struct device *dev, struct regmap *regmap);
/* CS42888 register map */
#define CS42XX8_CHIPID 0x01 /* Chip ID */
#define CS42XX8_PWRCTL 0x02 /* Power Control */
#define CS42XX8_FUNCMOD 0x03 /* Functional Mode */
#define CS42XX8_INTF 0x04 /* Interface Formats */
#define CS42XX8_ADCCTL 0x05 /* ADC Control */
#define CS42XX8_TXCTL 0x06 /* Transition Control */
#define CS42XX8_DACMUTE 0x07 /* DAC Mute Control */
#define CS42XX8_VOLAOUT1 0x08 /* Volume Control AOUT1 */
#define CS42XX8_VOLAOUT2 0x09 /* Volume Control AOUT2 */
#define CS42XX8_VOLAOUT3 0x0A /* Volume Control AOUT3 */
#define CS42XX8_VOLAOUT4 0x0B /* Volume Control AOUT4 */
#define CS42XX8_VOLAOUT5 0x0C /* Volume Control AOUT5 */
#define CS42XX8_VOLAOUT6 0x0D /* Volume Control AOUT6 */
#define CS42XX8_VOLAOUT7 0x0E /* Volume Control AOUT7 */
#define CS42XX8_VOLAOUT8 0x0F /* Volume Control AOUT8 */
#define CS42XX8_DACINV 0x10 /* DAC Channel Invert */
#define CS42XX8_VOLAIN1 0x11 /* Volume Control AIN1 */
#define CS42XX8_VOLAIN2 0x12 /* Volume Control AIN2 */
#define CS42XX8_VOLAIN3 0x13 /* Volume Control AIN3 */
#define CS42XX8_VOLAIN4 0x14 /* Volume Control AIN4 */
#define CS42XX8_VOLAIN5 0x15 /* Volume Control AIN5 */
#define CS42XX8_VOLAIN6 0x16 /* Volume Control AIN6 */
#define CS42XX8_ADCINV 0x17 /* ADC Channel Invert */
#define CS42XX8_STATUSCTL 0x18 /* Status Control */
#define CS42XX8_STATUS 0x19 /* Status */
#define CS42XX8_STATUSM 0x1A /* Status Mask */
#define CS42XX8_MUTEC 0x1B /* MUTEC Pin Control */
#define CS42XX8_FIRSTREG CS42XX8_CHIPID
#define CS42XX8_LASTREG CS42XX8_MUTEC
#define CS42XX8_NUMREGS (CS42XX8_LASTREG - CS42XX8_FIRSTREG + 1)
#define CS42XX8_I2C_INCR 0x80
/* Chip I.D. and Revision Register (Address 01h) */
#define CS42XX8_CHIPID_CHIP_ID_MASK 0xF0
#define CS42XX8_CHIPID_REV_ID_MASK 0x0F
/* Power Control (Address 02h) */
#define CS42XX8_PWRCTL_PDN_ADC3_SHIFT 7
#define CS42XX8_PWRCTL_PDN_ADC3_MASK (1 << CS42XX8_PWRCTL_PDN_ADC3_SHIFT)
#define CS42XX8_PWRCTL_PDN_ADC3 (1 << CS42XX8_PWRCTL_PDN_ADC3_SHIFT)
#define CS42XX8_PWRCTL_PDN_ADC2_SHIFT 6
#define CS42XX8_PWRCTL_PDN_ADC2_MASK (1 << CS42XX8_PWRCTL_PDN_ADC2_SHIFT)
#define CS42XX8_PWRCTL_PDN_ADC2 (1 << CS42XX8_PWRCTL_PDN_ADC2_SHIFT)
#define CS42XX8_PWRCTL_PDN_ADC1_SHIFT 5
#define CS42XX8_PWRCTL_PDN_ADC1_MASK (1 << CS42XX8_PWRCTL_PDN_ADC1_SHIFT)
#define CS42XX8_PWRCTL_PDN_ADC1 (1 << CS42XX8_PWRCTL_PDN_ADC1_SHIFT)
#define CS42XX8_PWRCTL_PDN_DAC4_SHIFT 4
#define CS42XX8_PWRCTL_PDN_DAC4_MASK (1 << CS42XX8_PWRCTL_PDN_DAC4_SHIFT)
#define CS42XX8_PWRCTL_PDN_DAC4 (1 << CS42XX8_PWRCTL_PDN_DAC4_SHIFT)
#define CS42XX8_PWRCTL_PDN_DAC3_SHIFT 3
#define CS42XX8_PWRCTL_PDN_DAC3_MASK (1 << CS42XX8_PWRCTL_PDN_DAC3_SHIFT)
#define CS42XX8_PWRCTL_PDN_DAC3 (1 << CS42XX8_PWRCTL_PDN_DAC3_SHIFT)
#define CS42XX8_PWRCTL_PDN_DAC2_SHIFT 2
#define CS42XX8_PWRCTL_PDN_DAC2_MASK (1 << CS42XX8_PWRCTL_PDN_DAC2_SHIFT)
#define CS42XX8_PWRCTL_PDN_DAC2 (1 << CS42XX8_PWRCTL_PDN_DAC2_SHIFT)
#define CS42XX8_PWRCTL_PDN_DAC1_SHIFT 1
#define CS42XX8_PWRCTL_PDN_DAC1_MASK (1 << CS42XX8_PWRCTL_PDN_DAC1_SHIFT)
#define CS42XX8_PWRCTL_PDN_DAC1 (1 << CS42XX8_PWRCTL_PDN_DAC1_SHIFT)
#define CS42XX8_PWRCTL_PDN_SHIFT 0
#define CS42XX8_PWRCTL_PDN_MASK (1 << CS42XX8_PWRCTL_PDN_SHIFT)
#define CS42XX8_PWRCTL_PDN (1 << CS42XX8_PWRCTL_PDN_SHIFT)
/* Functional Mode (Address 03h) */
#define CS42XX8_FUNCMOD_DAC_FM_SHIFT 6
#define CS42XX8_FUNCMOD_DAC_FM_WIDTH 2
#define CS42XX8_FUNCMOD_DAC_FM_MASK (((1 << CS42XX8_FUNCMOD_DAC_FM_WIDTH) - 1) << CS42XX8_FUNCMOD_DAC_FM_SHIFT)
#define CS42XX8_FUNCMOD_DAC_FM(v) ((v) << CS42XX8_FUNCMOD_DAC_FM_SHIFT)
#define CS42XX8_FUNCMOD_ADC_FM_SHIFT 4
#define CS42XX8_FUNCMOD_ADC_FM_WIDTH 2
#define CS42XX8_FUNCMOD_ADC_FM_MASK (((1 << CS42XX8_FUNCMOD_ADC_FM_WIDTH) - 1) << CS42XX8_FUNCMOD_ADC_FM_SHIFT)
#define CS42XX8_FUNCMOD_ADC_FM(v) ((v) << CS42XX8_FUNCMOD_ADC_FM_SHIFT)
#define CS42XX8_FUNCMOD_xC_FM_MASK(x) ((x) ? CS42XX8_FUNCMOD_DAC_FM_MASK : CS42XX8_FUNCMOD_ADC_FM_MASK)
#define CS42XX8_FUNCMOD_xC_FM(x, v) ((x) ? CS42XX8_FUNCMOD_DAC_FM(v) : CS42XX8_FUNCMOD_ADC_FM(v))
#define CS42XX8_FUNCMOD_MFREQ_SHIFT 1
#define CS42XX8_FUNCMOD_MFREQ_WIDTH 3
#define CS42XX8_FUNCMOD_MFREQ_MASK (((1 << CS42XX8_FUNCMOD_MFREQ_WIDTH) - 1) << CS42XX8_FUNCMOD_MFREQ_SHIFT)
#define CS42XX8_FUNCMOD_MFREQ_256(s) ((0 << CS42XX8_FUNCMOD_MFREQ_SHIFT) >> (s >> 1))
#define CS42XX8_FUNCMOD_MFREQ_384(s) ((1 << CS42XX8_FUNCMOD_MFREQ_SHIFT) >> (s >> 1))
#define CS42XX8_FUNCMOD_MFREQ_512(s) ((2 << CS42XX8_FUNCMOD_MFREQ_SHIFT) >> (s >> 1))
#define CS42XX8_FUNCMOD_MFREQ_768(s) ((3 << CS42XX8_FUNCMOD_MFREQ_SHIFT) >> (s >> 1))
#define CS42XX8_FUNCMOD_MFREQ_1024(s) ((4 << CS42XX8_FUNCMOD_MFREQ_SHIFT) >> (s >> 1))
#define CS42XX8_FM_SINGLE 0
#define CS42XX8_FM_DOUBLE 1
#define CS42XX8_FM_QUAD 2
#define CS42XX8_FM_AUTO 3
/* Interface Formats (Address 04h) */
#define CS42XX8_INTF_FREEZE_SHIFT 7
#define CS42XX8_INTF_FREEZE_MASK (1 << CS42XX8_INTF_FREEZE_SHIFT)
#define CS42XX8_INTF_FREEZE (1 << CS42XX8_INTF_FREEZE_SHIFT)
#define CS42XX8_INTF_AUX_DIF_SHIFT 6
#define CS42XX8_INTF_AUX_DIF_MASK (1 << CS42XX8_INTF_AUX_DIF_SHIFT)
#define CS42XX8_INTF_AUX_DIF (1 << CS42XX8_INTF_AUX_DIF_SHIFT)
#define CS42XX8_INTF_DAC_DIF_SHIFT 3
#define CS42XX8_INTF_DAC_DIF_WIDTH 3
#define CS42XX8_INTF_DAC_DIF_MASK (((1 << CS42XX8_INTF_DAC_DIF_WIDTH) - 1) << CS42XX8_INTF_DAC_DIF_SHIFT)
#define CS42XX8_INTF_DAC_DIF_LEFTJ (0 << CS42XX8_INTF_DAC_DIF_SHIFT)
#define CS42XX8_INTF_DAC_DIF_I2S (1 << CS42XX8_INTF_DAC_DIF_SHIFT)
#define CS42XX8_INTF_DAC_DIF_RIGHTJ (2 << CS42XX8_INTF_DAC_DIF_SHIFT)
#define CS42XX8_INTF_DAC_DIF_RIGHTJ_16 (3 << CS42XX8_INTF_DAC_DIF_SHIFT)
#define CS42XX8_INTF_DAC_DIF_ONELINE_20 (4 << CS42XX8_INTF_DAC_DIF_SHIFT)
#define CS42XX8_INTF_DAC_DIF_ONELINE_24 (5 << CS42XX8_INTF_DAC_DIF_SHIFT)
#define CS42XX8_INTF_DAC_DIF_TDM (6 << CS42XX8_INTF_DAC_DIF_SHIFT)
#define CS42XX8_INTF_ADC_DIF_SHIFT 0
#define CS42XX8_INTF_ADC_DIF_WIDTH 3
#define CS42XX8_INTF_ADC_DIF_MASK (((1 << CS42XX8_INTF_ADC_DIF_WIDTH) - 1) << CS42XX8_INTF_ADC_DIF_SHIFT)
#define CS42XX8_INTF_ADC_DIF_LEFTJ (0 << CS42XX8_INTF_ADC_DIF_SHIFT)
#define CS42XX8_INTF_ADC_DIF_I2S (1 << CS42XX8_INTF_ADC_DIF_SHIFT)
#define CS42XX8_INTF_ADC_DIF_RIGHTJ (2 << CS42XX8_INTF_ADC_DIF_SHIFT)
#define CS42XX8_INTF_ADC_DIF_RIGHTJ_16 (3 << CS42XX8_INTF_ADC_DIF_SHIFT)
#define CS42XX8_INTF_ADC_DIF_ONELINE_20 (4 << CS42XX8_INTF_ADC_DIF_SHIFT)
#define CS42XX8_INTF_ADC_DIF_ONELINE_24 (5 << CS42XX8_INTF_ADC_DIF_SHIFT)
#define CS42XX8_INTF_ADC_DIF_TDM (6 << CS42XX8_INTF_ADC_DIF_SHIFT)
/* ADC Control & DAC De-Emphasis (Address 05h) */
#define CS42XX8_ADCCTL_ADC_HPF_FREEZE_SHIFT 7
#define CS42XX8_ADCCTL_ADC_HPF_FREEZE_MASK (1 << CS42XX8_ADCCTL_ADC_HPF_FREEZE_SHIFT)
#define CS42XX8_ADCCTL_ADC_HPF_FREEZE (1 << CS42XX8_ADCCTL_ADC_HPF_FREEZE_SHIFT)
#define CS42XX8_ADCCTL_DAC_DEM_SHIFT 5
#define CS42XX8_ADCCTL_DAC_DEM_MASK (1 << CS42XX8_ADCCTL_DAC_DEM_SHIFT)
#define CS42XX8_ADCCTL_DAC_DEM (1 << CS42XX8_ADCCTL_DAC_DEM_SHIFT)
#define CS42XX8_ADCCTL_ADC1_SINGLE_SHIFT 4
#define CS42XX8_ADCCTL_ADC1_SINGLE_MASK (1 << CS42XX8_ADCCTL_ADC1_SINGLE_SHIFT)
#define CS42XX8_ADCCTL_ADC1_SINGLE (1 << CS42XX8_ADCCTL_ADC1_SINGLE_SHIFT)
#define CS42XX8_ADCCTL_ADC2_SINGLE_SHIFT 3
#define CS42XX8_ADCCTL_ADC2_SINGLE_MASK (1 << CS42XX8_ADCCTL_ADC2_SINGLE_SHIFT)
#define CS42XX8_ADCCTL_ADC2_SINGLE (1 << CS42XX8_ADCCTL_ADC2_SINGLE_SHIFT)
#define CS42XX8_ADCCTL_ADC3_SINGLE_SHIFT 2
#define CS42XX8_ADCCTL_ADC3_SINGLE_MASK (1 << CS42XX8_ADCCTL_ADC3_SINGLE_SHIFT)
#define CS42XX8_ADCCTL_ADC3_SINGLE (1 << CS42XX8_ADCCTL_ADC3_SINGLE_SHIFT)
#define CS42XX8_ADCCTL_AIN5_MUX_SHIFT 1
#define CS42XX8_ADCCTL_AIN5_MUX_MASK (1 << CS42XX8_ADCCTL_AIN5_MUX_SHIFT)
#define CS42XX8_ADCCTL_AIN5_MUX (1 << CS42XX8_ADCCTL_AIN5_MUX_SHIFT)
#define CS42XX8_ADCCTL_AIN6_MUX_SHIFT 0
#define CS42XX8_ADCCTL_AIN6_MUX_MASK (1 << CS42XX8_ADCCTL_AIN6_MUX_SHIFT)
#define CS42XX8_ADCCTL_AIN6_MUX (1 << CS42XX8_ADCCTL_AIN6_MUX_SHIFT)
/* Transition Control (Address 06h) */
#define CS42XX8_TXCTL_DAC_SNGVOL_SHIFT 7
#define CS42XX8_TXCTL_DAC_SNGVOL_MASK (1 << CS42XX8_TXCTL_DAC_SNGVOL_SHIFT)
#define CS42XX8_TXCTL_DAC_SNGVOL (1 << CS42XX8_TXCTL_DAC_SNGVOL_SHIFT)
#define CS42XX8_TXCTL_DAC_SZC_SHIFT 5
#define CS42XX8_TXCTL_DAC_SZC_WIDTH 2
#define CS42XX8_TXCTL_DAC_SZC_MASK (((1 << CS42XX8_TXCTL_DAC_SZC_WIDTH) - 1) << CS42XX8_TXCTL_DAC_SZC_SHIFT)
#define CS42XX8_TXCTL_DAC_SZC_IC (0 << CS42XX8_TXCTL_DAC_SZC_SHIFT)
#define CS42XX8_TXCTL_DAC_SZC_ZC (1 << CS42XX8_TXCTL_DAC_SZC_SHIFT)
#define CS42XX8_TXCTL_DAC_SZC_SR (2 << CS42XX8_TXCTL_DAC_SZC_SHIFT)
#define CS42XX8_TXCTL_DAC_SZC_SRZC (3 << CS42XX8_TXCTL_DAC_SZC_SHIFT)
#define CS42XX8_TXCTL_AMUTE_SHIFT 4
#define CS42XX8_TXCTL_AMUTE_MASK (1 << CS42XX8_TXCTL_AMUTE_SHIFT)
#define CS42XX8_TXCTL_AMUTE (1 << CS42XX8_TXCTL_AMUTE_SHIFT)
#define CS42XX8_TXCTL_MUTE_ADC_SP_SHIFT 3
#define CS42XX8_TXCTL_MUTE_ADC_SP_MASK (1 << CS42XX8_TXCTL_MUTE_ADC_SP_SHIFT)
#define CS42XX8_TXCTL_MUTE_ADC_SP (1 << CS42XX8_TXCTL_MUTE_ADC_SP_SHIFT)
#define CS42XX8_TXCTL_ADC_SNGVOL_SHIFT 2
#define CS42XX8_TXCTL_ADC_SNGVOL_MASK (1 << CS42XX8_TXCTL_ADC_SNGVOL_SHIFT)
#define CS42XX8_TXCTL_ADC_SNGVOL (1 << CS42XX8_TXCTL_ADC_SNGVOL_SHIFT)
#define CS42XX8_TXCTL_ADC_SZC_SHIFT 0
#define CS42XX8_TXCTL_ADC_SZC_MASK (((1 << CS42XX8_TXCTL_ADC_SZC_WIDTH) - 1) << CS42XX8_TXCTL_ADC_SZC_SHIFT)
#define CS42XX8_TXCTL_ADC_SZC_IC (0 << CS42XX8_TXCTL_ADC_SZC_SHIFT)
#define CS42XX8_TXCTL_ADC_SZC_ZC (1 << CS42XX8_TXCTL_ADC_SZC_SHIFT)
#define CS42XX8_TXCTL_ADC_SZC_SR (2 << CS42XX8_TXCTL_ADC_SZC_SHIFT)
#define CS42XX8_TXCTL_ADC_SZC_SRZC (3 << CS42XX8_TXCTL_ADC_SZC_SHIFT)
/* DAC Channel Mute (Address 07h) */
#define CS42XX8_DACMUTE_AOUT(n) (0x1 << n)
#define CS42XX8_DACMUTE_ALL 0xff
/* Status Control (Address 18h)*/
#define CS42XX8_STATUSCTL_INI_SHIFT 2
#define CS42XX8_STATUSCTL_INI_WIDTH 2
#define CS42XX8_STATUSCTL_INI_MASK (((1 << CS42XX8_STATUSCTL_INI_WIDTH) - 1) << CS42XX8_STATUSCTL_INI_SHIFT)
#define CS42XX8_STATUSCTL_INT_ACTIVE_HIGH (0 << CS42XX8_STATUSCTL_INI_SHIFT)
#define CS42XX8_STATUSCTL_INT_ACTIVE_LOW (1 << CS42XX8_STATUSCTL_INI_SHIFT)
#define CS42XX8_STATUSCTL_INT_OPEN_DRAIN (2 << CS42XX8_STATUSCTL_INI_SHIFT)
/* Status (Address 19h)*/
#define CS42XX8_STATUS_DAC_CLK_ERR_SHIFT 4
#define CS42XX8_STATUS_DAC_CLK_ERR_MASK (1 << CS42XX8_STATUS_DAC_CLK_ERR_SHIFT)
#define CS42XX8_STATUS_ADC_CLK_ERR_SHIFT 3
#define CS42XX8_STATUS_ADC_CLK_ERR_MASK (1 << CS42XX8_STATUS_ADC_CLK_ERR_SHIFT)
#define CS42XX8_STATUS_ADC3_OVFL_SHIFT 2
#define CS42XX8_STATUS_ADC3_OVFL_MASK (1 << CS42XX8_STATUS_ADC3_OVFL_SHIFT)
#define CS42XX8_STATUS_ADC2_OVFL_SHIFT 1
#define CS42XX8_STATUS_ADC2_OVFL_MASK (1 << CS42XX8_STATUS_ADC2_OVFL_SHIFT)
#define CS42XX8_STATUS_ADC1_OVFL_SHIFT 0
#define CS42XX8_STATUS_ADC1_OVFL_MASK (1 << CS42XX8_STATUS_ADC1_OVFL_SHIFT)
/* Status Mask (Address 1Ah) */
#define CS42XX8_STATUS_DAC_CLK_ERR_M_SHIFT 4
#define CS42XX8_STATUS_DAC_CLK_ERR_M_MASK (1 << CS42XX8_STATUS_DAC_CLK_ERR_M_SHIFT)
#define CS42XX8_STATUS_ADC_CLK_ERR_M_SHIFT 3
#define CS42XX8_STATUS_ADC_CLK_ERR_M_MASK (1 << CS42XX8_STATUS_ADC_CLK_ERR_M_SHIFT)
#define CS42XX8_STATUS_ADC3_OVFL_M_SHIFT 2
#define CS42XX8_STATUS_ADC3_OVFL_M_MASK (1 << CS42XX8_STATUS_ADC3_OVFL_M_SHIFT)
#define CS42XX8_STATUS_ADC2_OVFL_M_SHIFT 1
#define CS42XX8_STATUS_ADC2_OVFL_M_MASK (1 << CS42XX8_STATUS_ADC2_OVFL_M_SHIFT)
#define CS42XX8_STATUS_ADC1_OVFL_M_SHIFT 0
#define CS42XX8_STATUS_ADC1_OVFL_M_MASK (1 << CS42XX8_STATUS_ADC1_OVFL_M_SHIFT)
/* MUTEC Pin Control (Address 1Bh) */
#define CS42XX8_MUTEC_MCPOLARITY_SHIFT 1
#define CS42XX8_MUTEC_MCPOLARITY_MASK (1 << CS42XX8_MUTEC_MCPOLARITY_SHIFT)
#define CS42XX8_MUTEC_MCPOLARITY_ACTIVE_LOW (0 << CS42XX8_MUTEC_MCPOLARITY_SHIFT)
#define CS42XX8_MUTEC_MCPOLARITY_ACTIVE_HIGH (1 << CS42XX8_MUTEC_MCPOLARITY_SHIFT)
#define CS42XX8_MUTEC_MUTEC_ACTIVE_SHIFT 0
#define CS42XX8_MUTEC_MUTEC_ACTIVE_MASK (1 << CS42XX8_MUTEC_MUTEC_ACTIVE_SHIFT)
#define CS42XX8_MUTEC_MUTEC_ACTIVE (1 << CS42XX8_MUTEC_MUTEC_ACTIVE_SHIFT)
#endif /* _CS42XX8_H */

443
sound/soc/codecs/cx20442.c Normal file
View file

@ -0,0 +1,443 @@
/*
* cx20442.c -- CX20442 ALSA Soc Audio driver
*
* Copyright 2009 Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
*
* Initially based on sound/soc/codecs/wm8400.c
* Copyright 2008, 2009 Wolfson Microelectronics PLC.
* Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*/
#include <linux/tty.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/regulator/consumer.h>
#include <sound/core.h>
#include <sound/initval.h>
#include <sound/soc.h>
#include "cx20442.h"
struct cx20442_priv {
void *control_data;
struct regulator *por;
};
#define CX20442_PM 0x0
#define CX20442_TELIN 0
#define CX20442_TELOUT 1
#define CX20442_MIC 2
#define CX20442_SPKOUT 3
#define CX20442_AGC 4
static const struct snd_soc_dapm_widget cx20442_dapm_widgets[] = {
SND_SOC_DAPM_OUTPUT("TELOUT"),
SND_SOC_DAPM_OUTPUT("SPKOUT"),
SND_SOC_DAPM_OUTPUT("AGCOUT"),
SND_SOC_DAPM_MIXER("SPKOUT Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("TELOUT Amp", CX20442_PM, CX20442_TELOUT, 0, NULL, 0),
SND_SOC_DAPM_PGA("SPKOUT Amp", CX20442_PM, CX20442_SPKOUT, 0, NULL, 0),
SND_SOC_DAPM_PGA("SPKOUT AGC", CX20442_PM, CX20442_AGC, 0, NULL, 0),
SND_SOC_DAPM_DAC("DAC", "Playback", SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_ADC("ADC", "Capture", SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_MIXER("Input Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_MICBIAS("TELIN Bias", CX20442_PM, CX20442_TELIN, 0),
SND_SOC_DAPM_MICBIAS("MIC Bias", CX20442_PM, CX20442_MIC, 0),
SND_SOC_DAPM_PGA("MIC AGC", CX20442_PM, CX20442_AGC, 0, NULL, 0),
SND_SOC_DAPM_INPUT("TELIN"),
SND_SOC_DAPM_INPUT("MIC"),
SND_SOC_DAPM_INPUT("AGCIN"),
};
static const struct snd_soc_dapm_route cx20442_audio_map[] = {
{"TELOUT", NULL, "TELOUT Amp"},
{"SPKOUT", NULL, "SPKOUT Mixer"},
{"SPKOUT Mixer", NULL, "SPKOUT Amp"},
{"TELOUT Amp", NULL, "DAC"},
{"SPKOUT Amp", NULL, "DAC"},
{"SPKOUT Mixer", NULL, "SPKOUT AGC"},
{"SPKOUT AGC", NULL, "AGCIN"},
{"AGCOUT", NULL, "MIC AGC"},
{"MIC AGC", NULL, "MIC"},
{"MIC Bias", NULL, "MIC"},
{"Input Mixer", NULL, "MIC Bias"},
{"TELIN Bias", NULL, "TELIN"},
{"Input Mixer", NULL, "TELIN Bias"},
{"ADC", NULL, "Input Mixer"},
};
static unsigned int cx20442_read_reg_cache(struct snd_soc_codec *codec,
unsigned int reg)
{
u8 *reg_cache = codec->reg_cache;
if (reg >= codec->driver->reg_cache_size)
return -EINVAL;
return reg_cache[reg];
}
enum v253_vls {
V253_VLS_NONE = 0,
V253_VLS_T,
V253_VLS_L,
V253_VLS_LT,
V253_VLS_S,
V253_VLS_ST,
V253_VLS_M,
V253_VLS_MST,
V253_VLS_S1,
V253_VLS_S1T,
V253_VLS_MS1T,
V253_VLS_M1,
V253_VLS_M1ST,
V253_VLS_M1S1T,
V253_VLS_H,
V253_VLS_HT,
V253_VLS_MS,
V253_VLS_MS1,
V253_VLS_M1S,
V253_VLS_M1S1,
V253_VLS_TEST,
};
static int cx20442_pm_to_v253_vls(u8 value)
{
switch (value & ~(1 << CX20442_AGC)) {
case 0:
return V253_VLS_T;
case (1 << CX20442_SPKOUT):
case (1 << CX20442_MIC):
case (1 << CX20442_SPKOUT) | (1 << CX20442_MIC):
return V253_VLS_M1S1;
case (1 << CX20442_TELOUT):
case (1 << CX20442_TELIN):
case (1 << CX20442_TELOUT) | (1 << CX20442_TELIN):
return V253_VLS_L;
case (1 << CX20442_TELOUT) | (1 << CX20442_MIC):
return V253_VLS_NONE;
}
return -EINVAL;
}
static int cx20442_pm_to_v253_vsp(u8 value)
{
switch (value & ~(1 << CX20442_AGC)) {
case (1 << CX20442_SPKOUT):
case (1 << CX20442_MIC):
case (1 << CX20442_SPKOUT) | (1 << CX20442_MIC):
return (bool)(value & (1 << CX20442_AGC));
}
return (value & (1 << CX20442_AGC)) ? -EINVAL : 0;
}
static int cx20442_write(struct snd_soc_codec *codec, unsigned int reg,
unsigned int value)
{
struct cx20442_priv *cx20442 = snd_soc_codec_get_drvdata(codec);
u8 *reg_cache = codec->reg_cache;
int vls, vsp, old, len;
char buf[18];
if (reg >= codec->driver->reg_cache_size)
return -EINVAL;
/* hw_write and control_data pointers required for talking to the modem
* are expected to be set by the line discipline initialization code */
if (!codec->hw_write || !cx20442->control_data)
return -EIO;
old = reg_cache[reg];
reg_cache[reg] = value;
vls = cx20442_pm_to_v253_vls(value);
if (vls < 0)
return vls;
vsp = cx20442_pm_to_v253_vsp(value);
if (vsp < 0)
return vsp;
if ((vls == V253_VLS_T) ||
(vls == cx20442_pm_to_v253_vls(old))) {
if (vsp == cx20442_pm_to_v253_vsp(old))
return 0;
len = snprintf(buf, ARRAY_SIZE(buf), "at+vsp=%d\r", vsp);
} else if (vsp == cx20442_pm_to_v253_vsp(old))
len = snprintf(buf, ARRAY_SIZE(buf), "at+vls=%d\r", vls);
else
len = snprintf(buf, ARRAY_SIZE(buf),
"at+vls=%d;+vsp=%d\r", vls, vsp);
if (unlikely(len > (ARRAY_SIZE(buf) - 1)))
return -ENOMEM;
dev_dbg(codec->dev, "%s: %s\n", __func__, buf);
if (codec->hw_write(cx20442->control_data, buf, len) != len)
return -EIO;
return 0;
}
/*
* Line discpline related code
*
* Any of the callback functions below can be used in two ways:
* 1) registerd by a machine driver as one of line discipline operations,
* 2) called from a machine's provided line discipline callback function
* in case when extra machine specific code must be run as well.
*/
/* Modem init: echo off, digital speaker off, quiet off, voice mode */
static const char *v253_init = "ate0m0q0+fclass=8\r";
/* Line discipline .open() */
static int v253_open(struct tty_struct *tty)
{
int ret, len = strlen(v253_init);
/* Doesn't make sense without write callback */
if (!tty->ops->write)
return -EINVAL;
/* Won't work if no codec pointer has been passed by a card driver */
if (!tty->disc_data)
return -ENODEV;
if (tty->ops->write(tty, v253_init, len) != len) {
ret = -EIO;
goto err;
}
/* Actual setup will be performed after the modem responds. */
return 0;
err:
tty->disc_data = NULL;
return ret;
}
/* Line discipline .close() */
static void v253_close(struct tty_struct *tty)
{
struct snd_soc_codec *codec = tty->disc_data;
struct cx20442_priv *cx20442;
tty->disc_data = NULL;
if (!codec)
return;
cx20442 = snd_soc_codec_get_drvdata(codec);
/* Prevent the codec driver from further accessing the modem */
codec->hw_write = NULL;
cx20442->control_data = NULL;
codec->component.card->pop_time = 0;
}
/* Line discipline .hangup() */
static int v253_hangup(struct tty_struct *tty)
{
v253_close(tty);
return 0;
}
/* Line discipline .receive_buf() */
static void v253_receive(struct tty_struct *tty,
const unsigned char *cp, char *fp, int count)
{
struct snd_soc_codec *codec = tty->disc_data;
struct cx20442_priv *cx20442;
if (!codec)
return;
cx20442 = snd_soc_codec_get_drvdata(codec);
if (!cx20442->control_data) {
/* First modem response, complete setup procedure */
/* Set up codec driver access to modem controls */
cx20442->control_data = tty;
codec->hw_write = (hw_write_t)tty->ops->write;
codec->component.card->pop_time = 1;
}
}
/* Line discipline .write_wakeup() */
static void v253_wakeup(struct tty_struct *tty)
{
}
struct tty_ldisc_ops v253_ops = {
.magic = TTY_LDISC_MAGIC,
.name = "cx20442",
.owner = THIS_MODULE,
.open = v253_open,
.close = v253_close,
.hangup = v253_hangup,
.receive_buf = v253_receive,
.write_wakeup = v253_wakeup,
};
EXPORT_SYMBOL_GPL(v253_ops);
/*
* Codec DAI
*/
static struct snd_soc_dai_driver cx20442_dai = {
.name = "cx20442-voice",
.playback = {
.stream_name = "Playback",
.channels_min = 1,
.channels_max = 1,
.rates = SNDRV_PCM_RATE_8000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
.capture = {
.stream_name = "Capture",
.channels_min = 1,
.channels_max = 1,
.rates = SNDRV_PCM_RATE_8000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
};
static int cx20442_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
struct cx20442_priv *cx20442 = snd_soc_codec_get_drvdata(codec);
int err = 0;
switch (level) {
case SND_SOC_BIAS_PREPARE:
if (codec->dapm.bias_level != SND_SOC_BIAS_STANDBY)
break;
if (IS_ERR(cx20442->por))
err = PTR_ERR(cx20442->por);
else
err = regulator_enable(cx20442->por);
break;
case SND_SOC_BIAS_STANDBY:
if (codec->dapm.bias_level != SND_SOC_BIAS_PREPARE)
break;
if (IS_ERR(cx20442->por))
err = PTR_ERR(cx20442->por);
else
err = regulator_disable(cx20442->por);
break;
default:
break;
}
if (!err)
codec->dapm.bias_level = level;
return err;
}
static int cx20442_codec_probe(struct snd_soc_codec *codec)
{
struct cx20442_priv *cx20442;
cx20442 = kzalloc(sizeof(struct cx20442_priv), GFP_KERNEL);
if (cx20442 == NULL)
return -ENOMEM;
cx20442->por = regulator_get(codec->dev, "POR");
if (IS_ERR(cx20442->por))
dev_warn(codec->dev, "failed to get the regulator");
cx20442->control_data = NULL;
snd_soc_codec_set_drvdata(codec, cx20442);
codec->hw_write = NULL;
codec->component.card->pop_time = 0;
return 0;
}
/* power down chip */
static int cx20442_codec_remove(struct snd_soc_codec *codec)
{
struct cx20442_priv *cx20442 = snd_soc_codec_get_drvdata(codec);
if (cx20442->control_data) {
struct tty_struct *tty = cx20442->control_data;
tty_hangup(tty);
}
if (!IS_ERR(cx20442->por)) {
/* should be already in STANDBY, hence disabled */
regulator_put(cx20442->por);
}
snd_soc_codec_set_drvdata(codec, NULL);
kfree(cx20442);
return 0;
}
static const u8 cx20442_reg;
static struct snd_soc_codec_driver cx20442_codec_dev = {
.probe = cx20442_codec_probe,
.remove = cx20442_codec_remove,
.set_bias_level = cx20442_set_bias_level,
.reg_cache_default = &cx20442_reg,
.reg_cache_size = 1,
.reg_word_size = sizeof(u8),
.read = cx20442_read_reg_cache,
.write = cx20442_write,
.dapm_widgets = cx20442_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(cx20442_dapm_widgets),
.dapm_routes = cx20442_audio_map,
.num_dapm_routes = ARRAY_SIZE(cx20442_audio_map),
};
static int cx20442_platform_probe(struct platform_device *pdev)
{
return snd_soc_register_codec(&pdev->dev,
&cx20442_codec_dev, &cx20442_dai, 1);
}
static int __exit cx20442_platform_remove(struct platform_device *pdev)
{
snd_soc_unregister_codec(&pdev->dev);
return 0;
}
static struct platform_driver cx20442_platform_driver = {
.driver = {
.name = "cx20442-codec",
.owner = THIS_MODULE,
},
.probe = cx20442_platform_probe,
.remove = __exit_p(cx20442_platform_remove),
};
module_platform_driver(cx20442_platform_driver);
MODULE_DESCRIPTION("ASoC CX20442-11 voice modem codec driver");
MODULE_AUTHOR("Janusz Krzysztofik");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:cx20442-codec");

View file

@ -0,0 +1,18 @@
/*
* cx20442.h -- audio driver for CX20442
*
* Copyright 2009 Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#ifndef _CX20442_CODEC_H
#define _CX20442_CODEC_H
extern struct tty_ldisc_ops v253_ops;
#endif

1384
sound/soc/codecs/da7210.c Normal file

File diff suppressed because it is too large Load diff

1600
sound/soc/codecs/da7213.c Normal file

File diff suppressed because it is too large Load diff

523
sound/soc/codecs/da7213.h Normal file
View file

@ -0,0 +1,523 @@
/*
* da7213.h - DA7213 ASoC Codec Driver
*
* Copyright (c) 2013 Dialog Semiconductor
*
* Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef _DA7213_H
#define _DA7213_H
#include <linux/regmap.h>
#include <sound/da7213.h>
/*
* Registers
*/
/* Status Registers */
#define DA7213_STATUS1 0x02
#define DA7213_PLL_STATUS 0x03
#define DA7213_AUX_L_GAIN_STATUS 0x04
#define DA7213_AUX_R_GAIN_STATUS 0x05
#define DA7213_MIC_1_GAIN_STATUS 0x06
#define DA7213_MIC_2_GAIN_STATUS 0x07
#define DA7213_MIXIN_L_GAIN_STATUS 0x08
#define DA7213_MIXIN_R_GAIN_STATUS 0x09
#define DA7213_ADC_L_GAIN_STATUS 0x0A
#define DA7213_ADC_R_GAIN_STATUS 0x0B
#define DA7213_DAC_L_GAIN_STATUS 0x0C
#define DA7213_DAC_R_GAIN_STATUS 0x0D
#define DA7213_HP_L_GAIN_STATUS 0x0E
#define DA7213_HP_R_GAIN_STATUS 0x0F
#define DA7213_LINE_GAIN_STATUS 0x10
/* System Initialisation Registers */
#define DA7213_DIG_ROUTING_DAI 0x21
#define DA7213_SR 0x22
#define DA7213_REFERENCES 0x23
#define DA7213_PLL_FRAC_TOP 0x24
#define DA7213_PLL_FRAC_BOT 0x25
#define DA7213_PLL_INTEGER 0x26
#define DA7213_PLL_CTRL 0x27
#define DA7213_DAI_CLK_MODE 0x28
#define DA7213_DAI_CTRL 0x29
#define DA7213_DIG_ROUTING_DAC 0x2A
#define DA7213_ALC_CTRL1 0x2B
/* Input - Gain, Select and Filter Registers */
#define DA7213_AUX_L_GAIN 0x30
#define DA7213_AUX_R_GAIN 0x31
#define DA7213_MIXIN_L_SELECT 0x32
#define DA7213_MIXIN_R_SELECT 0x33
#define DA7213_MIXIN_L_GAIN 0x34
#define DA7213_MIXIN_R_GAIN 0x35
#define DA7213_ADC_L_GAIN 0x36
#define DA7213_ADC_R_GAIN 0x37
#define DA7213_ADC_FILTERS1 0x38
#define DA7213_MIC_1_GAIN 0x39
#define DA7213_MIC_2_GAIN 0x3A
/* Output - Gain, Select and Filter Registers */
#define DA7213_DAC_FILTERS5 0x40
#define DA7213_DAC_FILTERS2 0x41
#define DA7213_DAC_FILTERS3 0x42
#define DA7213_DAC_FILTERS4 0x43
#define DA7213_DAC_FILTERS1 0x44
#define DA7213_DAC_L_GAIN 0x45
#define DA7213_DAC_R_GAIN 0x46
#define DA7213_CP_CTRL 0x47
#define DA7213_HP_L_GAIN 0x48
#define DA7213_HP_R_GAIN 0x49
#define DA7213_LINE_GAIN 0x4A
#define DA7213_MIXOUT_L_SELECT 0x4B
#define DA7213_MIXOUT_R_SELECT 0x4C
/* System Controller Registers */
#define DA7213_SYSTEM_MODES_INPUT 0x50
#define DA7213_SYSTEM_MODES_OUTPUT 0x51
/* Control Registers */
#define DA7213_AUX_L_CTRL 0x60
#define DA7213_AUX_R_CTRL 0x61
#define DA7213_MICBIAS_CTRL 0x62
#define DA7213_MIC_1_CTRL 0x63
#define DA7213_MIC_2_CTRL 0x64
#define DA7213_MIXIN_L_CTRL 0x65
#define DA7213_MIXIN_R_CTRL 0x66
#define DA7213_ADC_L_CTRL 0x67
#define DA7213_ADC_R_CTRL 0x68
#define DA7213_DAC_L_CTRL 0x69
#define DA7213_DAC_R_CTRL 0x6A
#define DA7213_HP_L_CTRL 0x6B
#define DA7213_HP_R_CTRL 0x6C
#define DA7213_LINE_CTRL 0x6D
#define DA7213_MIXOUT_L_CTRL 0x6E
#define DA7213_MIXOUT_R_CTRL 0x6F
/* Configuration Registers */
#define DA7213_LDO_CTRL 0x90
#define DA7213_IO_CTRL 0x91
#define DA7213_GAIN_RAMP_CTRL 0x92
#define DA7213_MIC_CONFIG 0x93
#define DA7213_PC_COUNT 0x94
#define DA7213_CP_VOL_THRESHOLD1 0x95
#define DA7213_CP_DELAY 0x96
#define DA7213_CP_DETECTOR 0x97
#define DA7213_DAI_OFFSET 0x98
#define DA7213_DIG_CTRL 0x99
#define DA7213_ALC_CTRL2 0x9A
#define DA7213_ALC_CTRL3 0x9B
#define DA7213_ALC_NOISE 0x9C
#define DA7213_ALC_TARGET_MIN 0x9D
#define DA7213_ALC_TARGET_MAX 0x9E
#define DA7213_ALC_GAIN_LIMITS 0x9F
#define DA7213_ALC_ANA_GAIN_LIMITS 0xA0
#define DA7213_ALC_ANTICLIP_CTRL 0xA1
#define DA7213_ALC_ANTICLIP_LEVEL 0xA2
#define DA7213_ALC_OFFSET_AUTO_M_L 0xA3
#define DA7213_ALC_OFFSET_AUTO_U_L 0xA4
#define DA7213_ALC_OFFSET_MAN_M_L 0xA6
#define DA7213_ALC_OFFSET_MAN_U_L 0xA7
#define DA7213_ALC_OFFSET_AUTO_M_R 0xA8
#define DA7213_ALC_OFFSET_AUTO_U_R 0xA9
#define DA7213_ALC_OFFSET_MAN_M_R 0xAB
#define DA7213_ALC_OFFSET_MAN_U_R 0xAC
#define DA7213_ALC_CIC_OP_LVL_CTRL 0xAD
#define DA7213_ALC_CIC_OP_LVL_DATA 0xAE
#define DA7213_DAC_NG_SETUP_TIME 0xAF
#define DA7213_DAC_NG_OFF_THRESHOLD 0xB0
#define DA7213_DAC_NG_ON_THRESHOLD 0xB1
#define DA7213_DAC_NG_CTRL 0xB2
/*
* Bit fields
*/
/* DA7213_SR = 0x22 */
#define DA7213_SR_8000 (0x1 << 0)
#define DA7213_SR_11025 (0x2 << 0)
#define DA7213_SR_12000 (0x3 << 0)
#define DA7213_SR_16000 (0x5 << 0)
#define DA7213_SR_22050 (0x6 << 0)
#define DA7213_SR_24000 (0x7 << 0)
#define DA7213_SR_32000 (0x9 << 0)
#define DA7213_SR_44100 (0xA << 0)
#define DA7213_SR_48000 (0xB << 0)
#define DA7213_SR_88200 (0xE << 0)
#define DA7213_SR_96000 (0xF << 0)
/* DA7213_REFERENCES = 0x23 */
#define DA7213_BIAS_EN (0x1 << 3)
#define DA7213_VMID_EN (0x1 << 7)
/* DA7213_PLL_CTRL = 0x27 */
#define DA7213_PLL_INDIV_5_10_MHZ (0x0 << 2)
#define DA7213_PLL_INDIV_10_20_MHZ (0x1 << 2)
#define DA7213_PLL_INDIV_20_40_MHZ (0x2 << 2)
#define DA7213_PLL_INDIV_40_54_MHZ (0x3 << 2)
#define DA7213_PLL_INDIV_MASK (0x3 << 2)
#define DA7213_PLL_MCLK_SQR_EN (0x1 << 4)
#define DA7213_PLL_32K_MODE (0x1 << 5)
#define DA7213_PLL_SRM_EN (0x1 << 6)
#define DA7213_PLL_EN (0x1 << 7)
/* DA7213_DAI_CLK_MODE = 0x28 */
#define DA7213_DAI_BCLKS_PER_WCLK_32 (0x0 << 0)
#define DA7213_DAI_BCLKS_PER_WCLK_64 (0x1 << 0)
#define DA7213_DAI_BCLKS_PER_WCLK_128 (0x2 << 0)
#define DA7213_DAI_BCLKS_PER_WCLK_256 (0x3 << 0)
#define DA7213_DAI_BCLKS_PER_WCLK_MASK (0x3 << 0)
#define DA7213_DAI_CLK_POL_INV (0x1 << 2)
#define DA7213_DAI_WCLK_POL_INV (0x1 << 3)
#define DA7213_DAI_CLK_EN_SLAVE_MODE (0x0 << 7)
#define DA7213_DAI_CLK_EN_MASTER_MODE (0x1 << 7)
#define DA7213_DAI_CLK_EN_MASK (0x1 << 7)
/* DA7213_DAI_CTRL = 0x29 */
#define DA7213_DAI_FORMAT_I2S_MODE (0x0 << 0)
#define DA7213_DAI_FORMAT_LEFT_J (0x1 << 0)
#define DA7213_DAI_FORMAT_RIGHT_J (0x2 << 0)
#define DA7213_DAI_FORMAT_MASK (0x3 << 0)
#define DA7213_DAI_WORD_LENGTH_S16_LE (0x0 << 2)
#define DA7213_DAI_WORD_LENGTH_S20_LE (0x1 << 2)
#define DA7213_DAI_WORD_LENGTH_S24_LE (0x2 << 2)
#define DA7213_DAI_WORD_LENGTH_S32_LE (0x3 << 2)
#define DA7213_DAI_WORD_LENGTH_MASK (0x3 << 2)
#define DA7213_DAI_EN_SHIFT 7
/* DA7213_DIG_ROUTING_DAI = 0x21 */
#define DA7213_DAI_L_SRC_SHIFT 0
#define DA7213_DAI_R_SRC_SHIFT 4
#define DA7213_DAI_SRC_MAX 4
/* DA7213_DIG_ROUTING_DAC = 0x2A */
#define DA7213_DAC_L_SRC_SHIFT 0
#define DA7213_DAC_L_MONO_SHIFT 3
#define DA7213_DAC_R_SRC_SHIFT 4
#define DA7213_DAC_R_MONO_SHIFT 7
#define DA7213_DAC_SRC_MAX 4
#define DA7213_DAC_MONO_MAX 0x1
/* DA7213_ALC_CTRL1 = 0x2B */
#define DA7213_ALC_OFFSET_EN_SHIFT 0
#define DA7213_ALC_OFFSET_EN_MAX 0x1
#define DA7213_ALC_OFFSET_EN (0x1 << 0)
#define DA7213_ALC_SYNC_MODE (0x1 << 1)
#define DA7213_ALC_CALIB_MODE_MAN (0x1 << 2)
#define DA7213_ALC_L_EN_SHIFT 3
#define DA7213_ALC_AUTO_CALIB_EN (0x1 << 4)
#define DA7213_ALC_CALIB_OVERFLOW (0x1 << 5)
#define DA7213_ALC_R_EN_SHIFT 7
#define DA7213_ALC_EN_MAX 0x1
/* DA7213_AUX_L/R_GAIN = 0x30/0x31 */
#define DA7213_AUX_AMP_GAIN_SHIFT 0
#define DA7213_AUX_AMP_GAIN_MAX 0x3F
/* DA7213_MIXIN_L/R_SELECT = 0x32/0x33 */
#define DA7213_DMIC_EN_SHIFT 7
#define DA7213_DMIC_EN_MAX 0x1
/* DA7213_MIXIN_L_SELECT = 0x32 */
#define DA7213_MIXIN_L_MIX_SELECT_AUX_L_SHIFT 0
#define DA7213_MIXIN_L_MIX_SELECT_MIC_1_SHIFT 1
#define DA7213_MIXIN_L_MIX_SELECT_MIC_1 (0x1 << 1)
#define DA7213_MIXIN_L_MIX_SELECT_MIC_2_SHIFT 2
#define DA7213_MIXIN_L_MIX_SELECT_MIC_2 (0x1 << 2)
#define DA7213_MIXIN_L_MIX_SELECT_MIXIN_R_SHIFT 3
#define DA7213_MIXIN_L_MIX_SELECT_MAX 0x1
/* DA7213_MIXIN_R_SELECT = 0x33 */
#define DA7213_MIXIN_R_MIX_SELECT_AUX_R_SHIFT 0
#define DA7213_MIXIN_R_MIX_SELECT_MIC_2_SHIFT 1
#define DA7213_MIXIN_R_MIX_SELECT_MIC_2 (0x1 << 1)
#define DA7213_MIXIN_R_MIX_SELECT_MIC_1_SHIFT 2
#define DA7213_MIXIN_R_MIX_SELECT_MIC_1 (0x1 << 2)
#define DA7213_MIXIN_R_MIX_SELECT_MIXIN_L_SHIFT 3
#define DA7213_MIXIN_R_MIX_SELECT_MAX 0x1
#define DA7213_MIC_BIAS_OUTPUT_SELECT_2 (0x1 << 6)
/* DA7213_MIXIN_L/R_GAIN = 0x34/0x35 */
#define DA7213_MIXIN_AMP_GAIN_SHIFT 0
#define DA7213_MIXIN_AMP_GAIN_MAX 0xF
/* DA7213_ADC_L/R_GAIN = 0x36/0x37 */
#define DA7213_ADC_AMP_GAIN_SHIFT 0
#define DA7213_ADC_AMP_GAIN_MAX 0x7F
/* DA7213_ADC/DAC_FILTERS1 = 0x38/0x44 */
#define DA7213_VOICE_HPF_CORNER_SHIFT 0
#define DA7213_VOICE_HPF_CORNER_MAX 8
#define DA7213_VOICE_EN_SHIFT 3
#define DA7213_VOICE_EN_MAX 0x1
#define DA7213_AUDIO_HPF_CORNER_SHIFT 4
#define DA7213_AUDIO_HPF_CORNER_MAX 4
#define DA7213_HPF_EN_SHIFT 7
#define DA7213_HPF_EN_MAX 0x1
/* DA7213_MIC_1/2_GAIN = 0x39/0x3A */
#define DA7213_MIC_AMP_GAIN_SHIFT 0
#define DA7213_MIC_AMP_GAIN_MAX 0x7
/* DA7213_DAC_FILTERS5 = 0x40 */
#define DA7213_DAC_SOFTMUTE_EN_SHIFT 7
#define DA7213_DAC_SOFTMUTE_EN_MAX 0x1
#define DA7213_DAC_SOFTMUTE_RATE_SHIFT 4
#define DA7213_DAC_SOFTMUTE_RATE_MAX 7
/* DA7213_DAC_FILTERS2/3/4 = 0x41/0x42/0x43 */
#define DA7213_DAC_EQ_BAND_MAX 0xF
/* DA7213_DAC_FILTERS2 = 0x41 */
#define DA7213_DAC_EQ_BAND1_SHIFT 0
#define DA7213_DAC_EQ_BAND2_SHIFT 4
/* DA7213_DAC_FILTERS2 = 0x42 */
#define DA7213_DAC_EQ_BAND3_SHIFT 0
#define DA7213_DAC_EQ_BAND4_SHIFT 4
/* DA7213_DAC_FILTERS4 = 0x43 */
#define DA7213_DAC_EQ_BAND5_SHIFT 0
#define DA7213_DAC_EQ_EN_SHIFT 7
#define DA7213_DAC_EQ_EN_MAX 0x1
/* DA7213_DAC_L/R_GAIN = 0x45/0x46 */
#define DA7213_DAC_AMP_GAIN_SHIFT 0
#define DA7213_DAC_AMP_GAIN_MAX 0x7F
/* DA7213_HP_L/R_GAIN = 0x45/0x46 */
#define DA7213_HP_AMP_GAIN_SHIFT 0
#define DA7213_HP_AMP_GAIN_MAX 0x3F
/* DA7213_CP_CTRL = 0x47 */
#define DA7213_CP_EN_SHIFT 7
/* DA7213_LINE_GAIN = 0x4A */
#define DA7213_LINE_AMP_GAIN_SHIFT 0
#define DA7213_LINE_AMP_GAIN_MAX 0x3F
/* DA7213_MIXOUT_L_SELECT = 0x4B */
#define DA7213_MIXOUT_L_MIX_SELECT_AUX_L_SHIFT 0
#define DA7213_MIXOUT_L_MIX_SELECT_MIXIN_L_SHIFT 1
#define DA7213_MIXOUT_L_MIX_SELECT_MIXIN_R_SHIFT 2
#define DA7213_MIXOUT_L_MIX_SELECT_DAC_L_SHIFT 3
#define DA7213_MIXOUT_L_MIX_SELECT_AUX_L_INVERTED_SHIFT 4
#define DA7213_MIXOUT_L_MIX_SELECT_MIXIN_L_INVERTED_SHIFT 5
#define DA7213_MIXOUT_L_MIX_SELECT_MIXIN_R_INVERTED_SHIFT 6
#define DA7213_MIXOUT_L_MIX_SELECT_MAX 0x1
/* DA7213_MIXOUT_R_SELECT = 0x4C */
#define DA7213_MIXOUT_R_MIX_SELECT_AUX_R_SHIFT 0
#define DA7213_MIXOUT_R_MIX_SELECT_MIXIN_R_SHIFT 1
#define DA7213_MIXOUT_R_MIX_SELECT_MIXIN_L_SHIFT 2
#define DA7213_MIXOUT_R_MIX_SELECT_DAC_R_SHIFT 3
#define DA7213_MIXOUT_R_MIX_SELECT_AUX_R_INVERTED_SHIFT 4
#define DA7213_MIXOUT_R_MIX_SELECT_MIXIN_R_INVERTED_SHIFT 5
#define DA7213_MIXOUT_R_MIX_SELECT_MIXIN_L_INVERTED_SHIFT 6
#define DA7213_MIXOUT_R_MIX_SELECT_MAX 0x1
/*
* DA7213_AUX_L/R_CTRL = 0x60/0x61,
* DA7213_MIC_1/2_CTRL = 0x63/0x64,
* DA7213_MIXIN_L/R_CTRL = 0x65/0x66,
* DA7213_ADC_L/R_CTRL = 0x65/0x66,
* DA7213_DAC_L/R_CTRL = 0x69/0x6A,
* DA7213_HP_L/R_CTRL = 0x6B/0x6C,
* DA7213_LINE_CTRL = 0x6D
*/
#define DA7213_MUTE_EN_SHIFT 6
#define DA7213_MUTE_EN_MAX 0x1
#define DA7213_MUTE_EN (0x1 << 6)
/*
* DA7213_AUX_L/R_CTRL = 0x60/0x61,
* DA7213_MIXIN_L/R_CTRL = 0x65/0x66,
* DA7213_ADC_L/R_CTRL = 0x65/0x66,
* DA7213_DAC_L/R_CTRL = 0x69/0x6A,
* DA7213_HP_L/R_CTRL = 0x6B/0x6C,
* DA7213_LINE_CTRL = 0x6D
*/
#define DA7213_GAIN_RAMP_EN_SHIFT 5
#define DA7213_GAIN_RAMP_EN_MAX 0x1
#define DA7213_GAIN_RAMP_EN (0x1 << 5)
/*
* DA7213_AUX_L/R_CTRL = 0x60/0x61,
* DA7213_MIXIN_L/R_CTRL = 0x65/0x66,
* DA7213_HP_L/R_CTRL = 0x6B/0x6C,
* DA7213_LINE_CTRL = 0x6D
*/
#define DA7213_ZC_EN_SHIFT 4
#define DA7213_ZC_EN_MAX 0x1
/*
* DA7213_AUX_L/R_CTRL = 0x60/0x61,
* DA7213_MIC_1/2_CTRL = 0x63/0x64,
* DA7213_MIXIN_L/R_CTRL = 0x65/0x66,
* DA7213_HP_L/R_CTRL = 0x6B/0x6C,
* DA7213_MIXOUT_L/R_CTRL = 0x6E/0x6F,
* DA7213_LINE_CTRL = 0x6D
*/
#define DA7213_AMP_EN_SHIFT 7
/* DA7213_MIC_1/2_CTRL = 0x63/0x64 */
#define DA7213_MIC_AMP_IN_SEL_SHIFT 2
#define DA7213_MIC_AMP_IN_SEL_MAX 3
/* DA7213_MICBIAS_CTRL = 0x62 */
#define DA7213_MICBIAS1_LEVEL_SHIFT 0
#define DA7213_MICBIAS1_LEVEL_MASK (0x3 << 0)
#define DA7213_MICBIAS1_EN_SHIFT 3
#define DA7213_MICBIAS2_LEVEL_SHIFT 4
#define DA7213_MICBIAS2_LEVEL_MASK (0x3 << 4)
#define DA7213_MICBIAS2_EN_SHIFT 7
/* DA7213_MIXIN_L/R_CTRL = 0x65/0x66 */
#define DA7213_MIXIN_MIX_EN (0x1 << 3)
/* DA7213_ADC_L/R_CTRL = 0x67/0x68 */
#define DA7213_ADC_EN_SHIFT 7
#define DA7213_ADC_EN (0x1 << 7)
/* DA7213_DAC_L/R_CTRL = 0x69/0x6A*/
#define DA7213_DAC_EN_SHIFT 7
/* DA7213_HP_L/R_CTRL = 0x6B/0x6C */
#define DA7213_HP_AMP_OE (0x1 << 3)
/* DA7213_LINE_CTRL = 0x6D */
#define DA7213_LINE_AMP_OE (0x1 << 3)
/* DA7213_MIXOUT_L/R_CTRL = 0x6E/0x6F */
#define DA7213_MIXOUT_MIX_EN (0x1 << 3)
/* DA7213_GAIN_RAMP_CTRL = 0x92 */
#define DA7213_GAIN_RAMP_RATE_SHIFT 0
#define DA7213_GAIN_RAMP_RATE_MAX 4
/* DA7213_MIC_CONFIG = 0x93 */
#define DA7213_DMIC_DATA_SEL_SHIFT 0
#define DA7213_DMIC_DATA_SEL_MASK (0x1 << 0)
#define DA7213_DMIC_SAMPLEPHASE_SHIFT 1
#define DA7213_DMIC_SAMPLEPHASE_MASK (0x1 << 1)
#define DA7213_DMIC_CLK_RATE_SHIFT 2
#define DA7213_DMIC_CLK_RATE_MASK (0x1 << 2)
/* DA7213_DIG_CTRL = 0x99 */
#define DA7213_DAC_L_INV_SHIFT 3
#define DA7213_DAC_R_INV_SHIFT 7
#define DA7213_DAC_INV_MAX 0x1
/* DA7213_ALC_CTRL2 = 0x9A */
#define DA7213_ALC_ATTACK_SHIFT 0
#define DA7213_ALC_ATTACK_MAX 13
#define DA7213_ALC_RELEASE_SHIFT 4
#define DA7213_ALC_RELEASE_MAX 11
/* DA7213_ALC_CTRL3 = 0x9B */
#define DA7213_ALC_HOLD_SHIFT 0
#define DA7213_ALC_HOLD_MAX 16
#define DA7213_ALC_INTEG_ATTACK_SHIFT 4
#define DA7213_ALC_INTEG_RELEASE_SHIFT 6
#define DA7213_ALC_INTEG_MAX 4
/*
* DA7213_ALC_NOISE = 0x9C,
* DA7213_ALC_TARGET_MIN/MAX = 0x9D/0x9E
*/
#define DA7213_ALC_THRESHOLD_SHIFT 0
#define DA7213_ALC_THRESHOLD_MAX 0x3F
/* DA7213_ALC_GAIN_LIMITS = 0x9F */
#define DA7213_ALC_ATTEN_MAX_SHIFT 0
#define DA7213_ALC_GAIN_MAX_SHIFT 4
#define DA7213_ALC_ATTEN_GAIN_MAX_MAX 0xF
/* DA7213_ALC_ANA_GAIN_LIMITS = 0xA0 */
#define DA7213_ALC_ANA_GAIN_MIN_SHIFT 0
#define DA7213_ALC_ANA_GAIN_MAX_SHIFT 4
#define DA7213_ALC_ANA_GAIN_MAX 0x7
/* DA7213_ALC_ANTICLIP_CTRL = 0xA1 */
#define DA7213_ALC_ANTICLIP_EN_SHIFT 7
#define DA7213_ALC_ANTICLIP_EN_MAX 0x1
/* DA7213_ALC_ANTICLIP_LEVEL = 0xA2 */
#define DA7213_ALC_ANTICLIP_LEVEL_SHIFT 0
#define DA7213_ALC_ANTICLIP_LEVEL_MAX 0x7F
/* DA7213_ALC_CIC_OP_LVL_CTRL = 0xAD */
#define DA7213_ALC_DATA_MIDDLE (0x2 << 0)
#define DA7213_ALC_DATA_TOP (0x3 << 0)
#define DA7213_ALC_CIC_OP_CHANNEL_LEFT (0x0 << 7)
#define DA7213_ALC_CIC_OP_CHANNEL_RIGHT (0x1 << 7)
/* DA7213_DAC_NG_SETUP_TIME = 0xAF */
#define DA7213_DAC_NG_SETUP_TIME_SHIFT 0
#define DA7213_DAC_NG_SETUP_TIME_MAX 4
#define DA7213_DAC_NG_RAMPUP_RATE_SHIFT 2
#define DA7213_DAC_NG_RAMPDN_RATE_SHIFT 3
#define DA7213_DAC_NG_RAMP_RATE_MAX 2
/* DA7213_DAC_NG_OFF/ON_THRESH = 0xB0/0xB1 */
#define DA7213_DAC_NG_THRESHOLD_SHIFT 0
#define DA7213_DAC_NG_THRESHOLD_MAX 0x7
/* DA7213_DAC_NG_CTRL = 0xB2 */
#define DA7213_DAC_NG_EN_SHIFT 7
#define DA7213_DAC_NG_EN_MAX 0x1
/*
* General defines
*/
/* Register inversion */
#define DA7213_NO_INVERT 0
#define DA7213_INVERT 1
/* Byte related defines */
#define DA7213_BYTE_SHIFT 8
#define DA7213_BYTE_MASK 0xFF
/* ALC related */
#define DA7213_ALC_OFFSET_15_8 0x00FF00
#define DA7213_ALC_OFFSET_19_16 0x0F0000
#define DA7213_ALC_AVG_ITERATIONS 5
/* PLL related */
#define DA7213_SYSCLK_MCLK 0
#define DA7213_SYSCLK_PLL 1
#define DA7213_PLL_FREQ_OUT_90316800 90316800
#define DA7213_PLL_FREQ_OUT_98304000 98304000
#define DA7213_PLL_FREQ_OUT_94310400 94310400
#define DA7213_PLL_INDIV_5_10_MHZ_VAL 2
#define DA7213_PLL_INDIV_10_20_MHZ_VAL 4
#define DA7213_PLL_INDIV_20_40_MHZ_VAL 8
#define DA7213_PLL_INDIV_40_54_MHZ_VAL 16
enum clk_src {
DA7213_CLKSRC_MCLK
};
/* Codec private data */
struct da7213_priv {
struct regmap *regmap;
unsigned int mclk_rate;
bool master;
bool mclk_squarer_en;
bool srm_en;
bool alc_calib_auto;
bool alc_en;
struct da7213_platform_data *pdata;
};
#endif /* _DA7213_H */

1589
sound/soc/codecs/da732x.c Normal file

File diff suppressed because it is too large Load diff

130
sound/soc/codecs/da732x.h Normal file
View file

@ -0,0 +1,130 @@
/*
* da732x.h -- Dialog DA732X ALSA SoC Audio Driver Header File
*
* Copyright (C) 2012 Dialog Semiconductor GmbH
*
* Author: Michal Hajduk <Michal.Hajduk@diasemi.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef __DA732X_H_
#define __DA732X_H_
#include <sound/soc.h>
/* General */
#define DA732X_U8_MASK 0xFF
#define DA732X_4BYTES 4
#define DA732X_3BYTES 3
#define DA732X_2BYTES 2
#define DA732X_1BYTE 1
#define DA732X_1BYTE_SHIFT 8
#define DA732X_2BYTES_SHIFT 16
#define DA732X_3BYTES_SHIFT 24
#define DA732X_4BYTES_SHIFT 32
#define DA732X_DACS_DIS 0x0
#define DA732X_HP_DIS 0x0
#define DA732X_CLEAR_REG 0x0
/* Calibration */
#define DA732X_DAC_OFFSET_STEP 0x20
#define DA732X_OUTPUT_OFFSET_STEP 0x80
#define DA732X_HP_OUT_TRIM_VAL 0x0
#define DA732X_WAIT_FOR_STABILIZATION 1
#define DA732X_HPL_DAC 0
#define DA732X_HPR_DAC 1
#define DA732X_HP_DACS 2
#define DA732X_HPL_AMP 0
#define DA732X_HPR_AMP 1
#define DA732X_HP_AMPS 2
/* Clock settings */
#define DA732X_STARTUP_DELAY 100
#define DA732X_PLL_OUT_196608 196608000
#define DA732X_PLL_OUT_180634 180633600
#define DA732X_PLL_OUT_SRM 188620800
#define DA732X_MCLK_10MHZ 10000000
#define DA732X_MCLK_20MHZ 20000000
#define DA732X_MCLK_40MHZ 40000000
#define DA732X_MCLK_54MHZ 54000000
#define DA732X_MCLK_RET_0_10MHZ 0
#define DA732X_MCLK_VAL_0_10MHZ 1
#define DA732X_MCLK_RET_10_20MHZ 1
#define DA732X_MCLK_VAL_10_20MHZ 2
#define DA732X_MCLK_RET_20_40MHZ 2
#define DA732X_MCLK_VAL_20_40MHZ 4
#define DA732X_MCLK_RET_40_54MHZ 3
#define DA732X_MCLK_VAL_40_54MHZ 8
#define DA732X_DAI_ID1 0
#define DA732X_DAI_ID2 1
#define DA732X_SRCCLK_PLL 0
#define DA732X_SRCCLK_MCLK 1
#define DA732X_LIN_LP_VOL 0x4F
#define DA732X_LP_VOL 0x40
/* Kcontrols */
#define DA732X_DAC_EN_MAX 2
#define DA732X_ADCL_MUX_MAX 2
#define DA732X_ADCR_MUX_MAX 3
#define DA732X_HPF_MODE_MAX 3
#define DA732X_HPF_MODE_SHIFT 4
#define DA732X_HPF_MUSIC_SHIFT 0
#define DA732X_HPF_MUSIC_MAX 4
#define DA732X_HPF_VOICE_SHIFT 4
#define DA732X_HPF_VOICE_MAX 8
#define DA732X_EQ_EN_MAX 1
#define DA732X_HPF_VOICE 1
#define DA732X_HPF_MUSIC 2
#define DA732X_HPF_DISABLED 0
#define DA732X_NO_INVERT 0
#define DA732X_INVERT 1
#define DA732X_SWITCH_MAX 1
#define DA732X_ENABLE_CP 1
#define DA732X_DISABLE_CP 0
#define DA732X_DISABLE_ALL_CLKS 0
#define DA732X_RESET_ADCS 0
/* dB values */
#define DA732X_MIC_VOL_DB_MIN 0
#define DA732X_MIC_VOL_DB_INC 50
#define DA732X_MIC_PRE_VOL_DB_MIN 0
#define DA732X_MIC_PRE_VOL_DB_INC 600
#define DA732X_AUX_VOL_DB_MIN -6000
#define DA732X_AUX_VOL_DB_INC 150
#define DA732X_HP_VOL_DB_MIN -2250
#define DA732X_HP_VOL_DB_INC 150
#define DA732X_LIN2_VOL_DB_MIN -1650
#define DA732X_LIN2_VOL_DB_INC 150
#define DA732X_LIN3_VOL_DB_MIN -1650
#define DA732X_LIN3_VOL_DB_INC 150
#define DA732X_LIN4_VOL_DB_MIN -2250
#define DA732X_LIN4_VOL_DB_INC 150
#define DA732X_EQ_BAND_VOL_DB_MIN -1050
#define DA732X_EQ_BAND_VOL_DB_INC 150
#define DA732X_DAC_VOL_DB_MIN -7725
#define DA732X_DAC_VOL_DB_INC 75
#define DA732X_ADC_VOL_DB_MIN 0
#define DA732X_ADC_VOL_DB_INC -1
#define DA732X_EQ_OVERALL_VOL_DB_MIN -1800
#define DA732X_EQ_OVERALL_VOL_DB_INC 600
enum da732x_sysctl {
DA732X_SR_8KHZ = 0x1,
DA732X_SR_11_025KHZ = 0x2,
DA732X_SR_12KHZ = 0x3,
DA732X_SR_16KHZ = 0x5,
DA732X_SR_22_05KHZ = 0x6,
DA732X_SR_24KHZ = 0x7,
DA732X_SR_32KHZ = 0x9,
DA732X_SR_44_1KHZ = 0xA,
DA732X_SR_48KHZ = 0xB,
DA732X_SR_88_1KHZ = 0xE,
DA732X_SR_96KHZ = 0xF,
};
#endif /* __DA732X_H_ */

View file

@ -0,0 +1,654 @@
/*
* da732x_reg.h --- Dialog DA732X ALSA SoC Audio Registers Header File
*
* Copyright (C) 2012 Dialog Semiconductor GmbH
*
* Author: Michal Hajduk <Michal.Hajduk@diasemi.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef __DA732X_REG_H_
#define __DA732X_REG_H_
/* DA732X registers */
#define DA732X_REG_STATUS_EXT 0x00
#define DA732X_REG_STATUS 0x01
#define DA732X_REG_REF1 0x02
#define DA732X_REG_BIAS_EN 0x03
#define DA732X_REG_BIAS1 0x04
#define DA732X_REG_BIAS2 0x05
#define DA732X_REG_BIAS3 0x06
#define DA732X_REG_BIAS4 0x07
#define DA732X_REG_MICBIAS2 0x0F
#define DA732X_REG_MICBIAS1 0x10
#define DA732X_REG_MICDET 0x11
#define DA732X_REG_MIC1_PRE 0x12
#define DA732X_REG_MIC1 0x13
#define DA732X_REG_MIC2_PRE 0x14
#define DA732X_REG_MIC2 0x15
#define DA732X_REG_AUX1L 0x16
#define DA732X_REG_AUX1R 0x17
#define DA732X_REG_MIC3_PRE 0x18
#define DA732X_REG_MIC3 0x19
#define DA732X_REG_INP_PINBIAS 0x1A
#define DA732X_REG_INP_ZC_EN 0x1B
#define DA732X_REG_INP_MUX 0x1D
#define DA732X_REG_HP_DET 0x20
#define DA732X_REG_HPL_DAC_OFFSET 0x21
#define DA732X_REG_HPL_DAC_OFF_CNTL 0x22
#define DA732X_REG_HPL_OUT_OFFSET 0x23
#define DA732X_REG_HPL 0x24
#define DA732X_REG_HPL_VOL 0x25
#define DA732X_REG_HPR_DAC_OFFSET 0x26
#define DA732X_REG_HPR_DAC_OFF_CNTL 0x27
#define DA732X_REG_HPR_OUT_OFFSET 0x28
#define DA732X_REG_HPR 0x29
#define DA732X_REG_HPR_VOL 0x2A
#define DA732X_REG_LIN2 0x2B
#define DA732X_REG_LIN3 0x2C
#define DA732X_REG_LIN4 0x2D
#define DA732X_REG_OUT_ZC_EN 0x2E
#define DA732X_REG_HP_LIN1_GNDSEL 0x37
#define DA732X_REG_CP_HP1 0x3A
#define DA732X_REG_CP_HP2 0x3B
#define DA732X_REG_CP_CTRL1 0x40
#define DA732X_REG_CP_CTRL2 0x41
#define DA732X_REG_CP_CTRL3 0x42
#define DA732X_REG_CP_LEVEL_MASK 0x43
#define DA732X_REG_CP_DET 0x44
#define DA732X_REG_CP_STATUS 0x45
#define DA732X_REG_CP_THRESH1 0x46
#define DA732X_REG_CP_THRESH2 0x47
#define DA732X_REG_CP_THRESH3 0x48
#define DA732X_REG_CP_THRESH4 0x49
#define DA732X_REG_CP_THRESH5 0x4A
#define DA732X_REG_CP_THRESH6 0x4B
#define DA732X_REG_CP_THRESH7 0x4C
#define DA732X_REG_CP_THRESH8 0x4D
#define DA732X_REG_PLL_DIV_LO 0x50
#define DA732X_REG_PLL_DIV_MID 0x51
#define DA732X_REG_PLL_DIV_HI 0x52
#define DA732X_REG_PLL_CTRL 0x53
#define DA732X_REG_CLK_CTRL 0x54
#define DA732X_REG_CLK_DSP 0x5A
#define DA732X_REG_CLK_EN1 0x5B
#define DA732X_REG_CLK_EN2 0x5C
#define DA732X_REG_CLK_EN3 0x5D
#define DA732X_REG_CLK_EN4 0x5E
#define DA732X_REG_CLK_EN5 0x5F
#define DA732X_REG_AIF_MCLK 0x60
#define DA732X_REG_AIFA1 0x61
#define DA732X_REG_AIFA2 0x62
#define DA732X_REG_AIFA3 0x63
#define DA732X_REG_AIFB1 0x64
#define DA732X_REG_AIFB2 0x65
#define DA732X_REG_AIFB3 0x66
#define DA732X_REG_PC_CTRL 0x6A
#define DA732X_REG_DATA_ROUTE 0x70
#define DA732X_REG_DSP_CTRL 0x71
#define DA732X_REG_CIF_CTRL2 0x74
#define DA732X_REG_HANDSHAKE 0x75
#define DA732X_REG_MBOX0 0x76
#define DA732X_REG_MBOX1 0x77
#define DA732X_REG_MBOX2 0x78
#define DA732X_REG_MBOX_STATUS 0x79
#define DA732X_REG_SPARE1_OUT 0x7D
#define DA732X_REG_SPARE2_OUT 0x7E
#define DA732X_REG_SPARE1_IN 0x7F
#define DA732X_REG_ID 0x81
#define DA732X_REG_ADC1_PD 0x90
#define DA732X_REG_ADC1_HPF 0x93
#define DA732X_REG_ADC1_SEL 0x94
#define DA732X_REG_ADC1_EQ12 0x95
#define DA732X_REG_ADC1_EQ34 0x96
#define DA732X_REG_ADC1_EQ5 0x97
#define DA732X_REG_ADC2_PD 0x98
#define DA732X_REG_ADC2_HPF 0x9B
#define DA732X_REG_ADC2_SEL 0x9C
#define DA732X_REG_ADC2_EQ12 0x9D
#define DA732X_REG_ADC2_EQ34 0x9E
#define DA732X_REG_ADC2_EQ5 0x9F
#define DA732X_REG_DAC1_HPF 0xA0
#define DA732X_REG_DAC1_L_VOL 0xA1
#define DA732X_REG_DAC1_R_VOL 0xA2
#define DA732X_REG_DAC1_SEL 0xA3
#define DA732X_REG_DAC1_SOFTMUTE 0xA4
#define DA732X_REG_DAC1_EQ12 0xA5
#define DA732X_REG_DAC1_EQ34 0xA6
#define DA732X_REG_DAC1_EQ5 0xA7
#define DA732X_REG_DAC2_HPF 0xB0
#define DA732X_REG_DAC2_L_VOL 0xB1
#define DA732X_REG_DAC2_R_VOL 0xB2
#define DA732X_REG_DAC2_SEL 0xB3
#define DA732X_REG_DAC2_SOFTMUTE 0xB4
#define DA732X_REG_DAC2_EQ12 0xB5
#define DA732X_REG_DAC2_EQ34 0xB6
#define DA732X_REG_DAC2_EQ5 0xB7
#define DA732X_REG_DAC3_HPF 0xC0
#define DA732X_REG_DAC3_VOL 0xC1
#define DA732X_REG_DAC3_SEL 0xC3
#define DA732X_REG_DAC3_SOFTMUTE 0xC4
#define DA732X_REG_DAC3_EQ12 0xC5
#define DA732X_REG_DAC3_EQ34 0xC6
#define DA732X_REG_DAC3_EQ5 0xC7
#define DA732X_REG_BIQ_BYP 0xD2
#define DA732X_REG_DMA_CMD 0xD3
#define DA732X_REG_DMA_ADDR0 0xD4
#define DA732X_REG_DMA_ADDR1 0xD5
#define DA732X_REG_DMA_DATA0 0xD6
#define DA732X_REG_DMA_DATA1 0xD7
#define DA732X_REG_DMA_DATA2 0xD8
#define DA732X_REG_DMA_DATA3 0xD9
#define DA732X_REG_DMA_STATUS 0xDA
#define DA732X_REG_BROWNOUT 0xDF
#define DA732X_REG_UNLOCK 0xE0
#define DA732X_MAX_REG DA732X_REG_UNLOCK
/*
* Bits
*/
/* DA732X_REG_STATUS_EXT (addr=0x00) */
#define DA732X_STATUS_EXT_DSP (1 << 4)
#define DA732X_STATUS_EXT_CLEAR (0 << 0)
/* DA732X_REG_STATUS (addr=0x01) */
#define DA732X_STATUS_PLL_LOCK (1 << 0)
#define DA732X_STATUS_PLL_MCLK_DET (1 << 1)
#define DA732X_STATUS_HPDET_OUT (1 << 2)
#define DA732X_STATUS_INP_MIXDET_1 (1 << 3)
#define DA732X_STATUS_INP_MIXDET_2 (1 << 4)
#define DA732X_STATUS_BO_STATUS (1 << 5)
/* DA732X_REG_REF1 (addr=0x02) */
#define DA732X_VMID_FASTCHG (1 << 1)
#define DA732X_VMID_FASTDISCHG (1 << 2)
#define DA732X_REFBUFX2_EN (1 << 6)
#define DA732X_REFBUFX2_DIS (0 << 6)
/* DA732X_REG_BIAS_EN (addr=0x03) */
#define DA732X_BIAS_BOOST_MASK (3 << 0)
#define DA732X_BIAS_BOOST_100PC (0 << 0)
#define DA732X_BIAS_BOOST_133PC (1 << 0)
#define DA732X_BIAS_BOOST_88PC (2 << 0)
#define DA732X_BIAS_BOOST_50PC (3 << 0)
#define DA732X_BIAS_EN (1 << 7)
#define DA732X_BIAS_DIS (0 << 7)
/* DA732X_REG_BIAS1 (addr=0x04) */
#define DA732X_BIAS1_HP_DAC_BIAS_MASK (3 << 0)
#define DA732X_BIAS1_HP_DAC_BIAS_100PC (0 << 0)
#define DA732X_BIAS1_HP_DAC_BIAS_150PC (1 << 0)
#define DA732X_BIAS1_HP_DAC_BIAS_50PC (2 << 0)
#define DA732X_BIAS1_HP_DAC_BIAS_75PC (3 << 0)
#define DA732X_BIAS1_HP_OUT_BIAS_MASK (7 << 4)
#define DA732X_BIAS1_HP_OUT_BIAS_100PC (0 << 4)
#define DA732X_BIAS1_HP_OUT_BIAS_125PC (1 << 4)
#define DA732X_BIAS1_HP_OUT_BIAS_150PC (2 << 4)
#define DA732X_BIAS1_HP_OUT_BIAS_175PC (3 << 4)
#define DA732X_BIAS1_HP_OUT_BIAS_200PC (4 << 4)
#define DA732X_BIAS1_HP_OUT_BIAS_250PC (5 << 4)
#define DA732X_BIAS1_HP_OUT_BIAS_300PC (6 << 4)
#define DA732X_BIAS1_HP_OUT_BIAS_350PC (7 << 4)
/* DA732X_REG_BIAS2 (addr=0x05) */
#define DA732X_BIAS2_LINE2_DAC_BIAS_MASK (3 << 0)
#define DA732X_BIAS2_LINE2_DAC_BIAS_100PC (0 << 0)
#define DA732X_BIAS2_LINE2_DAC_BIAS_150PC (1 << 0)
#define DA732X_BIAS2_LINE2_DAC_BIAS_50PC (2 << 0)
#define DA732X_BIAS2_LINE2_DAC_BIAS_75PC (3 << 0)
#define DA732X_BIAS2_LINE2_OUT_BIAS_MASK (7 << 4)
#define DA732X_BIAS2_LINE2_OUT_BIAS_100PC (0 << 4)
#define DA732X_BIAS2_LINE2_OUT_BIAS_125PC (1 << 4)
#define DA732X_BIAS2_LINE2_OUT_BIAS_150PC (2 << 4)
#define DA732X_BIAS2_LINE2_OUT_BIAS_175PC (3 << 4)
#define DA732X_BIAS2_LINE2_OUT_BIAS_200PC (4 << 4)
#define DA732X_BIAS2_LINE2_OUT_BIAS_250PC (5 << 4)
#define DA732X_BIAS2_LINE2_OUT_BIAS_300PC (6 << 4)
#define DA732X_BIAS2_LINE2_OUT_BIAS_350PC (7 << 4)
/* DA732X_REG_BIAS3 (addr=0x06) */
#define DA732X_BIAS3_LINE3_DAC_BIAS_MASK (3 << 0)
#define DA732X_BIAS3_LINE3_DAC_BIAS_100PC (0 << 0)
#define DA732X_BIAS3_LINE3_DAC_BIAS_150PC (1 << 0)
#define DA732X_BIAS3_LINE3_DAC_BIAS_50PC (2 << 0)
#define DA732X_BIAS3_LINE3_DAC_BIAS_75PC (3 << 0)
#define DA732X_BIAS3_LINE3_OUT_BIAS_MASK (7 << 4)
#define DA732X_BIAS3_LINE3_OUT_BIAS_100PC (0 << 4)
#define DA732X_BIAS3_LINE3_OUT_BIAS_125PC (1 << 4)
#define DA732X_BIAS3_LINE3_OUT_BIAS_150PC (2 << 4)
#define DA732X_BIAS3_LINE3_OUT_BIAS_175PC (3 << 4)
#define DA732X_BIAS3_LINE3_OUT_BIAS_200PC (4 << 4)
#define DA732X_BIAS3_LINE3_OUT_BIAS_250PC (5 << 4)
#define DA732X_BIAS3_LINE3_OUT_BIAS_300PC (6 << 4)
#define DA732X_BIAS3_LINE3_OUT_BIAS_350PC (7 << 4)
/* DA732X_REG_BIAS4 (addr=0x07) */
#define DA732X_BIAS4_LINE4_DAC_BIAS_MASK (3 << 0)
#define DA732X_BIAS4_LINE4_DAC_BIAS_100PC (0 << 0)
#define DA732X_BIAS4_LINE4_DAC_BIAS_150PC (1 << 0)
#define DA732X_BIAS4_LINE4_DAC_BIAS_50PC (2 << 0)
#define DA732X_BIAS4_LINE4_DAC_BIAS_75PC (3 << 0)
#define DA732X_BIAS4_LINE4_OUT_BIAS_MASK (7 << 4)
#define DA732X_BIAS4_LINE4_OUT_BIAS_100PC (0 << 4)
#define DA732X_BIAS4_LINE4_OUT_BIAS_125PC (1 << 4)
#define DA732X_BIAS4_LINE4_OUT_BIAS_150PC (2 << 4)
#define DA732X_BIAS4_LINE4_OUT_BIAS_175PC (3 << 4)
#define DA732X_BIAS4_LINE4_OUT_BIAS_200PC (4 << 4)
#define DA732X_BIAS4_LINE4_OUT_BIAS_250PC (5 << 4)
#define DA732X_BIAS4_LINE4_OUT_BIAS_300PC (6 << 4)
#define DA732X_BIAS4_LINE4_OUT_BIAS_350PC (7 << 4)
/* DA732X_REG_SIF_VDD_SEL (addr=0x08) */
#define DA732X_SIF_VDD_SEL_AIFA_VDD2 (1 << 0)
#define DA732X_SIF_VDD_SEL_AIFB_VDD2 (1 << 1)
#define DA732X_SIF_VDD_SEL_CIFA_VDD2 (1 << 4)
/* DA732X_REG_MICBIAS2/1 (addr=0x0F/0x10) */
#define DA732X_MICBIAS_VOLTAGE_MASK (0x0F << 0)
#define DA732X_MICBIAS_VOLTAGE_2V (0x00 << 0)
#define DA732X_MICBIAS_VOLTAGE_2V05 (0x01 << 0)
#define DA732X_MICBIAS_VOLTAGE_2V1 (0x02 << 0)
#define DA732X_MICBIAS_VOLTAGE_2V15 (0x03 << 0)
#define DA732X_MICBIAS_VOLTAGE_2V2 (0x04 << 0)
#define DA732X_MICBIAS_VOLTAGE_2V25 (0x05 << 0)
#define DA732X_MICBIAS_VOLTAGE_2V3 (0x06 << 0)
#define DA732X_MICBIAS_VOLTAGE_2V35 (0x07 << 0)
#define DA732X_MICBIAS_VOLTAGE_2V4 (0x08 << 0)
#define DA732X_MICBIAS_VOLTAGE_2V45 (0x09 << 0)
#define DA732X_MICBIAS_VOLTAGE_2V5 (0x0A << 0)
#define DA732X_MICBIAS_EN (1 << 7)
#define DA732X_MICBIAS_EN_SHIFT 7
#define DA732X_MICBIAS_VOLTAGE_SHIFT 0
#define DA732X_MICBIAS_VOLTAGE_MAX 0x0B
/* DA732X_REG_MICDET (addr=0x11) */
#define DA732X_MICDET_INP_MICRES (1 << 0)
#define DA732X_MICDET_INP_MICHOOK (1 << 1)
#define DA732X_MICDET_INP_DEBOUNCE_PRD_8MS (0 << 0)
#define DA732X_MICDET_INP_DEBOUNCE_PRD_16MS (1 << 0)
#define DA732X_MICDET_INP_DEBOUNCE_PRD_32MS (2 << 0)
#define DA732X_MICDET_INP_DEBOUNCE_PRD_64MS (3 << 0)
#define DA732X_MICDET_INP_MICDET_EN (1 << 7)
/* DA732X_REG_MIC1/2/3_PRE (addr=0x11/0x14/0x18) */
#define DA732X_MICBOOST_MASK 0x7
#define DA732X_MICBOOST_SHIFT 0
#define DA732X_MICBOOST_MIN 0x1
#define DA732X_MICBOOST_MAX DA732X_MICBOOST_MASK
/* DA732X_REG_MIC1/2/3 (addr=0x13/0x15/0x19) */
#define DA732X_MIC_VOL_SHIFT 0
#define DA732X_MIC_VOL_VAL_MASK 0x1F
#define DA732X_MIC_MUTE_SHIFT 6
#define DA732X_MIC_EN_SHIFT 7
#define DA732X_MIC_VOL_VAL_MIN 0x7
#define DA732X_MIC_VOL_VAL_MAX DA732X_MIC_VOL_VAL_MASK
/* DA732X_REG_AUX1L/R (addr=0x16/0x17) */
#define DA732X_AUX_VOL_SHIFT 0
#define DA732X_AUX_VOL_MASK 0x7
#define DA732X_AUX_MUTE_SHIFT 6
#define DA732X_AUX_EN_SHIFT 7
#define DA732X_AUX_VOL_VAL_MAX DA732X_AUX_VOL_MASK
/* DA732X_REG_INP_PINBIAS (addr=0x1A) */
#define DA732X_INP_MICL_PINBIAS_EN (1 << 0)
#define DA732X_INP_MICR_PINBIAS_EN (1 << 1)
#define DA732X_INP_AUX1L_PINBIAS_EN (1 << 2)
#define DA732X_INP_AUX1R_PINBIAS_EN (1 << 3)
#define DA732X_INP_AUX2_PINBIAS_EN (1 << 4)
/* DA732X_REG_INP_ZC_EN (addr=0x1B) */
#define DA732X_MIC1_PRE_ZC_EN (1 << 0)
#define DA732X_MIC1_ZC_EN (1 << 1)
#define DA732X_MIC2_PRE_ZC_EN (1 << 2)
#define DA732X_MIC2_ZC_EN (1 << 3)
#define DA732X_AUXL_ZC_EN (1 << 4)
#define DA732X_AUXR_ZC_EN (1 << 5)
#define DA732X_MIC3_PRE_ZC_EN (1 << 6)
#define DA732X_MIC3_ZC_EN (1 << 7)
/* DA732X_REG_INP_MUX (addr=0x1D) */
#define DA732X_INP_ADC1L_MUX_SEL_AUX1L (0 << 0)
#define DA732X_INP_ADC1L_MUX_SEL_MIC1 (1 << 0)
#define DA732X_INP_ADC1R_MUX_SEL_MASK (3 << 2)
#define DA732X_INP_ADC1R_MUX_SEL_AUX1R (0 << 2)
#define DA732X_INP_ADC1R_MUX_SEL_MIC2 (1 << 2)
#define DA732X_INP_ADC1R_MUX_SEL_MIC3 (2 << 2)
#define DA732X_INP_ADC2L_MUX_SEL_AUX1L (0 << 4)
#define DA732X_INP_ADC2L_MUX_SEL_MICL (1 << 4)
#define DA732X_INP_ADC2R_MUX_SEL_MASK (3 << 6)
#define DA732X_INP_ADC2R_MUX_SEL_AUX1R (0 << 6)
#define DA732X_INP_ADC2R_MUX_SEL_MICR (1 << 6)
#define DA732X_INP_ADC2R_MUX_SEL_AUX2 (2 << 6)
#define DA732X_ADC1L_MUX_SEL_SHIFT 0
#define DA732X_ADC1R_MUX_SEL_SHIFT 2
#define DA732X_ADC2L_MUX_SEL_SHIFT 4
#define DA732X_ADC2R_MUX_SEL_SHIFT 6
/* DA732X_REG_HP_DET (addr=0x20) */
#define DA732X_HP_DET_AZ (1 << 0)
#define DA732X_HP_DET_SEL1 (1 << 1)
#define DA732X_HP_DET_IS_MASK (3 << 2)
#define DA732X_HP_DET_IS_0_5UA (0 << 2)
#define DA732X_HP_DET_IS_1UA (1 << 2)
#define DA732X_HP_DET_IS_2UA (2 << 2)
#define DA732X_HP_DET_IS_4UA (3 << 2)
#define DA732X_HP_DET_RS_MASK (3 << 4)
#define DA732X_HP_DET_RS_INFINITE (0 << 4)
#define DA732X_HP_DET_RS_100KOHM (1 << 4)
#define DA732X_HP_DET_RS_10KOHM (2 << 4)
#define DA732X_HP_DET_RS_1KOHM (3 << 4)
#define DA732X_HP_DET_EN (1 << 7)
/* DA732X_REG_HPL_DAC_OFFSET (addr=0x21/0x26) */
#define DA732X_HP_DAC_OFFSET_TRIM_MASK (0x3F << 0)
#define DA732X_HP_DAC_OFFSET_DAC_SIGN (1 << 6)
/* DA732X_REG_HPL_DAC_OFF_CNTL (addr=0x22/0x27) */
#define DA732X_HP_DAC_OFF_CNTL_CONT_MASK (7 << 0)
#define DA732X_HP_DAC_OFF_CNTL_COMPO (1 << 3)
#define DA732X_HP_DAC_OFF_CALIBRATION (1 << 0)
#define DA732X_HP_DAC_OFF_SCALE_STEPS (1 << 1)
#define DA732X_HP_DAC_OFF_MASK 0x7F
#define DA732X_HP_DAC_COMPO_SHIFT 3
/* DA732X_REG_HPL_OUT_OFFSET (addr=0x23/0x28) */
#define DA732X_HP_OUT_OFFSET_MASK (0xFF << 0)
#define DA732X_HP_DAC_OFFSET_TRIM_VAL 0x7F
/* DA732X_REG_HPL/R (addr=0x24/0x29) */
#define DA732X_HP_OUT_SIGN (1 << 0)
#define DA732X_HP_OUT_COMP (1 << 1)
#define DA732X_HP_OUT_RESERVED (1 << 2)
#define DA732X_HP_OUT_COMPO (1 << 3)
#define DA732X_HP_OUT_DAC_EN (1 << 4)
#define DA732X_HP_OUT_HIZ_EN (1 << 5)
#define DA732X_HP_OUT_HIZ_DIS (0 << 5)
#define DA732X_HP_OUT_MUTE (1 << 6)
#define DA732X_HP_OUT_EN (1 << 7)
#define DA732X_HP_OUT_COMPO_SHIFT 3
#define DA732X_HP_OUT_DAC_EN_SHIFT 4
#define DA732X_HP_HIZ_SHIFT 5
#define DA732X_HP_MUTE_SHIFT 6
#define DA732X_HP_OUT_EN_SHIFT 7
#define DA732X_OUT_HIZ_EN (1 << 5)
#define DA732X_OUT_HIZ_DIS (0 << 5)
/* DA732X_REG_HPL/R_VOL (addr=0x25/0x2A) */
#define DA732X_HP_VOL_VAL_MASK 0xF
#define DA732X_HP_VOL_SHIFT 0
#define DA732X_HP_VOL_VAL_MAX DA732X_HP_VOL_VAL_MASK
/* DA732X_REG_LIN2/3/4 (addr=0x2B/0x2C/0x2D) */
#define DA732X_LOUT_VOL_SHIFT 0
#define DA732X_LOUT_VOL_MASK 0x0F
#define DA732X_LOUT_DAC_OFF (0 << 4)
#define DA732X_LOUT_DAC_EN (1 << 4)
#define DA732X_LOUT_HIZ_N_DIS (0 << 5)
#define DA732X_LOUT_HIZ_N_EN (1 << 5)
#define DA732X_LOUT_UNMUTED (0 << 6)
#define DA732X_LOUT_MUTED (1 << 6)
#define DA732X_LOUT_EN (0 << 7)
#define DA732X_LOUT_DIS (1 << 7)
#define DA732X_LOUT_DAC_EN_SHIFT 4
#define DA732X_LOUT_MUTE_SHIFT 6
#define DA732X_LIN_OUT_EN_SHIFT 7
#define DA732X_LOUT_VOL_VAL_MAX DA732X_LOUT_VOL_MASK
/* DA732X_REG_OUT_ZC_EN (addr=0x2E) */
#define DA732X_HPL_ZC_EN_SHIFT 0
#define DA732X_HPR_ZC_EN_SHIFT 1
#define DA732X_HPL_ZC_EN (1 << 0)
#define DA732X_HPL_ZC_DIS (0 << 0)
#define DA732X_HPR_ZC_EN (1 << 1)
#define DA732X_HPR_ZC_DIS (0 << 1)
#define DA732X_LIN2_ZC_EN (1 << 2)
#define DA732X_LIN2_ZC_DIS (0 << 2)
#define DA732X_LIN3_ZC_EN (1 << 3)
#define DA732X_LIN3_ZC_DIS (0 << 3)
#define DA732X_LIN4_ZC_EN (1 << 4)
#define DA732X_LIN4_ZC_DIS (0 << 4)
/* DA732X_REG_HP_LIN1_GNDSEL (addr=0x37) */
#define DA732X_HP_OUT_GNDSEL (1 << 0)
/* DA732X_REG_CP_HP2 (addr=0x3a) */
#define DA732X_HP_CP_PULSESKIP (1 << 0)
#define DA732X_HP_CP_REG (1 << 1)
#define DA732X_HP_CP_EN (1 << 3)
#define DA732X_HP_CP_DIS (0 << 3)
/* DA732X_REG_CP_CTRL1 (addr=0x40) */
#define DA732X_CP_MODE_MASK (7 << 1)
#define DA732X_CP_CTRL_STANDBY (0 << 1)
#define DA732X_CP_CTRL_CPVDD6 (2 << 1)
#define DA732X_CP_CTRL_CPVDD5 (3 << 1)
#define DA732X_CP_CTRL_CPVDD4 (4 << 1)
#define DA732X_CP_CTRL_CPVDD3 (5 << 1)
#define DA732X_CP_CTRL_CPVDD2 (6 << 1)
#define DA732X_CP_CTRL_CPVDD1 (7 << 1)
#define DA723X_CP_DIS (0 << 7)
#define DA732X_CP_EN (1 << 7)
/* DA732X_REG_CP_CTRL2 (addr=0x41) */
#define DA732X_CP_BOOST (1 << 0)
#define DA732X_CP_MANAGE_MAGNITUDE (2 << 2)
/* DA732X_REG_CP_CTRL3 (addr=0x42) */
#define DA732X_CP_1MHZ (0 << 0)
#define DA732X_CP_500KHZ (1 << 0)
#define DA732X_CP_250KHZ (2 << 0)
#define DA732X_CP_125KHZ (3 << 0)
#define DA732X_CP_63KHZ (4 << 0)
#define DA732X_CP_0KHZ (5 << 0)
/* DA732X_REG_PLL_CTRL (addr=0x53) */
#define DA732X_PLL_INDIV_MASK (3 << 0)
#define DA732X_PLL_SRM_EN (1 << 2)
#define DA732X_PLL_EN (1 << 7)
#define DA732X_PLL_BYPASS (0 << 0)
/* DA732X_REG_CLK_CTRL (addr=0x54) */
#define DA732X_SR1_MASK (0xF)
#define DA732X_SR2_MASK (0xF0)
/* DA732X_REG_CLK_DSP (addr=0x5A) */
#define DA732X_DSP_FREQ_MASK (7 << 0)
#define DA732X_DSP_FREQ_12MHZ (0 << 0)
#define DA732X_DSP_FREQ_24MHZ (1 << 0)
#define DA732X_DSP_FREQ_36MHZ (2 << 0)
#define DA732X_DSP_FREQ_48MHZ (3 << 0)
#define DA732X_DSP_FREQ_60MHZ (4 << 0)
#define DA732X_DSP_FREQ_72MHZ (5 << 0)
#define DA732X_DSP_FREQ_84MHZ (6 << 0)
#define DA732X_DSP_FREQ_96MHZ (7 << 0)
/* DA732X_REG_CLK_EN1 (addr=0x5B) */
#define DA732X_DSP_CLK_EN (1 << 0)
#define DA732X_SYS3_CLK_EN (1 << 1)
#define DA732X_DSP12_CLK_EN (1 << 2)
#define DA732X_PC_CLK_EN (1 << 3)
#define DA732X_MCLK_SQR_EN (1 << 7)
/* DA732X_REG_CLK_EN2 (addr=0x5C) */
#define DA732X_UART_CLK_EN (1 << 1)
#define DA732X_CP_CLK_EN (1 << 2)
#define DA732X_CP_CLK_DIS (0 << 2)
/* DA732X_REG_CLK_EN3 (addr=0x5D) */
#define DA732X_ADCA_BB_CLK_EN (1 << 0)
#define DA732X_ADCC_BB_CLK_EN (1 << 4)
/* DA732X_REG_CLK_EN4 (addr=0x5E) */
#define DA732X_DACA_BB_CLK_EN (1 << 0)
#define DA732X_DACC_BB_CLK_EN (1 << 4)
#define DA732X_DACA_BB_CLK_SHIFT 0
#define DA732X_DACC_BB_CLK_SHIFT 4
/* DA732X_REG_CLK_EN5 (addr=0x5F) */
#define DA732X_DACE_BB_CLK_EN (1 << 0)
#define DA732X_DACE_BB_CLK_SHIFT 0
/* DA732X_REG_AIF_MCLK (addr=0x60) */
#define DA732X_AIFM_FRAME_64 (1 << 2)
#define DA732X_AIFM_SRC_SEL_AIFA (1 << 6)
#define DA732X_CLK_GENERATION_AIF_A (1 << 4)
#define DA732X_NO_CLK_GENERATION 0x0
/* DA732X_REG_AIFA1 (addr=0x61) */
#define DA732X_AIF_WORD_MASK (0x3 << 0)
#define DA732X_AIF_WORD_16 (0 << 0)
#define DA732X_AIF_WORD_20 (1 << 0)
#define DA732X_AIF_WORD_24 (2 << 0)
#define DA732X_AIF_WORD_32 (3 << 0)
#define DA732X_AIF_TDM_MONO_SHIFT (1 << 6)
#define DA732X_AIF1_CLK_MASK (1 << 7)
#define DA732X_AIF_SLAVE (0 << 7)
#define DA732X_AIF_CLK_FROM_SRC (1 << 7)
/* DA732X_REG_AIFA3 (addr=0x63) */
#define DA732X_AIF_MODE_SHIFT 0
#define DA732X_AIF_MODE_MASK 0x3
#define DA732X_AIF_I2S_MODE (0 << 0)
#define DA732X_AIF_LEFT_J_MODE (1 << 0)
#define DA732X_AIF_RIGHT_J_MODE (2 << 0)
#define DA732X_AIF_DSP_MODE (3 << 0)
#define DA732X_AIF_WCLK_INV (1 << 4)
#define DA732X_AIF_BCLK_INV (1 << 5)
#define DA732X_AIF_EN (1 << 7)
#define DA732X_AIF_EN_SHIFT 7
/* DA732X_REG_PC_CTRL (addr=0x6a) */
#define DA732X_PC_PULSE_AIFA (0 << 0)
#define DA732X_PC_PULSE_AIFB (1 << 0)
#define DA732X_PC_RESYNC_AUT (1 << 6)
#define DA732X_PC_RESYNC_NOT_AUT (0 << 6)
#define DA732X_PC_SAME (1 << 7)
/* DA732X_REG_DATA_ROUTE (addr=0x70) */
#define DA732X_ADC1_TO_AIFA (0 << 0)
#define DA732X_DSP_TO_AIFA (1 << 0)
#define DA732X_ADC2_TO_AIFB (0 << 1)
#define DA732X_DSP_TO_AIFB (1 << 1)
#define DA732X_AIFA_TO_DAC1L (0 << 2)
#define DA732X_DSP_TO_DAC1L (1 << 2)
#define DA732X_AIFA_TO_DAC1R (0 << 3)
#define DA732X_DSP_TO_DAC1R (1 << 3)
#define DA732X_AIFB_TO_DAC2L (0 << 4)
#define DA732X_DSP_TO_DAC2L (1 << 4)
#define DA732X_AIFB_TO_DAC2R (0 << 5)
#define DA732X_DSP_TO_DAC2R (1 << 5)
#define DA732X_AIFB_TO_DAC3 (0 << 6)
#define DA732X_DSP_TO_DAC3 (1 << 6)
#define DA732X_BYPASS_DSP (0 << 0)
#define DA732X_ALL_TO_DSP (0x7F << 0)
/* DA732X_REG_DSP_CTRL (addr=0x71) */
#define DA732X_DIGITAL_EN (1 << 0)
#define DA732X_DIGITAL_RESET (0 << 0)
#define DA732X_DSP_CORE_EN (1 << 1)
#define DA732X_DSP_CORE_RESET (0 << 1)
/* DA732X_REG_SPARE1_OUT (addr=0x7D)*/
#define DA732X_HP_DRIVER_EN (1 << 0)
#define DA732X_HP_GATE_LOW (1 << 2)
#define DA732X_HP_LOOP_GAIN_CTRL (1 << 3)
/* DA732X_REG_ID (addr=0x81)*/
#define DA732X_ID_MINOR_MASK (0xF << 0)
#define DA732X_ID_MAJOR_MASK (0xF << 4)
/* DA732X_REG_ADC1/2_PD (addr=0x90/0x98) */
#define DA732X_ADC_RST_MASK (0x3 << 0)
#define DA732X_ADC_PD_MASK (0x3 << 2)
#define DA732X_ADC_SET_ACT (0x3 << 0)
#define DA732X_ADC_SET_RST (0x0 << 0)
#define DA732X_ADC_ON (0x3 << 2)
#define DA732X_ADC_OFF (0x0 << 2)
/* DA732X_REG_ADC1/2_SEL (addr=0x94/0x9C) */
#define DA732X_ADC_VOL_VAL_MASK 0x7
#define DA732X_ADCL_VOL_SHIFT 0
#define DA732X_ADCR_VOL_SHIFT 4
#define DA732X_ADCL_EN_SHIFT 2
#define DA732X_ADCR_EN_SHIFT 3
#define DA732X_ADCL_EN (1 << 2)
#define DA732X_ADCR_EN (1 << 3)
#define DA732X_ADC_VOL_VAL_MAX DA732X_ADC_VOL_VAL_MASK
/*
* DA732X_REG_ADC1/2_HPF (addr=0x93/0x9b)
* DA732x_REG_DAC1/2/3_HPG (addr=0xA5/0xB5/0xC5)
*/
#define DA732X_HPF_MUSIC_EN (1 << 3)
#define DA732X_HPF_VOICE_EN ((1 << 3) | (1 << 7))
#define DA732X_HPF_MASK ((1 << 3) | (1 << 7))
#define DA732X_HPF_DIS ((0 << 3) | (0 << 7))
/* DA732X_REG_DAC1/2/3_VOL */
#define DA732X_DAC_VOL_VAL_MASK 0x7F
#define DA732X_DAC_VOL_SHIFT 0
#define DA732X_DAC_VOL_VAL_MAX DA732X_DAC_VOL_VAL_MASK
/* DA732X_REG_DAC1/2/3_SEL (addr=0xA3/0xB3/0xC3) */
#define DA732X_DACL_EN_SHIFT 3
#define DA732X_DACR_EN_SHIFT 7
#define DA732X_DACL_MUTE_SHIFT 2
#define DA732X_DACR_MUTE_SHIFT 6
#define DA732X_DACL_EN (1 << 3)
#define DA732X_DACR_EN (1 << 7)
#define DA732X_DACL_SDM (1 << 0)
#define DA732X_DACR_SDM (1 << 4)
#define DA732X_DACL_MUTE (1 << 2)
#define DA732X_DACR_MUTE (1 << 6)
/* DA732X_REG_DAC_SOFTMUTE (addr=0xA4/0xB4/0xC4) */
#define DA732X_SOFTMUTE_EN (1 << 7)
#define DA732X_GAIN_RAMPED (1 << 6)
#define DA732X_16_SAMPLES (4 << 0)
#define DA732X_SOFTMUTE_MASK (1 << 7)
#define DA732X_SOFTMUTE_SHIFT 7
/*
* DA732x_REG_ADC1/2_EQ12 (addr=0x95/0x9D)
* DA732x_REG_ADC1/2_EQ34 (addr=0x96/0x9E)
* DA732x_REG_ADC1/2_EQ5 (addr=0x97/0x9F)
* DA732x_REG_DAC1/2/3_EQ12 (addr=0xA5/0xB5/0xC5)
* DA732x_REG_DAC1/2/3_EQ34 (addr=0xA6/0xB6/0xC6)
* DA732x_REG_DAC1/2/3_EQ5 (addr=0xA7/0xB7/0xB7)
*/
#define DA732X_EQ_VOL_VAL_MASK 0xF
#define DA732X_EQ_BAND1_SHIFT 0
#define DA732X_EQ_BAND2_SHIFT 4
#define DA732X_EQ_BAND3_SHIFT 0
#define DA732X_EQ_BAND4_SHIFT 4
#define DA732X_EQ_BAND5_SHIFT 0
#define DA732X_EQ_OVERALL_SHIFT 4
#define DA732X_EQ_OVERALL_VOL_VAL_MASK 0x3
#define DA732X_EQ_DIS (0 << 7)
#define DA732X_EQ_EN (1 << 7)
#define DA732X_EQ_EN_SHIFT 7
#define DA732X_EQ_VOL_VAL_MAX DA732X_EQ_VOL_VAL_MASK
#define DA732X_EQ_OVERALL_VOL_VAL_MAX DA732X_EQ_OVERALL_VOL_VAL_MASK
/* DA732X_REG_DMA_CMD (addr=0xD3) */
#define DA732X_SEL_DSP_DMA_MASK (3 << 0)
#define DA732X_SEL_DSP_DMA_DIS (0 << 0)
#define DA732X_SEL_DSP_DMA_PMEM (1 << 0)
#define DA732X_SEL_DSP_DMA_XMEM (2 << 0)
#define DA732X_SEL_DSP_DMA_YMEM (3 << 0)
#define DA732X_DSP_RW_MASK (1 << 4)
#define DA732X_DSP_DMA_WRITE (0 << 4)
#define DA732X_DSP_DMA_READ (1 << 4)
/* DA732X_REG_DMA_STATUS (addr=0xDA) */
#define DA732X_DSP_DMA_FREE (0 << 0)
#define DA732X_DSP_DMA_BUSY (1 << 0)
#endif /* __DA732X_REG_H_ */

1554
sound/soc/codecs/da9055.c Normal file

File diff suppressed because it is too large Load diff

87
sound/soc/codecs/dmic.c Normal file
View file

@ -0,0 +1,87 @@
/*
* dmic.c -- SoC audio for Generic Digital MICs
*
* Author: Liam Girdwood <lrg@slimlogic.co.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
static struct snd_soc_dai_driver dmic_dai = {
.name = "dmic-hifi",
.capture = {
.stream_name = "Capture",
.channels_min = 1,
.channels_max = 8,
.rates = SNDRV_PCM_RATE_CONTINUOUS,
.formats = SNDRV_PCM_FMTBIT_S32_LE
| SNDRV_PCM_FMTBIT_S24_LE
| SNDRV_PCM_FMTBIT_S16_LE,
},
};
static const struct snd_soc_dapm_widget dmic_dapm_widgets[] = {
SND_SOC_DAPM_AIF_OUT("DMIC AIF", "Capture", 0,
SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_INPUT("DMic"),
};
static const struct snd_soc_dapm_route intercon[] = {
{"DMIC AIF", NULL, "DMic"},
};
static struct snd_soc_codec_driver soc_dmic = {
.dapm_widgets = dmic_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(dmic_dapm_widgets),
.dapm_routes = intercon,
.num_dapm_routes = ARRAY_SIZE(intercon),
};
static int dmic_dev_probe(struct platform_device *pdev)
{
return snd_soc_register_codec(&pdev->dev,
&soc_dmic, &dmic_dai, 1);
}
static int dmic_dev_remove(struct platform_device *pdev)
{
snd_soc_unregister_codec(&pdev->dev);
return 0;
}
MODULE_ALIAS("platform:dmic-codec");
static struct platform_driver dmic_driver = {
.driver = {
.name = "dmic-codec",
.owner = THIS_MODULE,
},
.probe = dmic_dev_probe,
.remove = dmic_dev_remove,
};
module_platform_driver(dmic_driver);
MODULE_DESCRIPTION("Generic DMIC driver");
MODULE_AUTHOR("Liam Girdwood <lrg@slimlogic.co.uk>");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,133 @@
/*
* ALSA SoC dummy codec driver
*
* This driver provides two dummy dais.
*
* Copyright (c) 2013 Samsung Electronics
* http://www.samsungsemi.com/
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/of.h>
#include <sound/soc.h>
#include <sound/pcm.h>
#include <sound/initval.h>
#define DRV_NAME "dummy-codec"
#define RATES SNDRV_PCM_RATE_8000_192000
#define FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
static struct snd_soc_codec_driver soc_codec_dummy;
static struct snd_soc_dai_driver dummy_codec_dai[] = {
{
.name = "dummy-aif1",
.playback = {
.stream_name = "AIF1 Playback",
.channels_min = 1,
.channels_max = 384,
.rates = RATES,
.formats = FORMATS,
},
.capture = {
.stream_name = "AIF1 Capture",
.channels_min = 1,
.channels_max = 384,
.rates = RATES,
.formats = FORMATS,
},
}, {
.name = "dummy-aif2",
.playback = {
.stream_name = "AIF2 Playback",
.channels_min = 1,
.channels_max = 384,
.rates = RATES,
.formats = FORMATS,
},
.capture = {
.stream_name = "AIF2 Capture",
.channels_min = 1,
.channels_max = 384,
.rates = RATES,
.formats = FORMATS,
},
}, {
.name = "dummy-aif3",
.playback = {
.stream_name = "AIF3 Playback",
.channels_min = 1,
.channels_max = 384,
.rates = RATES,
.formats = FORMATS,
},
.capture = {
.stream_name = "AIF3 Capture",
.channels_min = 1,
.channels_max = 384,
.rates = RATES,
.formats = FORMATS,
},
}, {
.name = "dummy-aif4",
.playback = {
.stream_name = "AIF4 Playback",
.channels_min = 1,
.channels_max = 384,
.rates = RATES,
.formats = FORMATS,
},
.capture = {
.stream_name = "AIF4 Capture",
.channels_min = 1,
.channels_max = 384,
.rates = RATES,
.formats = FORMATS,
},
}
};
static int dummy_codec_probe(struct platform_device *pdev)
{
return snd_soc_register_codec(&pdev->dev, &soc_codec_dummy,
dummy_codec_dai, ARRAY_SIZE(dummy_codec_dai));
}
static int dummy_codec_remove(struct platform_device *pdev)
{
snd_soc_unregister_codec(&pdev->dev);
return 0;
}
#ifdef CONFIG_OF
static const struct of_device_id dummy_codec_of_match[] = {
{ .compatible = "samsung,dummy-codec", },
{},
};
MODULE_DEVICE_TABLE(of, dummy_codec_of_match);
#endif /* CONFIG_OF */
static struct platform_driver dummy_codec_driver = {
.probe = dummy_codec_probe,
.remove = dummy_codec_remove,
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(dummy_codec_of_match),
},
};
module_platform_driver(dummy_codec_driver);
MODULE_AUTHOR("Yeongman Seo <yman.seo@samsung.com>");
MODULE_DESCRIPTION("Dummy codec driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:" DRV_NAME);

View file

@ -0,0 +1,60 @@
/*
* es8328-i2c.c -- ES8328 ALSA SoC I2C Audio driver
*
* Copyright 2014 Sutajio Ko-Usagi PTE LTD
*
* Author: Sean Cross <xobs@kosagi.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
#include <sound/soc.h>
#include "es8328.h"
static const struct i2c_device_id es8328_id[] = {
{ "es8328", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, es8328_id);
static const struct of_device_id es8328_of_match[] = {
{ .compatible = "everest,es8328", },
{ }
};
MODULE_DEVICE_TABLE(of, es8328_of_match);
static int es8328_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
return es8328_probe(&i2c->dev,
devm_regmap_init_i2c(i2c, &es8328_regmap_config));
}
static int es8328_i2c_remove(struct i2c_client *i2c)
{
snd_soc_unregister_codec(&i2c->dev);
return 0;
}
static struct i2c_driver es8328_i2c_driver = {
.driver = {
.name = "es8328",
.of_match_table = es8328_of_match,
},
.probe = es8328_i2c_probe,
.remove = es8328_i2c_remove,
.id_table = es8328_id,
};
module_i2c_driver(es8328_i2c_driver);
MODULE_DESCRIPTION("ASoC ES8328 audio CODEC I2C driver");
MODULE_AUTHOR("Sean Cross <xobs@kosagi.com>");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,49 @@
/*
* es8328.c -- ES8328 ALSA SoC SPI Audio driver
*
* Copyright 2014 Sutajio Ko-Usagi PTE LTD
*
* Author: Sean Cross <xobs@kosagi.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/spi/spi.h>
#include <sound/soc.h>
#include "es8328.h"
static const struct of_device_id es8328_of_match[] = {
{ .compatible = "everest,es8328", },
{ }
};
MODULE_DEVICE_TABLE(of, es8328_of_match);
static int es8328_spi_probe(struct spi_device *spi)
{
return es8328_probe(&spi->dev,
devm_regmap_init_spi(spi, &es8328_regmap_config));
}
static int es8328_spi_remove(struct spi_device *spi)
{
snd_soc_unregister_codec(&spi->dev);
return 0;
}
static struct spi_driver es8328_spi_driver = {
.driver = {
.name = "es8328",
.of_match_table = es8328_of_match,
},
.probe = es8328_spi_probe,
.remove = es8328_spi_remove,
};
module_spi_driver(es8328_spi_driver);
MODULE_DESCRIPTION("ASoC ES8328 audio CODEC SPI driver");
MODULE_AUTHOR("Sean Cross <xobs@kosagi.com>");
MODULE_LICENSE("GPL");

756
sound/soc/codecs/es8328.c Normal file
View file

@ -0,0 +1,756 @@
/*
* es8328.c -- ES8328 ALSA SoC Audio driver
*
* Copyright 2014 Sutajio Ko-Usagi PTE LTD
*
* Author: Sean Cross <xobs@kosagi.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/of_device.h>
#include <linux/module.h>
#include <linux/pm.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/regulator/consumer.h>
#include <sound/core.h>
#include <sound/initval.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/tlv.h>
#include "es8328.h"
#define ES8328_SYSCLK_RATE_1X 11289600
#define ES8328_SYSCLK_RATE_2X 22579200
/* Run the codec at 22.5792 or 11.2896 MHz to support these rates */
static struct {
int rate;
u8 ratio;
} mclk_ratios[] = {
{ 8000, 9 },
{11025, 7 },
{22050, 4 },
{44100, 2 },
};
/* regulator supplies for sgtl5000, VDDD is an optional external supply */
enum sgtl5000_regulator_supplies {
DVDD,
AVDD,
PVDD,
HPVDD,
ES8328_SUPPLY_NUM
};
/* vddd is optional supply */
static const char * const supply_names[ES8328_SUPPLY_NUM] = {
"DVDD",
"AVDD",
"PVDD",
"HPVDD",
};
#define ES8328_RATES (SNDRV_PCM_RATE_44100 | \
SNDRV_PCM_RATE_22050 | \
SNDRV_PCM_RATE_11025)
#define ES8328_FORMATS (SNDRV_PCM_FMTBIT_S16_LE)
struct es8328_priv {
struct regmap *regmap;
struct clk *clk;
int playback_fs;
bool deemph;
struct regulator_bulk_data supplies[ES8328_SUPPLY_NUM];
};
/*
* ES8328 Controls
*/
static const char * const adcpol_txt[] = {"Normal", "L Invert", "R Invert",
"L + R Invert"};
static SOC_ENUM_SINGLE_DECL(adcpol,
ES8328_ADCCONTROL6, 6, adcpol_txt);
static const DECLARE_TLV_DB_SCALE(play_tlv, -3000, 100, 0);
static const DECLARE_TLV_DB_SCALE(dac_adc_tlv, -9600, 50, 0);
static const DECLARE_TLV_DB_SCALE(pga_tlv, 0, 300, 0);
static const DECLARE_TLV_DB_SCALE(bypass_tlv, -1500, 300, 0);
static const DECLARE_TLV_DB_SCALE(mic_tlv, 0, 300, 0);
static const int deemph_settings[] = { 0, 32000, 44100, 48000 };
static int es8328_set_deemph(struct snd_soc_codec *codec)
{
struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec);
int val, i, best;
/*
* If we're using deemphasis select the nearest available sample
* rate.
*/
if (es8328->deemph) {
best = 1;
for (i = 2; i < ARRAY_SIZE(deemph_settings); i++) {
if (abs(deemph_settings[i] - es8328->playback_fs) <
abs(deemph_settings[best] - es8328->playback_fs))
best = i;
}
val = best << 1;
} else {
val = 0;
}
dev_dbg(codec->dev, "Set deemphasis %d\n", val);
return snd_soc_update_bits(codec, ES8328_DACCONTROL6, 0x6, val);
}
static int es8328_get_deemph(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec);
ucontrol->value.integer.value[0] = es8328->deemph;
return 0;
}
static int es8328_put_deemph(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec);
int deemph = ucontrol->value.integer.value[0];
int ret;
if (deemph > 1)
return -EINVAL;
ret = es8328_set_deemph(codec);
if (ret < 0)
return ret;
es8328->deemph = deemph;
return 0;
}
static const struct snd_kcontrol_new es8328_snd_controls[] = {
SOC_DOUBLE_R_TLV("Capture Digital Volume",
ES8328_ADCCONTROL8, ES8328_ADCCONTROL9,
0, 0xc0, 1, dac_adc_tlv),
SOC_SINGLE("Capture ZC Switch", ES8328_ADCCONTROL7, 6, 1, 0),
SOC_SINGLE_BOOL_EXT("DAC Deemphasis Switch", 0,
es8328_get_deemph, es8328_put_deemph),
SOC_ENUM("Capture Polarity", adcpol),
SOC_SINGLE_TLV("Left Mixer Left Bypass Volume",
ES8328_DACCONTROL17, 3, 7, 1, bypass_tlv),
SOC_SINGLE_TLV("Left Mixer Right Bypass Volume",
ES8328_DACCONTROL19, 3, 7, 1, bypass_tlv),
SOC_SINGLE_TLV("Right Mixer Left Bypass Volume",
ES8328_DACCONTROL18, 3, 7, 1, bypass_tlv),
SOC_SINGLE_TLV("Right Mixer Right Bypass Volume",
ES8328_DACCONTROL20, 3, 7, 1, bypass_tlv),
SOC_DOUBLE_R_TLV("PCM Volume",
ES8328_LDACVOL, ES8328_RDACVOL,
0, ES8328_DACVOL_MAX, 1, dac_adc_tlv),
SOC_DOUBLE_R_TLV("Output 1 Playback Volume",
ES8328_LOUT1VOL, ES8328_ROUT1VOL,
0, ES8328_OUT1VOL_MAX, 0, play_tlv),
SOC_DOUBLE_R_TLV("Output 2 Playback Volume",
ES8328_LOUT2VOL, ES8328_ROUT2VOL,
0, ES8328_OUT2VOL_MAX, 0, play_tlv),
SOC_DOUBLE_TLV("Mic PGA Volume", ES8328_ADCCONTROL1,
4, 0, 8, 0, mic_tlv),
};
/*
* DAPM Controls
*/
static const char * const es8328_line_texts[] = {
"Line 1", "Line 2", "PGA", "Differential"};
static const struct soc_enum es8328_lline_enum =
SOC_ENUM_SINGLE(ES8328_DACCONTROL16, 3,
ARRAY_SIZE(es8328_line_texts),
es8328_line_texts);
static const struct snd_kcontrol_new es8328_left_line_controls =
SOC_DAPM_ENUM("Route", es8328_lline_enum);
static const struct soc_enum es8328_rline_enum =
SOC_ENUM_SINGLE(ES8328_DACCONTROL16, 0,
ARRAY_SIZE(es8328_line_texts),
es8328_line_texts);
static const struct snd_kcontrol_new es8328_right_line_controls =
SOC_DAPM_ENUM("Route", es8328_lline_enum);
/* Left Mixer */
static const struct snd_kcontrol_new es8328_left_mixer_controls[] = {
SOC_DAPM_SINGLE("Playback Switch", ES8328_DACCONTROL17, 8, 1, 0),
SOC_DAPM_SINGLE("Left Bypass Switch", ES8328_DACCONTROL17, 7, 1, 0),
SOC_DAPM_SINGLE("Right Playback Switch", ES8328_DACCONTROL18, 8, 1, 0),
SOC_DAPM_SINGLE("Right Bypass Switch", ES8328_DACCONTROL18, 7, 1, 0),
};
/* Right Mixer */
static const struct snd_kcontrol_new es8328_right_mixer_controls[] = {
SOC_DAPM_SINGLE("Left Playback Switch", ES8328_DACCONTROL19, 8, 1, 0),
SOC_DAPM_SINGLE("Left Bypass Switch", ES8328_DACCONTROL19, 7, 1, 0),
SOC_DAPM_SINGLE("Playback Switch", ES8328_DACCONTROL20, 8, 1, 0),
SOC_DAPM_SINGLE("Right Bypass Switch", ES8328_DACCONTROL20, 7, 1, 0),
};
static const char * const es8328_pga_sel[] = {
"Line 1", "Line 2", "Line 3", "Differential"};
/* Left PGA Mux */
static const struct soc_enum es8328_lpga_enum =
SOC_ENUM_SINGLE(ES8328_ADCCONTROL2, 6,
ARRAY_SIZE(es8328_pga_sel),
es8328_pga_sel);
static const struct snd_kcontrol_new es8328_left_pga_controls =
SOC_DAPM_ENUM("Route", es8328_lpga_enum);
/* Right PGA Mux */
static const struct soc_enum es8328_rpga_enum =
SOC_ENUM_SINGLE(ES8328_ADCCONTROL2, 4,
ARRAY_SIZE(es8328_pga_sel),
es8328_pga_sel);
static const struct snd_kcontrol_new es8328_right_pga_controls =
SOC_DAPM_ENUM("Route", es8328_rpga_enum);
/* Differential Mux */
static const char * const es8328_diff_sel[] = {"Line 1", "Line 2"};
static SOC_ENUM_SINGLE_DECL(diffmux,
ES8328_ADCCONTROL3, 7, es8328_diff_sel);
static const struct snd_kcontrol_new es8328_diffmux_controls =
SOC_DAPM_ENUM("Route", diffmux);
/* Mono ADC Mux */
static const char * const es8328_mono_mux[] = {"Stereo", "Mono (Left)",
"Mono (Right)", "Digital Mono"};
static SOC_ENUM_SINGLE_DECL(monomux,
ES8328_ADCCONTROL3, 3, es8328_mono_mux);
static const struct snd_kcontrol_new es8328_monomux_controls =
SOC_DAPM_ENUM("Route", monomux);
static const struct snd_soc_dapm_widget es8328_dapm_widgets[] = {
SND_SOC_DAPM_MUX("Differential Mux", SND_SOC_NOPM, 0, 0,
&es8328_diffmux_controls),
SND_SOC_DAPM_MUX("Left ADC Mux", SND_SOC_NOPM, 0, 0,
&es8328_monomux_controls),
SND_SOC_DAPM_MUX("Right ADC Mux", SND_SOC_NOPM, 0, 0,
&es8328_monomux_controls),
SND_SOC_DAPM_MUX("Left PGA Mux", ES8328_ADCPOWER,
ES8328_ADCPOWER_AINL_OFF, 1,
&es8328_left_pga_controls),
SND_SOC_DAPM_MUX("Right PGA Mux", ES8328_ADCPOWER,
ES8328_ADCPOWER_AINR_OFF, 1,
&es8328_right_pga_controls),
SND_SOC_DAPM_MUX("Left Line Mux", SND_SOC_NOPM, 0, 0,
&es8328_left_line_controls),
SND_SOC_DAPM_MUX("Right Line Mux", SND_SOC_NOPM, 0, 0,
&es8328_right_line_controls),
SND_SOC_DAPM_ADC("Right ADC", "Right Capture", ES8328_ADCPOWER,
ES8328_ADCPOWER_ADCR_OFF, 1),
SND_SOC_DAPM_ADC("Left ADC", "Left Capture", ES8328_ADCPOWER,
ES8328_ADCPOWER_ADCL_OFF, 1),
SND_SOC_DAPM_SUPPLY("Mic Bias", ES8328_ADCPOWER,
ES8328_ADCPOWER_MIC_BIAS_OFF, 1, NULL, 0),
SND_SOC_DAPM_SUPPLY("Mic Bias Gen", ES8328_ADCPOWER,
ES8328_ADCPOWER_ADC_BIAS_GEN_OFF, 1, NULL, 0),
SND_SOC_DAPM_SUPPLY("DAC STM", ES8328_CHIPPOWER,
ES8328_CHIPPOWER_DACSTM_RESET, 1, NULL, 0),
SND_SOC_DAPM_SUPPLY("ADC STM", ES8328_CHIPPOWER,
ES8328_CHIPPOWER_ADCSTM_RESET, 1, NULL, 0),
SND_SOC_DAPM_SUPPLY("DAC DIG", ES8328_CHIPPOWER,
ES8328_CHIPPOWER_DACDIG_OFF, 1, NULL, 0),
SND_SOC_DAPM_SUPPLY("ADC DIG", ES8328_CHIPPOWER,
ES8328_CHIPPOWER_ADCDIG_OFF, 1, NULL, 0),
SND_SOC_DAPM_SUPPLY("DAC DLL", ES8328_CHIPPOWER,
ES8328_CHIPPOWER_DACDLL_OFF, 1, NULL, 0),
SND_SOC_DAPM_SUPPLY("ADC DLL", ES8328_CHIPPOWER,
ES8328_CHIPPOWER_ADCDLL_OFF, 1, NULL, 0),
SND_SOC_DAPM_SUPPLY("ADC Vref", ES8328_CHIPPOWER,
ES8328_CHIPPOWER_ADCVREF_OFF, 1, NULL, 0),
SND_SOC_DAPM_SUPPLY("DAC Vref", ES8328_CHIPPOWER,
ES8328_CHIPPOWER_DACVREF_OFF, 1, NULL, 0),
SND_SOC_DAPM_DAC("Right DAC", "Right Playback", ES8328_DACPOWER,
ES8328_DACPOWER_RDAC_OFF, 1),
SND_SOC_DAPM_DAC("Left DAC", "Left Playback", ES8328_DACPOWER,
ES8328_DACPOWER_LDAC_OFF, 1),
SND_SOC_DAPM_MIXER("Left Mixer", SND_SOC_NOPM, 0, 0,
&es8328_left_mixer_controls[0],
ARRAY_SIZE(es8328_left_mixer_controls)),
SND_SOC_DAPM_MIXER("Right Mixer", SND_SOC_NOPM, 0, 0,
&es8328_right_mixer_controls[0],
ARRAY_SIZE(es8328_right_mixer_controls)),
SND_SOC_DAPM_PGA("Right Out 2", ES8328_DACPOWER,
ES8328_DACPOWER_ROUT2_ON, 0, NULL, 0),
SND_SOC_DAPM_PGA("Left Out 2", ES8328_DACPOWER,
ES8328_DACPOWER_LOUT2_ON, 0, NULL, 0),
SND_SOC_DAPM_PGA("Right Out 1", ES8328_DACPOWER,
ES8328_DACPOWER_ROUT1_ON, 0, NULL, 0),
SND_SOC_DAPM_PGA("Left Out 1", ES8328_DACPOWER,
ES8328_DACPOWER_LOUT1_ON, 0, NULL, 0),
SND_SOC_DAPM_OUTPUT("LOUT1"),
SND_SOC_DAPM_OUTPUT("ROUT1"),
SND_SOC_DAPM_OUTPUT("LOUT2"),
SND_SOC_DAPM_OUTPUT("ROUT2"),
SND_SOC_DAPM_INPUT("LINPUT1"),
SND_SOC_DAPM_INPUT("LINPUT2"),
SND_SOC_DAPM_INPUT("RINPUT1"),
SND_SOC_DAPM_INPUT("RINPUT2"),
};
static const struct snd_soc_dapm_route es8328_dapm_routes[] = {
{ "Left Line Mux", "Line 1", "LINPUT1" },
{ "Left Line Mux", "Line 2", "LINPUT2" },
{ "Left Line Mux", "PGA", "Left PGA Mux" },
{ "Left Line Mux", "Differential", "Differential Mux" },
{ "Right Line Mux", "Line 1", "RINPUT1" },
{ "Right Line Mux", "Line 2", "RINPUT2" },
{ "Right Line Mux", "PGA", "Right PGA Mux" },
{ "Right Line Mux", "Differential", "Differential Mux" },
{ "Left PGA Mux", "Line 1", "LINPUT1" },
{ "Left PGA Mux", "Line 2", "LINPUT2" },
{ "Left PGA Mux", "Differential", "Differential Mux" },
{ "Right PGA Mux", "Line 1", "RINPUT1" },
{ "Right PGA Mux", "Line 2", "RINPUT2" },
{ "Right PGA Mux", "Differential", "Differential Mux" },
{ "Differential Mux", "Line 1", "LINPUT1" },
{ "Differential Mux", "Line 1", "RINPUT1" },
{ "Differential Mux", "Line 2", "LINPUT2" },
{ "Differential Mux", "Line 2", "RINPUT2" },
{ "Left ADC Mux", "Stereo", "Left PGA Mux" },
{ "Left ADC Mux", "Mono (Left)", "Left PGA Mux" },
{ "Left ADC Mux", "Digital Mono", "Left PGA Mux" },
{ "Right ADC Mux", "Stereo", "Right PGA Mux" },
{ "Right ADC Mux", "Mono (Right)", "Right PGA Mux" },
{ "Right ADC Mux", "Digital Mono", "Right PGA Mux" },
{ "Left ADC", NULL, "Left ADC Mux" },
{ "Right ADC", NULL, "Right ADC Mux" },
{ "ADC DIG", NULL, "ADC STM" },
{ "ADC DIG", NULL, "ADC Vref" },
{ "ADC DIG", NULL, "ADC DLL" },
{ "Left ADC", NULL, "ADC DIG" },
{ "Right ADC", NULL, "ADC DIG" },
{ "Mic Bias", NULL, "Mic Bias Gen" },
{ "Left Line Mux", "Line 1", "LINPUT1" },
{ "Left Line Mux", "Line 2", "LINPUT2" },
{ "Left Line Mux", "PGA", "Left PGA Mux" },
{ "Left Line Mux", "Differential", "Differential Mux" },
{ "Right Line Mux", "Line 1", "RINPUT1" },
{ "Right Line Mux", "Line 2", "RINPUT2" },
{ "Right Line Mux", "PGA", "Right PGA Mux" },
{ "Right Line Mux", "Differential", "Differential Mux" },
{ "Left Out 1", NULL, "Left DAC" },
{ "Right Out 1", NULL, "Right DAC" },
{ "Left Out 2", NULL, "Left DAC" },
{ "Right Out 2", NULL, "Right DAC" },
{ "Left Mixer", "Playback Switch", "Left DAC" },
{ "Left Mixer", "Left Bypass Switch", "Left Line Mux" },
{ "Left Mixer", "Right Playback Switch", "Right DAC" },
{ "Left Mixer", "Right Bypass Switch", "Right Line Mux" },
{ "Right Mixer", "Left Playback Switch", "Left DAC" },
{ "Right Mixer", "Left Bypass Switch", "Left Line Mux" },
{ "Right Mixer", "Playback Switch", "Right DAC" },
{ "Right Mixer", "Right Bypass Switch", "Right Line Mux" },
{ "DAC DIG", NULL, "DAC STM" },
{ "DAC DIG", NULL, "DAC Vref" },
{ "DAC DIG", NULL, "DAC DLL" },
{ "Left DAC", NULL, "DAC DIG" },
{ "Right DAC", NULL, "DAC DIG" },
{ "Left Out 1", NULL, "Left Mixer" },
{ "LOUT1", NULL, "Left Out 1" },
{ "Right Out 1", NULL, "Right Mixer" },
{ "ROUT1", NULL, "Right Out 1" },
{ "Left Out 2", NULL, "Left Mixer" },
{ "LOUT2", NULL, "Left Out 2" },
{ "Right Out 2", NULL, "Right Mixer" },
{ "ROUT2", NULL, "Right Out 2" },
};
static int es8328_mute(struct snd_soc_dai *dai, int mute)
{
return snd_soc_update_bits(dai->codec, ES8328_DACCONTROL3,
ES8328_DACCONTROL3_DACMUTE,
mute ? ES8328_DACCONTROL3_DACMUTE : 0);
}
static int es8328_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec);
int clk_rate;
int i;
int reg;
u8 ratio;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
reg = ES8328_DACCONTROL2;
else
reg = ES8328_ADCCONTROL5;
clk_rate = clk_get_rate(es8328->clk);
if ((clk_rate != ES8328_SYSCLK_RATE_1X) &&
(clk_rate != ES8328_SYSCLK_RATE_2X)) {
dev_err(codec->dev,
"%s: clock is running at %d Hz, not %d or %d Hz\n",
__func__, clk_rate,
ES8328_SYSCLK_RATE_1X, ES8328_SYSCLK_RATE_2X);
return -EINVAL;
}
/* find master mode MCLK to sampling frequency ratio */
ratio = mclk_ratios[0].rate;
for (i = 1; i < ARRAY_SIZE(mclk_ratios); i++)
if (params_rate(params) <= mclk_ratios[i].rate)
ratio = mclk_ratios[i].ratio;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
es8328->playback_fs = params_rate(params);
es8328_set_deemph(codec);
}
return snd_soc_update_bits(codec, reg, ES8328_RATEMASK, ratio);
}
static int es8328_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec);
int clk_rate;
u8 mode = ES8328_DACCONTROL1_DACWL_16;
/* set master/slave audio interface */
if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBM_CFM)
return -EINVAL;
/* interface format */
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
mode |= ES8328_DACCONTROL1_DACFORMAT_I2S;
break;
case SND_SOC_DAIFMT_RIGHT_J:
mode |= ES8328_DACCONTROL1_DACFORMAT_RJUST;
break;
case SND_SOC_DAIFMT_LEFT_J:
mode |= ES8328_DACCONTROL1_DACFORMAT_LJUST;
break;
default:
return -EINVAL;
}
/* clock inversion */
if ((fmt & SND_SOC_DAIFMT_INV_MASK) != SND_SOC_DAIFMT_NB_NF)
return -EINVAL;
snd_soc_write(codec, ES8328_DACCONTROL1, mode);
snd_soc_write(codec, ES8328_ADCCONTROL4, mode);
/* Master serial port mode, with BCLK generated automatically */
clk_rate = clk_get_rate(es8328->clk);
if (clk_rate == ES8328_SYSCLK_RATE_1X)
snd_soc_write(codec, ES8328_MASTERMODE,
ES8328_MASTERMODE_MSC);
else
snd_soc_write(codec, ES8328_MASTERMODE,
ES8328_MASTERMODE_MCLKDIV2 |
ES8328_MASTERMODE_MSC);
return 0;
}
static int es8328_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
switch (level) {
case SND_SOC_BIAS_ON:
break;
case SND_SOC_BIAS_PREPARE:
/* VREF, VMID=2x50k, digital enabled */
snd_soc_write(codec, ES8328_CHIPPOWER, 0);
snd_soc_update_bits(codec, ES8328_CONTROL1,
ES8328_CONTROL1_VMIDSEL_MASK |
ES8328_CONTROL1_ENREF,
ES8328_CONTROL1_VMIDSEL_50k |
ES8328_CONTROL1_ENREF);
break;
case SND_SOC_BIAS_STANDBY:
if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
snd_soc_update_bits(codec, ES8328_CONTROL1,
ES8328_CONTROL1_VMIDSEL_MASK |
ES8328_CONTROL1_ENREF,
ES8328_CONTROL1_VMIDSEL_5k |
ES8328_CONTROL1_ENREF);
/* Charge caps */
msleep(100);
}
snd_soc_write(codec, ES8328_CONTROL2,
ES8328_CONTROL2_OVERCURRENT_ON |
ES8328_CONTROL2_THERMAL_SHUTDOWN_ON);
/* VREF, VMID=2*500k, digital stopped */
snd_soc_update_bits(codec, ES8328_CONTROL1,
ES8328_CONTROL1_VMIDSEL_MASK |
ES8328_CONTROL1_ENREF,
ES8328_CONTROL1_VMIDSEL_500k |
ES8328_CONTROL1_ENREF);
break;
case SND_SOC_BIAS_OFF:
snd_soc_update_bits(codec, ES8328_CONTROL1,
ES8328_CONTROL1_VMIDSEL_MASK |
ES8328_CONTROL1_ENREF,
0);
break;
}
codec->dapm.bias_level = level;
return 0;
}
static const struct snd_soc_dai_ops es8328_dai_ops = {
.hw_params = es8328_hw_params,
.digital_mute = es8328_mute,
.set_fmt = es8328_set_dai_fmt,
};
static struct snd_soc_dai_driver es8328_dai = {
.name = "es8328-hifi-analog",
.playback = {
.stream_name = "Playback",
.channels_min = 2,
.channels_max = 2,
.rates = ES8328_RATES,
.formats = ES8328_FORMATS,
},
.capture = {
.stream_name = "Capture",
.channels_min = 2,
.channels_max = 2,
.rates = ES8328_RATES,
.formats = ES8328_FORMATS,
},
.ops = &es8328_dai_ops,
};
static int es8328_suspend(struct snd_soc_codec *codec)
{
struct es8328_priv *es8328;
int ret;
es8328 = snd_soc_codec_get_drvdata(codec);
clk_disable_unprepare(es8328->clk);
ret = regulator_bulk_disable(ARRAY_SIZE(es8328->supplies),
es8328->supplies);
if (ret) {
dev_err(codec->dev, "unable to disable regulators\n");
return ret;
}
return 0;
}
static int es8328_resume(struct snd_soc_codec *codec)
{
struct regmap *regmap = dev_get_regmap(codec->dev, NULL);
struct es8328_priv *es8328;
int ret;
es8328 = snd_soc_codec_get_drvdata(codec);
ret = clk_prepare_enable(es8328->clk);
if (ret) {
dev_err(codec->dev, "unable to enable clock\n");
return ret;
}
ret = regulator_bulk_enable(ARRAY_SIZE(es8328->supplies),
es8328->supplies);
if (ret) {
dev_err(codec->dev, "unable to enable regulators\n");
return ret;
}
regcache_mark_dirty(regmap);
ret = regcache_sync(regmap);
if (ret) {
dev_err(codec->dev, "unable to sync regcache\n");
return ret;
}
return 0;
}
static int es8328_codec_probe(struct snd_soc_codec *codec)
{
struct es8328_priv *es8328;
int ret;
es8328 = snd_soc_codec_get_drvdata(codec);
ret = regulator_bulk_enable(ARRAY_SIZE(es8328->supplies),
es8328->supplies);
if (ret) {
dev_err(codec->dev, "unable to enable regulators\n");
return ret;
}
/* Setup clocks */
es8328->clk = devm_clk_get(codec->dev, NULL);
if (IS_ERR(es8328->clk)) {
dev_err(codec->dev, "codec clock missing or invalid\n");
ret = PTR_ERR(es8328->clk);
goto clk_fail;
}
ret = clk_prepare_enable(es8328->clk);
if (ret) {
dev_err(codec->dev, "unable to prepare codec clk\n");
goto clk_fail;
}
return 0;
clk_fail:
regulator_bulk_disable(ARRAY_SIZE(es8328->supplies),
es8328->supplies);
return ret;
}
static int es8328_remove(struct snd_soc_codec *codec)
{
struct es8328_priv *es8328;
es8328 = snd_soc_codec_get_drvdata(codec);
if (es8328->clk)
clk_disable_unprepare(es8328->clk);
regulator_bulk_disable(ARRAY_SIZE(es8328->supplies),
es8328->supplies);
return 0;
}
const struct regmap_config es8328_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = ES8328_REG_MAX,
.cache_type = REGCACHE_RBTREE,
};
EXPORT_SYMBOL_GPL(es8328_regmap_config);
static struct snd_soc_codec_driver es8328_codec_driver = {
.probe = es8328_codec_probe,
.suspend = es8328_suspend,
.resume = es8328_resume,
.remove = es8328_remove,
.set_bias_level = es8328_set_bias_level,
.suspend_bias_off = true,
.controls = es8328_snd_controls,
.num_controls = ARRAY_SIZE(es8328_snd_controls),
.dapm_widgets = es8328_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(es8328_dapm_widgets),
.dapm_routes = es8328_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(es8328_dapm_routes),
};
int es8328_probe(struct device *dev, struct regmap *regmap)
{
struct es8328_priv *es8328;
int ret;
int i;
if (IS_ERR(regmap))
return PTR_ERR(regmap);
es8328 = devm_kzalloc(dev, sizeof(*es8328), GFP_KERNEL);
if (es8328 == NULL)
return -ENOMEM;
es8328->regmap = regmap;
for (i = 0; i < ARRAY_SIZE(es8328->supplies); i++)
es8328->supplies[i].supply = supply_names[i];
ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(es8328->supplies),
es8328->supplies);
if (ret) {
dev_err(dev, "unable to get regulators\n");
return ret;
}
dev_set_drvdata(dev, es8328);
return snd_soc_register_codec(dev,
&es8328_codec_driver, &es8328_dai, 1);
}
EXPORT_SYMBOL_GPL(es8328_probe);
MODULE_DESCRIPTION("ASoC ES8328 driver");
MODULE_AUTHOR("Sean Cross <xobs@kosagi.com>");
MODULE_LICENSE("GPL");

314
sound/soc/codecs/es8328.h Normal file
View file

@ -0,0 +1,314 @@
/*
* es8328.h -- ES8328 ALSA SoC Audio driver
*/
#ifndef _ES8328_H
#define _ES8328_H
#include <linux/regmap.h>
struct device;
extern const struct regmap_config es8328_regmap_config;
int es8328_probe(struct device *dev, struct regmap *regmap);
#define ES8328_DACLVOL 46
#define ES8328_DACRVOL 47
#define ES8328_DACCTL 28
#define ES8328_RATEMASK (0x1f << 0)
#define ES8328_CONTROL1 0x00
#define ES8328_CONTROL1_VMIDSEL_OFF (0 << 0)
#define ES8328_CONTROL1_VMIDSEL_50k (1 << 0)
#define ES8328_CONTROL1_VMIDSEL_500k (2 << 0)
#define ES8328_CONTROL1_VMIDSEL_5k (3 << 0)
#define ES8328_CONTROL1_VMIDSEL_MASK (7 << 0)
#define ES8328_CONTROL1_ENREF (1 << 2)
#define ES8328_CONTROL1_SEQEN (1 << 3)
#define ES8328_CONTROL1_SAMEFS (1 << 4)
#define ES8328_CONTROL1_DACMCLK_ADC (0 << 5)
#define ES8328_CONTROL1_DACMCLK_DAC (1 << 5)
#define ES8328_CONTROL1_LRCM (1 << 6)
#define ES8328_CONTROL1_SCP_RESET (1 << 7)
#define ES8328_CONTROL2 0x01
#define ES8328_CONTROL2_VREF_BUF_OFF (1 << 0)
#define ES8328_CONTROL2_VREF_LOWPOWER (1 << 1)
#define ES8328_CONTROL2_IBIASGEN_OFF (1 << 2)
#define ES8328_CONTROL2_ANALOG_OFF (1 << 3)
#define ES8328_CONTROL2_VREF_BUF_LOWPOWER (1 << 4)
#define ES8328_CONTROL2_VCM_MOD_LOWPOWER (1 << 5)
#define ES8328_CONTROL2_OVERCURRENT_ON (1 << 6)
#define ES8328_CONTROL2_THERMAL_SHUTDOWN_ON (1 << 7)
#define ES8328_CHIPPOWER 0x02
#define ES8328_CHIPPOWER_DACVREF_OFF 0
#define ES8328_CHIPPOWER_ADCVREF_OFF 1
#define ES8328_CHIPPOWER_DACDLL_OFF 2
#define ES8328_CHIPPOWER_ADCDLL_OFF 3
#define ES8328_CHIPPOWER_DACSTM_RESET 4
#define ES8328_CHIPPOWER_ADCSTM_RESET 5
#define ES8328_CHIPPOWER_DACDIG_OFF 6
#define ES8328_CHIPPOWER_ADCDIG_OFF 7
#define ES8328_ADCPOWER 0x03
#define ES8328_ADCPOWER_INT1_LOWPOWER 0
#define ES8328_ADCPOWER_FLASH_ADC_LOWPOWER 1
#define ES8328_ADCPOWER_ADC_BIAS_GEN_OFF 2
#define ES8328_ADCPOWER_MIC_BIAS_OFF 3
#define ES8328_ADCPOWER_ADCR_OFF 4
#define ES8328_ADCPOWER_ADCL_OFF 5
#define ES8328_ADCPOWER_AINR_OFF 6
#define ES8328_ADCPOWER_AINL_OFF 7
#define ES8328_DACPOWER 0x04
#define ES8328_DACPOWER_OUT3_ON 0
#define ES8328_DACPOWER_MONO_ON 1
#define ES8328_DACPOWER_ROUT2_ON 2
#define ES8328_DACPOWER_LOUT2_ON 3
#define ES8328_DACPOWER_ROUT1_ON 4
#define ES8328_DACPOWER_LOUT1_ON 5
#define ES8328_DACPOWER_RDAC_OFF 6
#define ES8328_DACPOWER_LDAC_OFF 7
#define ES8328_CHIPLOPOW1 0x05
#define ES8328_CHIPLOPOW2 0x06
#define ES8328_ANAVOLMANAG 0x07
#define ES8328_MASTERMODE 0x08
#define ES8328_MASTERMODE_BCLKDIV (0 << 0)
#define ES8328_MASTERMODE_BCLK_INV (1 << 5)
#define ES8328_MASTERMODE_MCLKDIV2 (1 << 6)
#define ES8328_MASTERMODE_MSC (1 << 7)
#define ES8328_ADCCONTROL1 0x09
#define ES8328_ADCCONTROL2 0x0a
#define ES8328_ADCCONTROL3 0x0b
#define ES8328_ADCCONTROL4 0x0c
#define ES8328_ADCCONTROL5 0x0d
#define ES8328_ADCCONTROL5_RATEMASK (0x1f << 0)
#define ES8328_ADCCONTROL6 0x0e
#define ES8328_ADCCONTROL7 0x0f
#define ES8328_ADCCONTROL7_ADC_MUTE (1 << 2)
#define ES8328_ADCCONTROL7_ADC_LER (1 << 3)
#define ES8328_ADCCONTROL7_ADC_ZERO_CROSS (1 << 4)
#define ES8328_ADCCONTROL7_ADC_SOFT_RAMP (1 << 5)
#define ES8328_ADCCONTROL7_ADC_RAMP_RATE_4 (0 << 6)
#define ES8328_ADCCONTROL7_ADC_RAMP_RATE_8 (1 << 6)
#define ES8328_ADCCONTROL7_ADC_RAMP_RATE_16 (2 << 6)
#define ES8328_ADCCONTROL7_ADC_RAMP_RATE_32 (3 << 6)
#define ES8328_ADCCONTROL8 0x10
#define ES8328_ADCCONTROL9 0x11
#define ES8328_ADCCONTROL10 0x12
#define ES8328_ADCCONTROL11 0x13
#define ES8328_ADCCONTROL12 0x14
#define ES8328_ADCCONTROL13 0x15
#define ES8328_ADCCONTROL14 0x16
#define ES8328_DACCONTROL1 0x17
#define ES8328_DACCONTROL1_DACFORMAT_I2S (0 << 1)
#define ES8328_DACCONTROL1_DACFORMAT_LJUST (1 << 1)
#define ES8328_DACCONTROL1_DACFORMAT_RJUST (2 << 1)
#define ES8328_DACCONTROL1_DACFORMAT_PCM (3 << 1)
#define ES8328_DACCONTROL1_DACWL_24 (0 << 3)
#define ES8328_DACCONTROL1_DACWL_20 (1 << 3)
#define ES8328_DACCONTROL1_DACWL_18 (2 << 3)
#define ES8328_DACCONTROL1_DACWL_16 (3 << 3)
#define ES8328_DACCONTROL1_DACWL_32 (4 << 3)
#define ES8328_DACCONTROL1_DACLRP_I2S_POL_NORMAL (0 << 6)
#define ES8328_DACCONTROL1_DACLRP_I2S_POL_INV (1 << 6)
#define ES8328_DACCONTROL1_DACLRP_PCM_MSB_CLK2 (0 << 6)
#define ES8328_DACCONTROL1_DACLRP_PCM_MSB_CLK1 (1 << 6)
#define ES8328_DACCONTROL1_LRSWAP (1 << 7)
#define ES8328_DACCONTROL2 0x18
#define ES8328_DACCONTROL2_RATEMASK (0x1f << 0)
#define ES8328_DACCONTROL2_DOUBLESPEED (1 << 5)
#define ES8328_DACCONTROL3 0x19
#define ES8328_DACCONTROL3_AUTOMUTE (1 << 2)
#define ES8328_DACCONTROL3_DACMUTE (1 << 2)
#define ES8328_DACCONTROL3_LEFTGAINVOL (1 << 3)
#define ES8328_DACCONTROL3_DACZEROCROSS (1 << 4)
#define ES8328_DACCONTROL3_DACSOFTRAMP (1 << 5)
#define ES8328_DACCONTROL3_DACRAMPRATE (3 << 6)
#define ES8328_LDACVOL 0x1a
#define ES8328_LDACVOL_MASK (0 << 0)
#define ES8328_LDACVOL_MAX (0xc0)
#define ES8328_RDACVOL 0x1b
#define ES8328_RDACVOL_MASK (0 << 0)
#define ES8328_RDACVOL_MAX (0xc0)
#define ES8328_DACVOL_MAX (0xc0)
#define ES8328_DACCONTROL4 0x1a
#define ES8328_DACCONTROL5 0x1b
#define ES8328_DACCONTROL6 0x1c
#define ES8328_DACCONTROL6_CLICKFREE (1 << 3)
#define ES8328_DACCONTROL6_DAC_INVR (1 << 4)
#define ES8328_DACCONTROL6_DAC_INVL (1 << 5)
#define ES8328_DACCONTROL6_DEEMPH_OFF (0 << 6)
#define ES8328_DACCONTROL6_DEEMPH_32k (1 << 6)
#define ES8328_DACCONTROL6_DEEMPH_44_1k (2 << 6)
#define ES8328_DACCONTROL6_DEEMPH_48k (3 << 6)
#define ES8328_DACCONTROL7 0x1d
#define ES8328_DACCONTROL7_VPP_SCALE_3p5 (0 << 0)
#define ES8328_DACCONTROL7_VPP_SCALE_4p0 (1 << 0)
#define ES8328_DACCONTROL7_VPP_SCALE_3p0 (2 << 0)
#define ES8328_DACCONTROL7_VPP_SCALE_2p5 (3 << 0)
#define ES8328_DACCONTROL7_SHELVING_STRENGTH (1 << 2) /* In eights */
#define ES8328_DACCONTROL7_MONO (1 << 5)
#define ES8328_DACCONTROL7_ZEROR (1 << 6)
#define ES8328_DACCONTROL7_ZEROL (1 << 7)
/* Shelving filter */
#define ES8328_DACCONTROL8 0x1e
#define ES8328_DACCONTROL9 0x1f
#define ES8328_DACCONTROL10 0x20
#define ES8328_DACCONTROL11 0x21
#define ES8328_DACCONTROL12 0x22
#define ES8328_DACCONTROL13 0x23
#define ES8328_DACCONTROL14 0x24
#define ES8328_DACCONTROL15 0x25
#define ES8328_DACCONTROL16 0x26
#define ES8328_DACCONTROL16_RMIXSEL_RIN1 (0 << 0)
#define ES8328_DACCONTROL16_RMIXSEL_RIN2 (1 << 0)
#define ES8328_DACCONTROL16_RMIXSEL_RIN3 (2 << 0)
#define ES8328_DACCONTROL16_RMIXSEL_RADC (3 << 0)
#define ES8328_DACCONTROL16_LMIXSEL_LIN1 (0 << 3)
#define ES8328_DACCONTROL16_LMIXSEL_LIN2 (1 << 3)
#define ES8328_DACCONTROL16_LMIXSEL_LIN3 (2 << 3)
#define ES8328_DACCONTROL16_LMIXSEL_LADC (3 << 3)
#define ES8328_DACCONTROL17 0x27
#define ES8328_DACCONTROL17_LI2LOVOL (7 << 3)
#define ES8328_DACCONTROL17_LI2LO (1 << 6)
#define ES8328_DACCONTROL17_LD2LO (1 << 7)
#define ES8328_DACCONTROL18 0x28
#define ES8328_DACCONTROL18_RI2LOVOL (7 << 3)
#define ES8328_DACCONTROL18_RI2LO (1 << 6)
#define ES8328_DACCONTROL18_RD2LO (1 << 7)
#define ES8328_DACCONTROL19 0x29
#define ES8328_DACCONTROL19_LI2ROVOL (7 << 3)
#define ES8328_DACCONTROL19_LI2RO (1 << 6)
#define ES8328_DACCONTROL19_LD2RO (1 << 7)
#define ES8328_DACCONTROL20 0x2a
#define ES8328_DACCONTROL20_RI2ROVOL (7 << 3)
#define ES8328_DACCONTROL20_RI2RO (1 << 6)
#define ES8328_DACCONTROL20_RD2RO (1 << 7)
#define ES8328_DACCONTROL21 0x2b
#define ES8328_DACCONTROL21_LI2MOVOL (7 << 3)
#define ES8328_DACCONTROL21_LI2MO (1 << 6)
#define ES8328_DACCONTROL21_LD2MO (1 << 7)
#define ES8328_DACCONTROL22 0x2c
#define ES8328_DACCONTROL22_RI2MOVOL (7 << 3)
#define ES8328_DACCONTROL22_RI2MO (1 << 6)
#define ES8328_DACCONTROL22_RD2MO (1 << 7)
#define ES8328_DACCONTROL23 0x2d
#define ES8328_DACCONTROL23_MOUTINV (1 << 1)
#define ES8328_DACCONTROL23_HPSWPOL (1 << 2)
#define ES8328_DACCONTROL23_HPSWEN (1 << 3)
#define ES8328_DACCONTROL23_VROI_1p5k (0 << 4)
#define ES8328_DACCONTROL23_VROI_40k (1 << 4)
#define ES8328_DACCONTROL23_OUT3_VREF (0 << 5)
#define ES8328_DACCONTROL23_OUT3_ROUT1 (1 << 5)
#define ES8328_DACCONTROL23_OUT3_MONOOUT (2 << 5)
#define ES8328_DACCONTROL23_OUT3_RIGHT_MIXER (3 << 5)
#define ES8328_DACCONTROL23_ROUT2INV (1 << 7)
/* LOUT1 Amplifier */
#define ES8328_LOUT1VOL 0x2e
#define ES8328_LOUT1VOL_MASK (0 << 5)
#define ES8328_LOUT1VOL_MAX (0x24)
/* ROUT1 Amplifier */
#define ES8328_ROUT1VOL 0x2f
#define ES8328_ROUT1VOL_MASK (0 << 5)
#define ES8328_ROUT1VOL_MAX (0x24)
#define ES8328_OUT1VOL_MAX (0x24)
/* LOUT2 Amplifier */
#define ES8328_LOUT2VOL 0x30
#define ES8328_LOUT2VOL_MASK (0 << 5)
#define ES8328_LOUT2VOL_MAX (0x24)
/* ROUT2 Amplifier */
#define ES8328_ROUT2VOL 0x31
#define ES8328_ROUT2VOL_MASK (0 << 5)
#define ES8328_ROUT2VOL_MAX (0x24)
#define ES8328_OUT2VOL_MAX (0x24)
/* Mono Out Amplifier */
#define ES8328_MONOOUTVOL 0x32
#define ES8328_MONOOUTVOL_MASK (0 << 5)
#define ES8328_MONOOUTVOL_MAX (0x24)
#define ES8328_DACCONTROL29 0x33
#define ES8328_DACCONTROL30 0x34
#define ES8328_SYSCLK 0
#define ES8328_REG_MAX 0x35
#define ES8328_PLL1 0
#define ES8328_PLL2 1
/* clock inputs */
#define ES8328_MCLK 0
#define ES8328_PCMCLK 1
/* clock divider id's */
#define ES8328_PCMDIV 0
#define ES8328_BCLKDIV 1
#define ES8328_VXCLKDIV 2
/* PCM clock dividers */
#define ES8328_PCM_DIV_1 (0 << 6)
#define ES8328_PCM_DIV_3 (2 << 6)
#define ES8328_PCM_DIV_5_5 (3 << 6)
#define ES8328_PCM_DIV_2 (4 << 6)
#define ES8328_PCM_DIV_4 (5 << 6)
#define ES8328_PCM_DIV_6 (6 << 6)
#define ES8328_PCM_DIV_8 (7 << 6)
/* BCLK clock dividers */
#define ES8328_BCLK_DIV_1 (0 << 7)
#define ES8328_BCLK_DIV_2 (1 << 7)
#define ES8328_BCLK_DIV_4 (2 << 7)
#define ES8328_BCLK_DIV_8 (3 << 7)
/* VXCLK clock dividers */
#define ES8328_VXCLK_DIV_1 (0 << 6)
#define ES8328_VXCLK_DIV_2 (1 << 6)
#define ES8328_VXCLK_DIV_4 (2 << 6)
#define ES8328_VXCLK_DIV_8 (3 << 6)
#define ES8328_VXCLK_DIV_16 (4 << 6)
#define ES8328_DAI_HIFI 0
#define ES8328_DAI_VOICE 1
#define ES8328_1536FS 1536
#define ES8328_1024FS 1024
#define ES8328_768FS 768
#define ES8328_512FS 512
#define ES8328_384FS 384
#define ES8328_256FS 256
#define ES8328_128FS 128
#endif

View file

@ -0,0 +1,356 @@
/**
* Copyright 2015 Samsung Electronics Co. Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef _AUDMIXER_REGISTERS_H
#define _AUDMIXER_REGISTERS_H
/**
* The register offsets in APB-based Audio Mixers are 4 times those in I2C-based
* Audio Mixers. To handle this, we have used SoC type based multiplier.
* TODO: Need to find a better way to do this.
*/
#ifdef CONFIG_SOC_EXYNOS7580
#define AUDMIXER_REG_MULT 1
#else
#define AUDMIXER_REG_MULT 4
#endif
/**
* Register addresses
*/
#define AUDMIXER_REG_00_SOFT_RSTB (0x00 * AUDMIXER_REG_MULT)
#define AUDMIXER_REG_01_IN1_CTL1 (0x01 * AUDMIXER_REG_MULT)
#define AUDMIXER_REG_02_IN1_CTL2 (0x02 * AUDMIXER_REG_MULT)
#define AUDMIXER_REG_03_IN1_CTL3 (0x03 * AUDMIXER_REG_MULT)
#define AUDMIXER_REG_04_IN2_CTL1 (0x04 * AUDMIXER_REG_MULT)
#define AUDMIXER_REG_05_IN2_CTL2 (0x05 * AUDMIXER_REG_MULT)
#define AUDMIXER_REG_06_IN2_CTL3 (0x06 * AUDMIXER_REG_MULT)
#define AUDMIXER_REG_07_IN3_CTL1 (0x07 * AUDMIXER_REG_MULT)
#define AUDMIXER_REG_08_IN3_CTL2 (0x08 * AUDMIXER_REG_MULT)
#define AUDMIXER_REG_09_IN3_CTL3 (0x09 * AUDMIXER_REG_MULT)
#define AUDMIXER_REG_0A_HQ_CTL (0x0a * AUDMIXER_REG_MULT)
#define AUDMIXER_REG_0B_SLOT_L (0x0b * AUDMIXER_REG_MULT)
#define AUDMIXER_REG_0C_SLOT_R (0x0c * AUDMIXER_REG_MULT)
#define AUDMIXER_REG_0D_RMIX_CTL (0x0d * AUDMIXER_REG_MULT)
#define AUDMIXER_REG_0E_TSLOT (0x0e * AUDMIXER_REG_MULT)
#define AUDMIXER_REG_0F_DIG_EN (0x0f * AUDMIXER_REG_MULT)
#define AUDMIXER_REG_10_DMIX1 (0x10 * AUDMIXER_REG_MULT)
#define AUDMIXER_REG_11_DMIX2 (0x11 * AUDMIXER_REG_MULT)
#define AUDMIXER_REG_16_DOUTMX1 (0x16 * AUDMIXER_REG_MULT)
#define AUDMIXER_REG_17_DOUTMX2 (0x17 * AUDMIXER_REG_MULT)
#define AUDMIXER_REG_18_INAMP_CTL (0x18 * AUDMIXER_REG_MULT)
#define AUDMIXER_REG_19_OUTAP1_CTL (0x19 * AUDMIXER_REG_MULT)
#define AUDMIXER_REG_1A_OUTCP1_CTL (0x1a * AUDMIXER_REG_MULT)
#define AUDMIXER_REG_68_ALC_CTL (0x68 * AUDMIXER_REG_MULT)
#define AUDMIXER_REG_69_ALC_GA1 (0x69 * AUDMIXER_REG_MULT)
#define AUDMIXER_REG_6A_ALC_GA2 (0x6a * AUDMIXER_REG_MULT)
#define AUDMIXER_REG_6B_ALC_LVL (0x6b * AUDMIXER_REG_MULT)
#define AUDMIXER_REG_6C_ALC_LVR (0x6c * AUDMIXER_REG_MULT)
#define AUDMIXER_REG_6D_ALC_HLD (0x6d * AUDMIXER_REG_MULT)
#define AUDMIXER_REG_6E_ALC_ATK (0x6e * AUDMIXER_REG_MULT)
#define AUDMIXER_REG_6F_ALC_DCY (0x6f * AUDMIXER_REG_MULT)
#define AUDMIXER_REG_70_ALC_NG (0x70 * AUDMIXER_REG_MULT)
#define AUDMIXER_REG_71_ALC_SGL (0x71 * AUDMIXER_REG_MULT)
#define AUDMIXER_REG_72_ALC_SGR (0x72 * AUDMIXER_REG_MULT)
#define AUDMIXER_MAX_REGISTER AUDMIXER_REG_72_ALC_SGR
/**
* Description of bit-fields of various registers
*/
/* AUDMIXER_REG_00_SOFT_RSTB */
#define SOFT_RSTB_DATA_RSTB_SHIFT 1
#define SOFT_RSTB_SYS_RSTB_SHIFT 0
/**
* AUDMIXER_REG_01_IN1_CTL1
* AUDMIXER_REG_04_IN2_CTL1
* AUDMIXER_REG_07_IN3_CTL1
*/
#define INCTL1_MPCM_SRATE_SHIFT 5
#define INCTL1_MPCM_SRATE_MASK 0x3
#define INCTL1_MASTER_SHIFT 4
#define INCTL1_MPCM_SLOT_SHIFT 2
#define INCTL1_MPCM_SLOT_MASK 0x3
#define INCTL1_BCK_POL_SHIFT 1
#define INCTL1_I2S_PCM_SHIFT 0
#define INCTL1_BT_OSYNC_POL_MASK 1
#define INCTL1_BT_OSYNC_POL_SHIFT 2
#define INCTL1_ISYNC_POL_MASK 1
#define INCTL1_ISYNC_POL_SHIFT 1
#define INCTL1_FM_BT_SEL_MASK 1
#define INCTL1_FM_BT_SEL_SHIFT 0
#define MPCM_SRATE_32KHZ 0x3
#define MPCM_SRATE_24KHZ 0x2
#define MPCM_SRATE_16KHZ 0x1
#define MPCM_SRATE_8KHZ 0x0
#define MIXER_MASTER 1
#define MIXER_SLAVE 0
#define MPCM_SLOT_64BCK 3
#define MPCM_SLOT_48BCK 2
#define MPCM_SLOT_32BCK 1
#define MPCM_SLOT_16BCK 0
#define BCK_POL_INVERTED 1
#define BCK_POL_NORMAL 0
#define I2S_PCM_MODE_PCM 1
#define I2S_PCM_MODE_I2S 0
/* AUDMIXER_REG_02_IN1_CTL2 */
/* AUDMIXER_REG_05_IN2_CTL2 */
/* AUDMIXER_REG_08_IN3_CTL2 */
#define INCTL2_I2S_XFS_SHIFT 5
#define INCTL2_I2S_XFS_MASK 0x3
#define INCTL2_LRCK_POL_SHIFT 4
#define INCTL2_I2S_DF_SHIFT 2
#define INCTL2_I2S_DF_MASK 0x3
#define INCTL2_I2S_DL_SHIFT 0
#define INCTL2_I2S_DL_MASK 0x3
#define I2S_XFS_64FS 2
#define I2S_XFS_48FS 1
#define I2S_XFS_32FS 0
#define LRCLK_POL_RIGHT 1
#define LRCLK_POL_LEFT 0
#define I2S_DF_RJ 2
#define I2S_DF_LJ 1
#define I2S_DF_I2S 0
#define I2S_DL_24BIT 3
#define I2S_DL_20BIT 2
#define I2S_DL_18BIT 1
#define I2S_DL_16BIT 0
/* AUDMIXER_REG_03_IN1_CTL3 */
/* AUDMIXER_REG_06_IN2_CTL3 */
/* AUDMIXER_REG_09_IN3_CTL3 */
#define INCTL3_PCM_DAD_SHIFT 5
#define INCTL3_PCM_DAD_MASK 0x3
#define INCTL3_PCM_DF_SHIFT 0
#define INCTL3_PCM_DF_MASK 0xf
#define PCM_DAD_4BCK 6
#define PCM_DAD_3BCK 4
#define PCM_DAD_2BCK 2
#define PCM_DAD_0BCK 1
#define PCM_DAD_1BCK 0
#define PCM_DF_SHORT_FRAME 4
#define PCM_DF_LONG_FRAME 0xc
/* AUDMIXER_REG_0A_HQ_CTL */
#define HQ_CTL_CH3_SEL_SHIFT 3
#define HQ_CTL_MCKO_EN_SHIFT 2
#define HQ_CTL_BCK4_MODE_SHIFT 1
#define HQ_CTL_HQ_EN_SHIFT 0
#define HQ_CTL_HQ_EN_MASK BIT(HQ_CTL_HQ_EN_SHIFT)
/* AUDMIXER_REG_0B_SLOT_L */
#define SLOT_L_SEL_SHIFT 0
#define SLOT_L_SEL_MASK 0xf
/* AUDMIXER_REG_0C_SLOT_R */
#define SLOT_R_SEL_SHIFT 0
#define SLOT_R_SEL_MASK 0xf
#define SLOT_SEL_1ST_SLOT 1
#define SLOT_SEL_2ND_SLOT 2
#define SLOT_SEL_3RD_SLOT 4
#define SLOT_SEL_4TH_SLOT 8
/* AUDMIXER_REG_0D_RMIX_CTL */
#define RMIX_CTL_RMIX2_EN_SHIFT 7
#define RMIX_CTL_RMIX2_LVL_SHIFT 4
#define RMIX_CTL_RMIX2_LVL_WIDTH 3
#define RMIX_CTL_RMIX2_LVL_MASK 0x7
#define RMIX_CTL_RMIX1_EN_SHIFT 3
#define RMIX_CTL_RMIX1_LVL_SHIFT 0
#define RMIX_CTL_RMIX1_LVL_WIDTH 3
#define RMIX_CTL_RMIX1_LVL_MASK 0x7
/* AUDMIXER_REG_0E_TSLOT */
#define TSLOT_SLOT_SHIFT 0
#define TSLOT_SLOT_MASK 0x3
#define TSLOT_USED_4 3
#define TSLOT_USED_3 2
#define TSLOT_USED_2 1
#define TSLOT_USED_1 0
/* AUDMIXER_REG_0F_DIG_EN */
#define DIG_EN_CP1_EN_SHIFT 5
#define DIG_EN_AP1_EN_SHIFT 4
#define DIG_EN_MIX_EN_SHIFT 3
#define DIG_EN_SRC3_EN_SHIFT 2
#define DIG_EN_SRC2_EN_SHIFT 1
#define DIG_EN_SRC1_EN_SHIFT 0
#define DIG_EN_AP0_EN_SHIFT 0
#define DIG_EN_MASK 1
/* AUDMIXER_REG_10_DMIX1 */
#define DMIX1_MIX_EN2_SHIFT 7
#define DMIX1_MIX_LVL2_SHIFT 4
#define DMIX1_MIX_LVL2_WIDTH 3
#define DMIX1_MIX_LVL2_MASK 0x7
#define DMIX1_MIX_EN1_SHIFT 3
#define DMIX1_MIX_LVL1_SHIFT 0
#define DMIX1_MIX_LVL1_WIDTH 3
#define DMIX1_MIX_LVL1_MASK 0x7
/* AUDMIXER_REG_11_DMIX2 */
#define DMIX2_MIX_EN4_SHIFT 7
#define DMIX2_MIX_LVL4_SHIFT 4
#define DMIX2_MIX_LVL4_WIDTH 3
#define DMIX2_MIX_LVL4_MASK 0x7
#define DMIX2_MIX_EN3_SHIFT 3
#define DMIX2_MIX_LVL3_SHIFT 0
#define DMIX2_MIX_LVL3_WIDTH 3
#define DMIX2_MIX_LVL3_MASK 0x7
#define DMIX_EN_MASK 0x1
/* AUDMIXER_REG_16_DOUTMX1 */
#define DOUTMX1_DOUT_SEL2_SHIFT 3
#define DOUTMX1_DOUT_SEL2_MASK 0x7
#define DOUTMX1_DOUT_SEL1_SHIFT 0
#define DOUTMX1_DOUT_SEL1_MASK 0x7
#define DOUT_SEL2_BT_DATA 2
#define DOUT_SEL2_ADC_DATA 1
#define DOUT_SEL2_MIX_DATA 0
#define DOUT_SEL1_ADC_CP_DATA 2
#define DOUT_SEL1_ADC_DATA 1
#define DOUT_SEL1_MIX_DATA 0
/* AUDMIXER_REG_17_DOUTMX2 */
#define DOUTMX2_DOUT_SEL3_SHIFT 0
#define DOUTMX2_DOUT_SEL3_MASK 0x7
#define DOUT_SEL3_CP_DATA 2
#define DOUT_SEL3_ADC_DATA 1
#define DOUT_SEL3_MIX_DATA 0
/* AUDMIXER_REG_18_INAMP_CTL */
#define INAMP_CTL_BCK_POL_SHIFT 7
#define INAMP_CTL_BCK_POL_MASK 1
#define INAMP_CTL_LRCLK_POL_SHIFT 6
#define INAMP_CTL_LRCLK_POL_MASK 1
#define INAMP_CTL_I2S_XFS_SHIFT 2
#define INAMP_CTL_I2S_XFS_MASK 0x3
#define INAMP_CTL_I2S_DL_SHIFT 0
#define INAMP_CTL_I2S_DL_MASK 0x3
/* AUDMIXER_REG_19_OUTAP1_CTL */
/* AUDMIXER_REG_1A_OUTCP1_CTL */
#define OUTAMP_CTL_LRCLK_POL_SHIFT 7
#define OUTAMP_CTL_LRCLK_POL_MASK 1
#define OUTAMP_CTL_BCK_POL_SHIFT 6
#define OUTAMP_CTL_BCK_POL_MASK 1
#define OUTAMP_CTL_I2S_DL_SHIFT 4
#define OUTAMP_CTL_I2S_DL_MASK 0x3
#define OUTAMP_CTL_I2S_XFS_SHIFT 0
#define OUTAMP_CTL_I2S_XFS_MASK 0x3
#define AMP_I2S_DL_24BIT 2
#define AMP_I2S_DL_20BIT 1
#define AMP_I2S_DL_16BIT 0
/* AUDMIXER_REG_68_ALC_CTL */
#define ALC_CTL_ALC_NG_HYS_SHIFT 6
#define ALC_CTL_ALC_NG_HYS_WIDTH 2
#define ALC_CTL_ALC_NG_HYS_MASK 0x3
#define ALC_CTL_WINSEL_SHIFT 4
#define ALC_CTL_WINSEL_MASK 0x3
#define ALC_CTL_ALC_EN_SHIFT 3
#define ALC_CTL_ALC_LIM_SHIFT 2
#define ALC_CTL_ALC_MODE_SHIFT 0
#define ALC_CTL_ALC_MODE_MASK 0x3
#define WINSEL_300FS 3
#define WINSEL_2400FS 2
#define WINSEL_1200FS 1
#define WINSEL_600FS 0
#define ALC_MODE_RIGHT_LEFT_IND 3
#define ALC_MODE_LEFT_CHAN 2
#define ALC_MODE_RIGHT_CHAN 1
#define ALC_MODE_STEREO 0
/* AUDMIXER_REG_69_ALC_GA1 */
#define ALC_GA1_ALC_MAX_GAIN_SHIFT 0
#define ALC_GA1_ALC_MAX_GAIN_MASK 0xff
#define ALC_GA1_ALC_MAX_GAIN_MAXVAL 0x9c
#define ALC_GA1_ALC_MAX_GAIN_MINVAL 0x6c
/* AUDMIXER_REG_6A_ALC_GA2 */
#define ALC_GA2_ALC_MIN_GAIN_SHIFT 0
#define ALC_GA2_ALC_MIN_GAIN_MASK 0xff
#define ALC_GA2_ALC_MIN_GAIN_MAXVAL 0x6c
#define ALC_GA2_ALC_MIN_GAIN_MINVAL 0x0
/* AUDMIXER_REG_6B_ALC_LVL */
#define ALC_LVL_LVL_SHIFT 0
#define ALC_LVL_LVL_WIDTH 5
#define ALC_LVL_LVL_MASK 0x1f
/* AUDMIXER_REG_6C_ALC_LVR */
#define ALC_LVR_LVL_SHIFT 0
#define ALC_LVR_LVL_WIDTH 5
#define ALC_LVR_LVL_MASK 0x1f
/* AUDMIXER_REG_6D_ALC_HLD */
#define ALC_HLD_ALC_PATH_SEL_SHIFT 7
#define ALC_HLD_ST_GAIN_EN_SHIFT 6
#define ALC_HLD_HOLD_SHIFT 0
#define ALC_HLD_HOLD_MASK 0x1f
/* AUDMIXER_REG_6E_ALC_ATK */
#define ALC_ATK_ATTACK_SHIFT 0
#define ALC_ATK_ATTACK_MASK 0x1f
/* AUDMIXER_REG_6F_ALC_DCY */
#define ALC_DCY_DECAY_SHIFT 0
#define ALC_DCY_DECAY_MASK 0x1f
/* AUDMIXER_REG_70_ALC_NG */
#define ALC_NG_NGAT_SHIFT 7
#define ALC_NG_ALCNGTH_SHIFT 0
#define ALC_NG_ALCNGTH_WIDTH 5
#define ALC_NG_ALCNGTH_MASK 0x1f
/* AUDMIXER_REG_71_ALC_SGL */
#define ALC_SGL_START_GAIN_L_SHIFT 0
#define ALC_SGL_START_GAIN_L_MASK 0xff
#define ALC_SGL_START_GAIN_L_MAXVAL 0x9c
#define ALC_SGL_START_GAIN_L_MINVAL 0x6c
/* AUDMIXER_REG_72_ALC_SGR */
#define ALC_SGR_START_GAIN_R_SHIFT 0
#define ALC_SGR_START_GAIN_R_MASK 0xff
#define ALC_SGR_START_GAIN_R_MAXVAL 0x9c
#define ALC_SGR_START_GAIN_R_MINVAL 0x6c
#endif

File diff suppressed because it is too large Load diff

108
sound/soc/codecs/hdmi.c Normal file
View file

@ -0,0 +1,108 @@
/*
* ALSA SoC codec driver for HDMI audio codecs.
* Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
* Author: Ricardo Neri <ricardo.neri@ti.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#include <linux/module.h>
#include <sound/soc.h>
#include <linux/of.h>
#include <linux/of_device.h>
#define DRV_NAME "hdmi-audio-codec"
static const struct snd_soc_dapm_widget hdmi_widgets[] = {
SND_SOC_DAPM_INPUT("RX"),
SND_SOC_DAPM_OUTPUT("TX"),
};
static const struct snd_soc_dapm_route hdmi_routes[] = {
{ "Capture", NULL, "RX" },
{ "TX", NULL, "Playback" },
};
static struct snd_soc_dai_driver hdmi_codec_dai = {
.name = "hdmi-hifi",
.playback = {
.stream_name = "Playback",
.channels_min = 2,
.channels_max = 8,
.rates = SNDRV_PCM_RATE_32000 |
SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE,
},
.capture = {
.stream_name = "Capture",
.channels_min = 2,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_32000 |
SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE,
},
};
#ifdef CONFIG_OF
static const struct of_device_id hdmi_audio_codec_ids[] = {
{ .compatible = "linux,hdmi-audio", },
{ }
};
MODULE_DEVICE_TABLE(of, hdmi_audio_codec_ids);
#endif
static struct snd_soc_codec_driver hdmi_codec = {
.dapm_widgets = hdmi_widgets,
.num_dapm_widgets = ARRAY_SIZE(hdmi_widgets),
.dapm_routes = hdmi_routes,
.num_dapm_routes = ARRAY_SIZE(hdmi_routes),
};
static int hdmi_codec_probe(struct platform_device *pdev)
{
return snd_soc_register_codec(&pdev->dev, &hdmi_codec,
&hdmi_codec_dai, 1);
}
static int hdmi_codec_remove(struct platform_device *pdev)
{
snd_soc_unregister_codec(&pdev->dev);
return 0;
}
static struct platform_driver hdmi_codec_driver = {
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(hdmi_audio_codec_ids),
},
.probe = hdmi_codec_probe,
.remove = hdmi_codec_remove,
};
module_platform_driver(hdmi_codec_driver);
MODULE_AUTHOR("Ricardo Neri <ricardo.neri@ti.com>");
MODULE_DESCRIPTION("ASoC generic HDMI codec driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:" DRV_NAME);

1166
sound/soc/codecs/isabelle.c Normal file

File diff suppressed because it is too large Load diff

143
sound/soc/codecs/isabelle.h Normal file
View file

@ -0,0 +1,143 @@
/*
* isabelle.h - Low power high fidelity audio codec driver header file
*
* Copyright (c) 2012 Texas Instruments, Inc
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
*/
#ifndef _ISABELLE_H
#define _ISABELLE_H
#include <linux/bitops.h>
/* ISABELLE REGISTERS */
#define ISABELLE_PWR_CFG_REG 0x01
#define ISABELLE_PWR_EN_REG 0x02
#define ISABELLE_PS_EN1_REG 0x03
#define ISABELLE_INT1_STATUS_REG 0x04
#define ISABELLE_INT1_MASK_REG 0x05
#define ISABELLE_INT2_STATUS_REG 0x06
#define ISABELLE_INT2_MASK_REG 0x07
#define ISABELLE_HKCTL1_REG 0x08
#define ISABELLE_HKCTL2_REG 0x09
#define ISABELLE_HKCTL3_REG 0x0A
#define ISABELLE_ACCDET_STATUS_REG 0x0B
#define ISABELLE_BUTTON_ID_REG 0x0C
#define ISABELLE_PLL_CFG_REG 0x10
#define ISABELLE_PLL_EN_REG 0x11
#define ISABELLE_FS_RATE_CFG_REG 0x12
#define ISABELLE_INTF_CFG_REG 0x13
#define ISABELLE_INTF_EN_REG 0x14
#define ISABELLE_ULATX12_INTF_CFG_REG 0x15
#define ISABELLE_DL12_INTF_CFG_REG 0x16
#define ISABELLE_DL34_INTF_CFG_REG 0x17
#define ISABELLE_DL56_INTF_CFG_REG 0x18
#define ISABELLE_ATX_STPGA1_CFG_REG 0x19
#define ISABELLE_ATX_STPGA2_CFG_REG 0x1A
#define ISABELLE_VTX_STPGA1_CFG_REG 0x1B
#define ISABELLE_VTX2_STPGA2_CFG_REG 0x1C
#define ISABELLE_ATX1_DPGA_REG 0x1D
#define ISABELLE_ATX2_DPGA_REG 0x1E
#define ISABELLE_VTX1_DPGA_REG 0x1F
#define ISABELLE_VTX2_DPGA_REG 0x20
#define ISABELLE_TX_INPUT_CFG_REG 0x21
#define ISABELLE_RX_INPUT_CFG_REG 0x22
#define ISABELLE_RX_INPUT_CFG2_REG 0x23
#define ISABELLE_VOICE_HPF_CFG_REG 0x24
#define ISABELLE_AUDIO_HPF_CFG_REG 0x25
#define ISABELLE_RX1_DPGA_REG 0x26
#define ISABELLE_RX2_DPGA_REG 0x27
#define ISABELLE_RX3_DPGA_REG 0x28
#define ISABELLE_RX4_DPGA_REG 0x29
#define ISABELLE_RX5_DPGA_REG 0x2A
#define ISABELLE_RX6_DPGA_REG 0x2B
#define ISABELLE_ALU_TX_EN_REG 0x2C
#define ISABELLE_ALU_RX_EN_REG 0x2D
#define ISABELLE_IIR_RESYNC_REG 0x2E
#define ISABELLE_ABIAS_CFG_REG 0x30
#define ISABELLE_DBIAS_CFG_REG 0x31
#define ISABELLE_MIC1_GAIN_REG 0x32
#define ISABELLE_MIC2_GAIN_REG 0x33
#define ISABELLE_AMIC_CFG_REG 0x34
#define ISABELLE_DMIC_CFG_REG 0x35
#define ISABELLE_APGA_GAIN_REG 0x36
#define ISABELLE_APGA_CFG_REG 0x37
#define ISABELLE_TX_GAIN_DLY_REG 0x38
#define ISABELLE_RX_GAIN_DLY_REG 0x39
#define ISABELLE_RX_PWR_CTRL_REG 0x3A
#define ISABELLE_DPGA1LR_IN_SEL_REG 0x3B
#define ISABELLE_DPGA1L_GAIN_REG 0x3C
#define ISABELLE_DPGA1R_GAIN_REG 0x3D
#define ISABELLE_DPGA2L_IN_SEL_REG 0x3E
#define ISABELLE_DPGA2R_IN_SEL_REG 0x3F
#define ISABELLE_DPGA2L_GAIN_REG 0x40
#define ISABELLE_DPGA2R_GAIN_REG 0x41
#define ISABELLE_DPGA3LR_IN_SEL_REG 0x42
#define ISABELLE_DPGA3L_GAIN_REG 0x43
#define ISABELLE_DPGA3R_GAIN_REG 0x44
#define ISABELLE_DAC1_SOFTRAMP_REG 0x45
#define ISABELLE_DAC2_SOFTRAMP_REG 0x46
#define ISABELLE_DAC3_SOFTRAMP_REG 0x47
#define ISABELLE_DAC_CFG_REG 0x48
#define ISABELLE_EARDRV_CFG1_REG 0x49
#define ISABELLE_EARDRV_CFG2_REG 0x4A
#define ISABELLE_HSDRV_GAIN_REG 0x4B
#define ISABELLE_HSDRV_CFG1_REG 0x4C
#define ISABELLE_HSDRV_CFG2_REG 0x4D
#define ISABELLE_HS_NG_CFG1_REG 0x4E
#define ISABELLE_HS_NG_CFG2_REG 0x4F
#define ISABELLE_LINEAMP_GAIN_REG 0x50
#define ISABELLE_LINEAMP_CFG_REG 0x51
#define ISABELLE_HFL_VOL_CTRL_REG 0x52
#define ISABELLE_HFL_SFTVOL_CTRL_REG 0x53
#define ISABELLE_HFL_LIM_CTRL_1_REG 0x54
#define ISABELLE_HFL_LIM_CTRL_2_REG 0x55
#define ISABELLE_HFR_VOL_CTRL_REG 0x56
#define ISABELLE_HFR_SFTVOL_CTRL_REG 0x57
#define ISABELLE_HFR_LIM_CTRL_1_REG 0x58
#define ISABELLE_HFR_LIM_CTRL_2_REG 0x59
#define ISABELLE_HF_MODE_REG 0x5A
#define ISABELLE_HFLPGA_CFG_REG 0x5B
#define ISABELLE_HFRPGA_CFG_REG 0x5C
#define ISABELLE_HFDRV_CFG_REG 0x5D
#define ISABELLE_PDMOUT_CFG1_REG 0x5E
#define ISABELLE_PDMOUT_CFG2_REG 0x5F
#define ISABELLE_PDMOUT_L_WM_REG 0x60
#define ISABELLE_PDMOUT_R_WM_REG 0x61
#define ISABELLE_HF_NG_CFG1_REG 0x62
#define ISABELLE_HF_NG_CFG2_REG 0x63
/* ISABELLE_PWR_EN_REG (0x02h) */
#define ISABELLE_CHIP_EN BIT(0)
/* ISABELLE DAI FORMATS */
#define ISABELLE_AIF_FMT_MASK 0x70
#define ISABELLE_I2S_MODE 0x0
#define ISABELLE_LEFT_J_MODE 0x1
#define ISABELLE_PDM_MODE 0x2
#define ISABELLE_AIF_LENGTH_MASK 0x30
#define ISABELLE_AIF_LENGTH_20 0x00
#define ISABELLE_AIF_LENGTH_32 0x10
#define ISABELLE_AIF_MS 0x80
#define ISABELLE_FS_RATE_MASK 0xF
#define ISABELLE_FS_RATE_8 0x0
#define ISABELLE_FS_RATE_11 0x1
#define ISABELLE_FS_RATE_12 0x2
#define ISABELLE_FS_RATE_16 0x4
#define ISABELLE_FS_RATE_22 0x5
#define ISABELLE_FS_RATE_24 0x6
#define ISABELLE_FS_RATE_32 0x8
#define ISABELLE_FS_RATE_44 0x9
#define ISABELLE_FS_RATE_48 0xA
#define ISABELLE_MAX_REGISTER 0xFF
#endif

Some files were not shown because too many files have changed in this diff Show more