Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ASoC: Add ssm2518 support

This patch adds a ASoC CODEC driver for the SSM2516. The SSM2516 is a stereo
Class-D audio amplifier with an I2S interface for audio in and a built-in
dynamic range control processor.

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>

authored by

Lars-Peter Clausen and committed by
Mark Brown
b6b5e76b c7788792

+924
+20
Documentation/devicetree/bindings/sound/ssm2518.txt
··· 1 + SSM2518 audio amplifier 2 + 3 + This device supports I2C only. 4 + 5 + Required properties: 6 + - compatible : Must be "adi,ssm2518" 7 + - reg : the I2C address of the device. This will either be 0x34 (ADDR pin low) 8 + or 0x35 (ADDR pin high) 9 + 10 + Optional properties: 11 + - gpios : GPIO connected to the nSD pin. If the property is not present it is 12 + assumed that the nSD pin is hardwired to always on. 13 + 14 + Example: 15 + 16 + ssm2518: ssm2518@34 { 17 + compatible = "adi,ssm2518"; 18 + reg = <0x34>; 19 + gpios = <&gpio 5 0>; 20 + };
+22
include/linux/platform_data/ssm2518.h
··· 1 + /* 2 + * SSM2518 amplifier audio driver 3 + * 4 + * Copyright 2013 Analog Devices Inc. 5 + * Author: Lars-Peter Clausen <lars@metafoo.de> 6 + * 7 + * Licensed under the GPL-2. 8 + */ 9 + 10 + #ifndef __LINUX_PLATFORM_DATA_SSM2518_H__ 11 + #define __LINUX_PLATFORM_DATA_SSM2518_H__ 12 + 13 + /** 14 + * struct ssm2518_platform_data - Platform data for the ssm2518 driver 15 + * @enable_gpio: GPIO connected to the nSD pin. Set to -1 if the nSD pin is 16 + * hardwired. 17 + */ 18 + struct ssm2518_platform_data { 19 + int enable_gpio; 20 + }; 21 + 22 + #endif
+4
sound/soc/codecs/Kconfig
··· 60 60 select SND_SOC_SI476X if MFD_SI476X_CORE 61 61 select SND_SOC_SN95031 if INTEL_SCU_IPC 62 62 select SND_SOC_SPDIF 63 + select SND_SOC_SSM2518 if I2C 63 64 select SND_SOC_SSM2602 if SND_SOC_I2C_AND_SPI 64 65 select SND_SOC_STA32X if I2C 65 66 select SND_SOC_STA529 if I2C ··· 312 311 tristate 313 312 314 313 config SND_SOC_SPDIF 314 + tristate 315 + 316 + config SND_SOC_SSM2518 315 317 tristate 316 318 317 319 config SND_SOC_SSM2602
+2
sound/soc/codecs/Makefile
··· 52 52 snd-soc-sn95031-objs := sn95031.o 53 53 snd-soc-spdif-tx-objs := spdif_transciever.o 54 54 snd-soc-spdif-rx-objs := spdif_receiver.o 55 + snd-soc-ssm2518-objs := ssm2518.o 55 56 snd-soc-ssm2602-objs := ssm2602.o 56 57 snd-soc-sta32x-objs := sta32x.o 57 58 snd-soc-sta529-objs := sta529.o ··· 177 176 obj-$(CONFIG_SND_SOC_SI476X) += snd-soc-si476x.o 178 177 obj-$(CONFIG_SND_SOC_SN95031) +=snd-soc-sn95031.o 179 178 obj-$(CONFIG_SND_SOC_SPDIF) += snd-soc-spdif-rx.o snd-soc-spdif-tx.o 179 + obj-$(CONFIG_SND_SOC_SSM2518) += snd-soc-ssm2518.o 180 180 obj-$(CONFIG_SND_SOC_SSM2602) += snd-soc-ssm2602.o 181 181 obj-$(CONFIG_SND_SOC_STA32X) += snd-soc-sta32x.o 182 182 obj-$(CONFIG_SND_SOC_STA529) += snd-soc-sta529.o
+856
sound/soc/codecs/ssm2518.c
··· 1 + /* 2 + * SSM2518 amplifier audio driver 3 + * 4 + * Copyright 2013 Analog Devices Inc. 5 + * Author: Lars-Peter Clausen <lars@metafoo.de> 6 + * 7 + * Licensed under the GPL-2. 8 + */ 9 + 10 + #include <linux/module.h> 11 + #include <linux/init.h> 12 + #include <linux/i2c.h> 13 + #include <linux/regmap.h> 14 + #include <linux/slab.h> 15 + #include <linux/gpio.h> 16 + #include <linux/of_gpio.h> 17 + #include <linux/platform_data/ssm2518.h> 18 + #include <sound/core.h> 19 + #include <sound/pcm.h> 20 + #include <sound/pcm_params.h> 21 + #include <sound/soc.h> 22 + #include <sound/initval.h> 23 + #include <sound/tlv.h> 24 + 25 + #include "ssm2518.h" 26 + 27 + #define SSM2518_REG_POWER1 0x00 28 + #define SSM2518_REG_CLOCK 0x01 29 + #define SSM2518_REG_SAI_CTRL1 0x02 30 + #define SSM2518_REG_SAI_CTRL2 0x03 31 + #define SSM2518_REG_CHAN_MAP 0x04 32 + #define SSM2518_REG_LEFT_VOL 0x05 33 + #define SSM2518_REG_RIGHT_VOL 0x06 34 + #define SSM2518_REG_MUTE_CTRL 0x07 35 + #define SSM2518_REG_FAULT_CTRL 0x08 36 + #define SSM2518_REG_POWER2 0x09 37 + #define SSM2518_REG_DRC_1 0x0a 38 + #define SSM2518_REG_DRC_2 0x0b 39 + #define SSM2518_REG_DRC_3 0x0c 40 + #define SSM2518_REG_DRC_4 0x0d 41 + #define SSM2518_REG_DRC_5 0x0e 42 + #define SSM2518_REG_DRC_6 0x0f 43 + #define SSM2518_REG_DRC_7 0x10 44 + #define SSM2518_REG_DRC_8 0x11 45 + #define SSM2518_REG_DRC_9 0x12 46 + 47 + #define SSM2518_POWER1_RESET BIT(7) 48 + #define SSM2518_POWER1_NO_BCLK BIT(5) 49 + #define SSM2518_POWER1_MCS_MASK (0xf << 1) 50 + #define SSM2518_POWER1_MCS_64FS (0x0 << 1) 51 + #define SSM2518_POWER1_MCS_128FS (0x1 << 1) 52 + #define SSM2518_POWER1_MCS_256FS (0x2 << 1) 53 + #define SSM2518_POWER1_MCS_384FS (0x3 << 1) 54 + #define SSM2518_POWER1_MCS_512FS (0x4 << 1) 55 + #define SSM2518_POWER1_MCS_768FS (0x5 << 1) 56 + #define SSM2518_POWER1_MCS_100FS (0x6 << 1) 57 + #define SSM2518_POWER1_MCS_200FS (0x7 << 1) 58 + #define SSM2518_POWER1_MCS_400FS (0x8 << 1) 59 + #define SSM2518_POWER1_SPWDN BIT(0) 60 + 61 + #define SSM2518_CLOCK_ASR BIT(0) 62 + 63 + #define SSM2518_SAI_CTRL1_FMT_MASK (0x3 << 5) 64 + #define SSM2518_SAI_CTRL1_FMT_I2S (0x0 << 5) 65 + #define SSM2518_SAI_CTRL1_FMT_LJ (0x1 << 5) 66 + #define SSM2518_SAI_CTRL1_FMT_RJ_24BIT (0x2 << 5) 67 + #define SSM2518_SAI_CTRL1_FMT_RJ_16BIT (0x3 << 5) 68 + 69 + #define SSM2518_SAI_CTRL1_SAI_MASK (0x7 << 2) 70 + #define SSM2518_SAI_CTRL1_SAI_I2S (0x0 << 2) 71 + #define SSM2518_SAI_CTRL1_SAI_TDM_2 (0x1 << 2) 72 + #define SSM2518_SAI_CTRL1_SAI_TDM_4 (0x2 << 2) 73 + #define SSM2518_SAI_CTRL1_SAI_TDM_8 (0x3 << 2) 74 + #define SSM2518_SAI_CTRL1_SAI_TDM_16 (0x4 << 2) 75 + #define SSM2518_SAI_CTRL1_SAI_MONO (0x5 << 2) 76 + 77 + #define SSM2518_SAI_CTRL1_FS_MASK (0x3) 78 + #define SSM2518_SAI_CTRL1_FS_8000_12000 (0x0) 79 + #define SSM2518_SAI_CTRL1_FS_16000_24000 (0x1) 80 + #define SSM2518_SAI_CTRL1_FS_32000_48000 (0x2) 81 + #define SSM2518_SAI_CTRL1_FS_64000_96000 (0x3) 82 + 83 + #define SSM2518_SAI_CTRL2_BCLK_INTERAL BIT(7) 84 + #define SSM2518_SAI_CTRL2_LRCLK_PULSE BIT(6) 85 + #define SSM2518_SAI_CTRL2_LRCLK_INVERT BIT(5) 86 + #define SSM2518_SAI_CTRL2_MSB BIT(4) 87 + #define SSM2518_SAI_CTRL2_SLOT_WIDTH_MASK (0x3 << 2) 88 + #define SSM2518_SAI_CTRL2_SLOT_WIDTH_32 (0x0 << 2) 89 + #define SSM2518_SAI_CTRL2_SLOT_WIDTH_24 (0x1 << 2) 90 + #define SSM2518_SAI_CTRL2_SLOT_WIDTH_16 (0x2 << 2) 91 + #define SSM2518_SAI_CTRL2_BCLK_INVERT BIT(1) 92 + 93 + #define SSM2518_CHAN_MAP_RIGHT_SLOT_OFFSET 4 94 + #define SSM2518_CHAN_MAP_RIGHT_SLOT_MASK 0xf0 95 + #define SSM2518_CHAN_MAP_LEFT_SLOT_OFFSET 0 96 + #define SSM2518_CHAN_MAP_LEFT_SLOT_MASK 0x0f 97 + 98 + #define SSM2518_MUTE_CTRL_ANA_GAIN BIT(5) 99 + #define SSM2518_MUTE_CTRL_MUTE_MASTER BIT(0) 100 + 101 + #define SSM2518_POWER2_APWDN BIT(0) 102 + 103 + #define SSM2518_DAC_MUTE BIT(6) 104 + #define SSM2518_DAC_FS_MASK 0x07 105 + #define SSM2518_DAC_FS_8000 0x00 106 + #define SSM2518_DAC_FS_16000 0x01 107 + #define SSM2518_DAC_FS_32000 0x02 108 + #define SSM2518_DAC_FS_64000 0x03 109 + #define SSM2518_DAC_FS_128000 0x04 110 + 111 + struct ssm2518 { 112 + struct regmap *regmap; 113 + bool right_j; 114 + 115 + unsigned int sysclk; 116 + const struct snd_pcm_hw_constraint_list *constraints; 117 + 118 + int enable_gpio; 119 + }; 120 + 121 + static const struct reg_default ssm2518_reg_defaults[] = { 122 + { 0x00, 0x05 }, 123 + { 0x01, 0x00 }, 124 + { 0x02, 0x02 }, 125 + { 0x03, 0x00 }, 126 + { 0x04, 0x10 }, 127 + { 0x05, 0x40 }, 128 + { 0x06, 0x40 }, 129 + { 0x07, 0x81 }, 130 + { 0x08, 0x0c }, 131 + { 0x09, 0x99 }, 132 + { 0x0a, 0x7c }, 133 + { 0x0b, 0x5b }, 134 + { 0x0c, 0x57 }, 135 + { 0x0d, 0x89 }, 136 + { 0x0e, 0x8c }, 137 + { 0x0f, 0x77 }, 138 + { 0x10, 0x26 }, 139 + { 0x11, 0x1c }, 140 + { 0x12, 0x97 }, 141 + }; 142 + 143 + static const DECLARE_TLV_DB_MINMAX_MUTE(ssm2518_vol_tlv, -7125, 2400); 144 + static const DECLARE_TLV_DB_SCALE(ssm2518_compressor_tlv, -3400, 200, 0); 145 + static const DECLARE_TLV_DB_SCALE(ssm2518_expander_tlv, -8100, 300, 0); 146 + static const DECLARE_TLV_DB_SCALE(ssm2518_noise_gate_tlv, -9600, 300, 0); 147 + static const DECLARE_TLV_DB_SCALE(ssm2518_post_drc_tlv, -2400, 300, 0); 148 + 149 + static const DECLARE_TLV_DB_RANGE(ssm2518_limiter_tlv, 150 + 0, 7, TLV_DB_SCALE_ITEM(-2200, 200, 0), 151 + 7, 15, TLV_DB_SCALE_ITEM(-800, 100, 0), 152 + ); 153 + 154 + static const char * const ssm2518_drc_peak_detector_attack_time_text[] = { 155 + "0 ms", "0.1 ms", "0.19 ms", "0.37 ms", "0.75 ms", "1.5 ms", "3 ms", 156 + "6 ms", "12 ms", "24 ms", "48 ms", "96 ms", "192 ms", "384 ms", 157 + "768 ms", "1536 ms", 158 + }; 159 + 160 + static const char * const ssm2518_drc_peak_detector_release_time_text[] = { 161 + "0 ms", "1.5 ms", "3 ms", "6 ms", "12 ms", "24 ms", "48 ms", "96 ms", 162 + "192 ms", "384 ms", "768 ms", "1536 ms", "3072 ms", "6144 ms", 163 + "12288 ms", "24576 ms" 164 + }; 165 + 166 + static const char * const ssm2518_drc_hold_time_text[] = { 167 + "0 ms", "0.67 ms", "1.33 ms", "2.67 ms", "5.33 ms", "10.66 ms", 168 + "21.32 ms", "42.64 ms", "85.28 ms", "170.56 ms", "341.12 ms", 169 + "682.24 ms", "1364 ms", 170 + }; 171 + 172 + static const SOC_ENUM_SINGLE_DECL(ssm2518_drc_peak_detector_attack_time_enum, 173 + SSM2518_REG_DRC_2, 4, ssm2518_drc_peak_detector_attack_time_text); 174 + static const SOC_ENUM_SINGLE_DECL(ssm2518_drc_peak_detector_release_time_enum, 175 + SSM2518_REG_DRC_2, 0, ssm2518_drc_peak_detector_release_time_text); 176 + static const SOC_ENUM_SINGLE_DECL(ssm2518_drc_attack_time_enum, 177 + SSM2518_REG_DRC_6, 4, ssm2518_drc_peak_detector_attack_time_text); 178 + static const SOC_ENUM_SINGLE_DECL(ssm2518_drc_decay_time_enum, 179 + SSM2518_REG_DRC_6, 0, ssm2518_drc_peak_detector_release_time_text); 180 + static const SOC_ENUM_SINGLE_DECL(ssm2518_drc_hold_time_enum, 181 + SSM2518_REG_DRC_7, 4, ssm2518_drc_hold_time_text); 182 + static const SOC_ENUM_SINGLE_DECL(ssm2518_drc_noise_gate_hold_time_enum, 183 + SSM2518_REG_DRC_7, 0, ssm2518_drc_hold_time_text); 184 + static const SOC_ENUM_SINGLE_DECL(ssm2518_drc_rms_averaging_time_enum, 185 + SSM2518_REG_DRC_9, 0, ssm2518_drc_peak_detector_release_time_text); 186 + 187 + static const struct snd_kcontrol_new ssm2518_snd_controls[] = { 188 + SOC_SINGLE("Playback De-emphasis Switch", SSM2518_REG_MUTE_CTRL, 189 + 4, 1, 0), 190 + SOC_DOUBLE_R_TLV("Master Playback Volume", SSM2518_REG_LEFT_VOL, 191 + SSM2518_REG_RIGHT_VOL, 0, 0xff, 1, ssm2518_vol_tlv), 192 + SOC_DOUBLE("Master Playback Switch", SSM2518_REG_MUTE_CTRL, 2, 1, 1, 1), 193 + 194 + SOC_SINGLE("Amp Low Power Mode Switch", SSM2518_REG_POWER2, 4, 1, 0), 195 + SOC_SINGLE("DAC Low Power Mode Switch", SSM2518_REG_POWER2, 3, 1, 0), 196 + 197 + SOC_SINGLE("DRC Limiter Switch", SSM2518_REG_DRC_1, 5, 1, 0), 198 + SOC_SINGLE("DRC Compressor Switch", SSM2518_REG_DRC_1, 4, 1, 0), 199 + SOC_SINGLE("DRC Expander Switch", SSM2518_REG_DRC_1, 3, 1, 0), 200 + SOC_SINGLE("DRC Noise Gate Switch", SSM2518_REG_DRC_1, 2, 1, 0), 201 + SOC_DOUBLE("DRC Switch", SSM2518_REG_DRC_1, 0, 1, 1, 0), 202 + 203 + SOC_SINGLE_TLV("DRC Limiter Threshold Volume", 204 + SSM2518_REG_DRC_3, 4, 15, 1, ssm2518_limiter_tlv), 205 + SOC_SINGLE_TLV("DRC Compressor Lower Threshold Volume", 206 + SSM2518_REG_DRC_3, 0, 15, 1, ssm2518_compressor_tlv), 207 + SOC_SINGLE_TLV("DRC Expander Upper Threshold Volume", SSM2518_REG_DRC_4, 208 + 4, 15, 1, ssm2518_expander_tlv), 209 + SOC_SINGLE_TLV("DRC Noise Gate Threshold Volume", 210 + SSM2518_REG_DRC_4, 0, 15, 1, ssm2518_noise_gate_tlv), 211 + SOC_SINGLE_TLV("DRC Upper Output Threshold Volume", 212 + SSM2518_REG_DRC_5, 4, 15, 1, ssm2518_limiter_tlv), 213 + SOC_SINGLE_TLV("DRC Lower Output Threshold Volume", 214 + SSM2518_REG_DRC_5, 0, 15, 1, ssm2518_noise_gate_tlv), 215 + SOC_SINGLE_TLV("DRC Post Volume", SSM2518_REG_DRC_8, 216 + 2, 15, 1, ssm2518_post_drc_tlv), 217 + 218 + SOC_ENUM("DRC Peak Detector Attack Time", 219 + ssm2518_drc_peak_detector_attack_time_enum), 220 + SOC_ENUM("DRC Peak Detector Release Time", 221 + ssm2518_drc_peak_detector_release_time_enum), 222 + SOC_ENUM("DRC Attack Time", ssm2518_drc_attack_time_enum), 223 + SOC_ENUM("DRC Decay Time", ssm2518_drc_decay_time_enum), 224 + SOC_ENUM("DRC Hold Time", ssm2518_drc_hold_time_enum), 225 + SOC_ENUM("DRC Noise Gate Hold Time", 226 + ssm2518_drc_noise_gate_hold_time_enum), 227 + SOC_ENUM("DRC RMS Averaging Time", ssm2518_drc_rms_averaging_time_enum), 228 + }; 229 + 230 + static const struct snd_soc_dapm_widget ssm2518_dapm_widgets[] = { 231 + SND_SOC_DAPM_DAC("DACL", "HiFi Playback", SSM2518_REG_POWER2, 1, 1), 232 + SND_SOC_DAPM_DAC("DACR", "HiFi Playback", SSM2518_REG_POWER2, 2, 1), 233 + 234 + SND_SOC_DAPM_OUTPUT("OUTL"), 235 + SND_SOC_DAPM_OUTPUT("OUTR"), 236 + }; 237 + 238 + static const struct snd_soc_dapm_route ssm2518_routes[] = { 239 + { "OUTL", NULL, "DACL" }, 240 + { "OUTR", NULL, "DACR" }, 241 + }; 242 + 243 + struct ssm2518_mcs_lut { 244 + unsigned int rate; 245 + const unsigned int *sysclks; 246 + }; 247 + 248 + static const unsigned int ssm2518_sysclks_2048000[] = { 249 + 2048000, 4096000, 8192000, 12288000, 16384000, 24576000, 250 + 3200000, 6400000, 12800000, 0 251 + }; 252 + 253 + static const unsigned int ssm2518_sysclks_2822000[] = { 254 + 2822000, 5644800, 11289600, 16934400, 22579200, 33868800, 255 + 4410000, 8820000, 17640000, 0 256 + }; 257 + 258 + static const unsigned int ssm2518_sysclks_3072000[] = { 259 + 3072000, 6144000, 12288000, 16384000, 24576000, 38864000, 260 + 4800000, 9600000, 19200000, 0 261 + }; 262 + 263 + static const struct ssm2518_mcs_lut ssm2518_mcs_lut[] = { 264 + { 8000, ssm2518_sysclks_2048000, }, 265 + { 11025, ssm2518_sysclks_2822000, }, 266 + { 12000, ssm2518_sysclks_3072000, }, 267 + { 16000, ssm2518_sysclks_2048000, }, 268 + { 24000, ssm2518_sysclks_3072000, }, 269 + { 22050, ssm2518_sysclks_2822000, }, 270 + { 32000, ssm2518_sysclks_2048000, }, 271 + { 44100, ssm2518_sysclks_2822000, }, 272 + { 48000, ssm2518_sysclks_3072000, }, 273 + { 96000, ssm2518_sysclks_3072000, }, 274 + }; 275 + 276 + static const unsigned int ssm2518_rates_2048000[] = { 277 + 8000, 16000, 32000, 278 + }; 279 + 280 + static const struct snd_pcm_hw_constraint_list ssm2518_constraints_2048000 = { 281 + .list = ssm2518_rates_2048000, 282 + .count = ARRAY_SIZE(ssm2518_rates_2048000), 283 + }; 284 + 285 + static const unsigned int ssm2518_rates_2822000[] = { 286 + 11025, 22050, 44100, 287 + }; 288 + 289 + static const struct snd_pcm_hw_constraint_list ssm2518_constraints_2822000 = { 290 + .list = ssm2518_rates_2822000, 291 + .count = ARRAY_SIZE(ssm2518_rates_2822000), 292 + }; 293 + 294 + static const unsigned int ssm2518_rates_3072000[] = { 295 + 12000, 24000, 48000, 96000, 296 + }; 297 + 298 + static const struct snd_pcm_hw_constraint_list ssm2518_constraints_3072000 = { 299 + .list = ssm2518_rates_3072000, 300 + .count = ARRAY_SIZE(ssm2518_rates_3072000), 301 + }; 302 + 303 + static const unsigned int ssm2518_rates_12288000[] = { 304 + 8000, 12000, 16000, 24000, 32000, 48000, 96000, 305 + }; 306 + 307 + static const struct snd_pcm_hw_constraint_list ssm2518_constraints_12288000 = { 308 + .list = ssm2518_rates_12288000, 309 + .count = ARRAY_SIZE(ssm2518_rates_12288000), 310 + }; 311 + 312 + static unsigned int ssm2518_lookup_mcs(struct ssm2518 *ssm2518, 313 + unsigned int rate) 314 + { 315 + const unsigned int *sysclks = NULL; 316 + int i; 317 + 318 + for (i = 0; i < ARRAY_SIZE(ssm2518_mcs_lut); i++) { 319 + if (ssm2518_mcs_lut[i].rate == rate) { 320 + sysclks = ssm2518_mcs_lut[i].sysclks; 321 + break; 322 + } 323 + } 324 + 325 + if (!sysclks) 326 + return -EINVAL; 327 + 328 + for (i = 0; sysclks[i]; i++) { 329 + if (sysclks[i] == ssm2518->sysclk) 330 + return i; 331 + } 332 + 333 + return -EINVAL; 334 + } 335 + 336 + static int ssm2518_hw_params(struct snd_pcm_substream *substream, 337 + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) 338 + { 339 + struct snd_soc_codec *codec = dai->codec; 340 + struct ssm2518 *ssm2518 = snd_soc_codec_get_drvdata(codec); 341 + unsigned int rate = params_rate(params); 342 + unsigned int ctrl1, ctrl1_mask; 343 + int mcs; 344 + int ret; 345 + 346 + mcs = ssm2518_lookup_mcs(ssm2518, rate); 347 + if (mcs < 0) 348 + return mcs; 349 + 350 + ctrl1_mask = SSM2518_SAI_CTRL1_FS_MASK; 351 + 352 + if (rate >= 8000 && rate <= 12000) 353 + ctrl1 = SSM2518_SAI_CTRL1_FS_8000_12000; 354 + else if (rate >= 16000 && rate <= 24000) 355 + ctrl1 = SSM2518_SAI_CTRL1_FS_16000_24000; 356 + else if (rate >= 32000 && rate <= 48000) 357 + ctrl1 = SSM2518_SAI_CTRL1_FS_32000_48000; 358 + else if (rate >= 64000 && rate <= 96000) 359 + ctrl1 = SSM2518_SAI_CTRL1_FS_64000_96000; 360 + else 361 + return -EINVAL; 362 + 363 + if (ssm2518->right_j) { 364 + switch (params_format(params)) { 365 + case SNDRV_PCM_FORMAT_S16_LE: 366 + ctrl1 |= SSM2518_SAI_CTRL1_FMT_RJ_16BIT; 367 + break; 368 + case SNDRV_PCM_FORMAT_S24_LE: 369 + ctrl1 |= SSM2518_SAI_CTRL1_FMT_RJ_24BIT; 370 + break; 371 + default: 372 + return -EINVAL; 373 + } 374 + ctrl1_mask |= SSM2518_SAI_CTRL1_FMT_MASK; 375 + } 376 + 377 + /* Disable auto samplerate detection */ 378 + ret = regmap_update_bits(ssm2518->regmap, SSM2518_REG_CLOCK, 379 + SSM2518_CLOCK_ASR, SSM2518_CLOCK_ASR); 380 + if (ret < 0) 381 + return ret; 382 + 383 + ret = regmap_update_bits(ssm2518->regmap, SSM2518_REG_SAI_CTRL1, 384 + ctrl1_mask, ctrl1); 385 + if (ret < 0) 386 + return ret; 387 + 388 + return regmap_update_bits(ssm2518->regmap, SSM2518_REG_POWER1, 389 + SSM2518_POWER1_MCS_MASK, mcs << 1); 390 + } 391 + 392 + static int ssm2518_mute(struct snd_soc_dai *dai, int mute) 393 + { 394 + struct ssm2518 *ssm2518 = snd_soc_codec_get_drvdata(dai->codec); 395 + unsigned int val; 396 + 397 + if (mute) 398 + val = SSM2518_MUTE_CTRL_MUTE_MASTER; 399 + else 400 + val = 0; 401 + 402 + return regmap_update_bits(ssm2518->regmap, SSM2518_REG_MUTE_CTRL, 403 + SSM2518_MUTE_CTRL_MUTE_MASTER, val); 404 + } 405 + 406 + static int ssm2518_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) 407 + { 408 + struct ssm2518 *ssm2518 = snd_soc_codec_get_drvdata(dai->codec); 409 + unsigned int ctrl1 = 0, ctrl2 = 0; 410 + bool invert_fclk; 411 + int ret; 412 + 413 + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 414 + case SND_SOC_DAIFMT_CBS_CFS: 415 + break; 416 + default: 417 + return -EINVAL; 418 + } 419 + 420 + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 421 + case SND_SOC_DAIFMT_NB_NF: 422 + invert_fclk = false; 423 + break; 424 + case SND_SOC_DAIFMT_IB_NF: 425 + ctrl2 |= SSM2518_SAI_CTRL2_BCLK_INVERT; 426 + invert_fclk = false; 427 + break; 428 + case SND_SOC_DAIFMT_NB_IF: 429 + invert_fclk = true; 430 + break; 431 + case SND_SOC_DAIFMT_IB_IF: 432 + ctrl2 |= SSM2518_SAI_CTRL2_BCLK_INVERT; 433 + invert_fclk = true; 434 + break; 435 + default: 436 + return -EINVAL; 437 + } 438 + 439 + ssm2518->right_j = false; 440 + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 441 + case SND_SOC_DAIFMT_I2S: 442 + ctrl1 |= SSM2518_SAI_CTRL1_FMT_I2S; 443 + break; 444 + case SND_SOC_DAIFMT_LEFT_J: 445 + ctrl1 |= SSM2518_SAI_CTRL1_FMT_LJ; 446 + invert_fclk = !invert_fclk; 447 + break; 448 + case SND_SOC_DAIFMT_RIGHT_J: 449 + ctrl1 |= SSM2518_SAI_CTRL1_FMT_RJ_24BIT; 450 + ssm2518->right_j = true; 451 + invert_fclk = !invert_fclk; 452 + break; 453 + case SND_SOC_DAIFMT_DSP_A: 454 + ctrl2 |= SSM2518_SAI_CTRL2_LRCLK_PULSE; 455 + ctrl1 |= SSM2518_SAI_CTRL1_FMT_I2S; 456 + invert_fclk = false; 457 + break; 458 + case SND_SOC_DAIFMT_DSP_B: 459 + ctrl2 |= SSM2518_SAI_CTRL2_LRCLK_PULSE; 460 + ctrl1 |= SSM2518_SAI_CTRL1_FMT_LJ; 461 + invert_fclk = false; 462 + break; 463 + default: 464 + return -EINVAL; 465 + } 466 + 467 + if (invert_fclk) 468 + ctrl2 |= SSM2518_SAI_CTRL2_LRCLK_INVERT; 469 + 470 + ret = regmap_write(ssm2518->regmap, SSM2518_REG_SAI_CTRL1, ctrl1); 471 + if (ret) 472 + return ret; 473 + 474 + return regmap_write(ssm2518->regmap, SSM2518_REG_SAI_CTRL2, ctrl2); 475 + } 476 + 477 + static int ssm2518_set_power(struct ssm2518 *ssm2518, bool enable) 478 + { 479 + int ret = 0; 480 + 481 + if (!enable) { 482 + ret = regmap_update_bits(ssm2518->regmap, SSM2518_REG_POWER1, 483 + SSM2518_POWER1_SPWDN, SSM2518_POWER1_SPWDN); 484 + regcache_mark_dirty(ssm2518->regmap); 485 + } 486 + 487 + if (gpio_is_valid(ssm2518->enable_gpio)) 488 + gpio_set_value(ssm2518->enable_gpio, enable); 489 + 490 + regcache_cache_only(ssm2518->regmap, !enable); 491 + 492 + if (enable) { 493 + ret = regmap_update_bits(ssm2518->regmap, SSM2518_REG_POWER1, 494 + SSM2518_POWER1_SPWDN | SSM2518_POWER1_RESET, 0x00); 495 + regcache_sync(ssm2518->regmap); 496 + } 497 + 498 + return ret; 499 + } 500 + 501 + static int ssm2518_set_bias_level(struct snd_soc_codec *codec, 502 + enum snd_soc_bias_level level) 503 + { 504 + struct ssm2518 *ssm2518 = snd_soc_codec_get_drvdata(codec); 505 + int ret = 0; 506 + 507 + switch (level) { 508 + case SND_SOC_BIAS_ON: 509 + break; 510 + case SND_SOC_BIAS_PREPARE: 511 + break; 512 + case SND_SOC_BIAS_STANDBY: 513 + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) 514 + ret = ssm2518_set_power(ssm2518, true); 515 + break; 516 + case SND_SOC_BIAS_OFF: 517 + ret = ssm2518_set_power(ssm2518, false); 518 + break; 519 + } 520 + 521 + if (ret) 522 + return ret; 523 + 524 + codec->dapm.bias_level = level; 525 + 526 + return 0; 527 + } 528 + 529 + static int ssm2518_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, 530 + unsigned int rx_mask, int slots, int width) 531 + { 532 + struct ssm2518 *ssm2518 = snd_soc_codec_get_drvdata(dai->codec); 533 + unsigned int ctrl1, ctrl2; 534 + int left_slot, right_slot; 535 + int ret; 536 + 537 + if (slots == 0) 538 + return regmap_update_bits(ssm2518->regmap, 539 + SSM2518_REG_SAI_CTRL1, SSM2518_SAI_CTRL1_SAI_MASK, 540 + SSM2518_SAI_CTRL1_SAI_I2S); 541 + 542 + if (tx_mask == 0 || tx_mask != 0) 543 + return -EINVAL; 544 + 545 + if (slots == 1) { 546 + if (tx_mask != 1) 547 + return -EINVAL; 548 + left_slot = 0; 549 + right_slot = 0; 550 + } else { 551 + /* We assume the left channel < right channel */ 552 + left_slot = ffs(tx_mask); 553 + tx_mask &= ~(1 << tx_mask); 554 + if (tx_mask == 0) { 555 + right_slot = left_slot; 556 + } else { 557 + right_slot = ffs(tx_mask); 558 + tx_mask &= ~(1 << tx_mask); 559 + } 560 + } 561 + 562 + if (tx_mask != 0 || left_slot >= slots || right_slot >= slots) 563 + return -EINVAL; 564 + 565 + switch (width) { 566 + case 16: 567 + ctrl2 = SSM2518_SAI_CTRL2_SLOT_WIDTH_16; 568 + break; 569 + case 24: 570 + ctrl2 = SSM2518_SAI_CTRL2_SLOT_WIDTH_24; 571 + break; 572 + case 32: 573 + ctrl2 = SSM2518_SAI_CTRL2_SLOT_WIDTH_32; 574 + break; 575 + default: 576 + return -EINVAL; 577 + } 578 + 579 + switch (slots) { 580 + case 1: 581 + ctrl1 = SSM2518_SAI_CTRL1_SAI_MONO; 582 + break; 583 + case 2: 584 + ctrl1 = SSM2518_SAI_CTRL1_SAI_TDM_2; 585 + break; 586 + case 4: 587 + ctrl1 = SSM2518_SAI_CTRL1_SAI_TDM_4; 588 + break; 589 + case 8: 590 + ctrl1 = SSM2518_SAI_CTRL1_SAI_TDM_8; 591 + break; 592 + case 16: 593 + ctrl1 = SSM2518_SAI_CTRL1_SAI_TDM_16; 594 + break; 595 + default: 596 + return -EINVAL; 597 + } 598 + 599 + ret = regmap_write(ssm2518->regmap, SSM2518_REG_CHAN_MAP, 600 + (left_slot << SSM2518_CHAN_MAP_LEFT_SLOT_OFFSET) | 601 + (right_slot << SSM2518_CHAN_MAP_RIGHT_SLOT_OFFSET)); 602 + if (ret) 603 + return ret; 604 + 605 + ret = regmap_update_bits(ssm2518->regmap, SSM2518_REG_SAI_CTRL1, 606 + SSM2518_SAI_CTRL1_SAI_MASK, ctrl1); 607 + if (ret) 608 + return ret; 609 + 610 + return regmap_update_bits(ssm2518->regmap, SSM2518_REG_SAI_CTRL2, 611 + SSM2518_SAI_CTRL2_SLOT_WIDTH_MASK, ctrl2); 612 + } 613 + 614 + static int ssm2518_startup(struct snd_pcm_substream *substream, 615 + struct snd_soc_dai *dai) 616 + { 617 + struct ssm2518 *ssm2518 = snd_soc_codec_get_drvdata(dai->codec); 618 + 619 + if (ssm2518->constraints) 620 + snd_pcm_hw_constraint_list(substream->runtime, 0, 621 + SNDRV_PCM_HW_PARAM_RATE, ssm2518->constraints); 622 + 623 + return 0; 624 + } 625 + 626 + #define SSM2518_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | \ 627 + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32) 628 + 629 + static const struct snd_soc_dai_ops ssm2518_dai_ops = { 630 + .startup = ssm2518_startup, 631 + .hw_params = ssm2518_hw_params, 632 + .digital_mute = ssm2518_mute, 633 + .set_fmt = ssm2518_set_dai_fmt, 634 + .set_tdm_slot = ssm2518_set_tdm_slot, 635 + }; 636 + 637 + static struct snd_soc_dai_driver ssm2518_dai = { 638 + .name = "ssm2518-hifi", 639 + .playback = { 640 + .stream_name = "Playback", 641 + .channels_min = 2, 642 + .channels_max = 2, 643 + .rates = SNDRV_PCM_RATE_8000_96000, 644 + .formats = SSM2518_FORMATS, 645 + }, 646 + .ops = &ssm2518_dai_ops, 647 + }; 648 + 649 + static int ssm2518_probe(struct snd_soc_codec *codec) 650 + { 651 + struct ssm2518 *ssm2518 = snd_soc_codec_get_drvdata(codec); 652 + int ret; 653 + 654 + codec->control_data = ssm2518->regmap; 655 + ret = snd_soc_codec_set_cache_io(codec, 0, 0, SND_SOC_REGMAP); 656 + if (ret < 0) { 657 + dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); 658 + return ret; 659 + } 660 + 661 + return ssm2518_set_bias_level(codec, SND_SOC_BIAS_OFF); 662 + } 663 + 664 + static int ssm2518_remove(struct snd_soc_codec *codec) 665 + { 666 + ssm2518_set_bias_level(codec, SND_SOC_BIAS_OFF); 667 + return 0; 668 + } 669 + 670 + static int ssm2518_set_sysclk(struct snd_soc_codec *codec, int clk_id, 671 + int source, unsigned int freq, int dir) 672 + { 673 + struct ssm2518 *ssm2518 = snd_soc_codec_get_drvdata(codec); 674 + unsigned int val; 675 + 676 + if (clk_id != SSM2518_SYSCLK) 677 + return -EINVAL; 678 + 679 + switch (source) { 680 + case SSM2518_SYSCLK_SRC_MCLK: 681 + val = 0; 682 + break; 683 + case SSM2518_SYSCLK_SRC_BCLK: 684 + /* In this case the bitclock is used as the system clock, and 685 + * the bitclock signal needs to be connected to the MCLK pin and 686 + * the BCLK pin is left unconnected */ 687 + val = SSM2518_POWER1_NO_BCLK; 688 + break; 689 + default: 690 + return -EINVAL; 691 + } 692 + 693 + switch (freq) { 694 + case 0: 695 + ssm2518->constraints = NULL; 696 + break; 697 + case 2048000: 698 + case 4096000: 699 + case 8192000: 700 + case 3200000: 701 + case 6400000: 702 + case 12800000: 703 + ssm2518->constraints = &ssm2518_constraints_2048000; 704 + break; 705 + case 2822000: 706 + case 5644800: 707 + case 11289600: 708 + case 16934400: 709 + case 22579200: 710 + case 33868800: 711 + case 4410000: 712 + case 8820000: 713 + case 17640000: 714 + ssm2518->constraints = &ssm2518_constraints_2822000; 715 + break; 716 + case 3072000: 717 + case 6144000: 718 + case 38864000: 719 + case 4800000: 720 + case 9600000: 721 + case 19200000: 722 + ssm2518->constraints = &ssm2518_constraints_3072000; 723 + break; 724 + case 12288000: 725 + case 16384000: 726 + case 24576000: 727 + ssm2518->constraints = &ssm2518_constraints_12288000; 728 + break; 729 + default: 730 + return -EINVAL; 731 + } 732 + 733 + ssm2518->sysclk = freq; 734 + 735 + return regmap_update_bits(ssm2518->regmap, SSM2518_REG_POWER1, 736 + SSM2518_POWER1_NO_BCLK, val); 737 + } 738 + 739 + static struct snd_soc_codec_driver ssm2518_codec_driver = { 740 + .probe = ssm2518_probe, 741 + .remove = ssm2518_remove, 742 + .set_bias_level = ssm2518_set_bias_level, 743 + .set_sysclk = ssm2518_set_sysclk, 744 + .idle_bias_off = true, 745 + 746 + .controls = ssm2518_snd_controls, 747 + .num_controls = ARRAY_SIZE(ssm2518_snd_controls), 748 + .dapm_widgets = ssm2518_dapm_widgets, 749 + .num_dapm_widgets = ARRAY_SIZE(ssm2518_dapm_widgets), 750 + .dapm_routes = ssm2518_routes, 751 + .num_dapm_routes = ARRAY_SIZE(ssm2518_routes), 752 + }; 753 + 754 + static bool ssm2518_register_volatile(struct device *dev, unsigned int reg) 755 + { 756 + return false; 757 + } 758 + 759 + static const struct regmap_config ssm2518_regmap_config = { 760 + .val_bits = 8, 761 + .reg_bits = 8, 762 + 763 + .max_register = SSM2518_REG_DRC_9, 764 + .volatile_reg = ssm2518_register_volatile, 765 + 766 + .cache_type = REGCACHE_RBTREE, 767 + .reg_defaults = ssm2518_reg_defaults, 768 + .num_reg_defaults = ARRAY_SIZE(ssm2518_reg_defaults), 769 + }; 770 + 771 + static int ssm2518_i2c_probe(struct i2c_client *i2c, 772 + const struct i2c_device_id *id) 773 + { 774 + struct ssm2518_platform_data *pdata = i2c->dev.platform_data; 775 + struct ssm2518 *ssm2518; 776 + int ret; 777 + 778 + ssm2518 = devm_kzalloc(&i2c->dev, sizeof(*ssm2518), GFP_KERNEL); 779 + if (ssm2518 == NULL) 780 + return -ENOMEM; 781 + 782 + if (pdata) { 783 + ssm2518->enable_gpio = pdata->enable_gpio; 784 + } else if (i2c->dev.of_node) { 785 + ssm2518->enable_gpio = of_get_gpio(i2c->dev.of_node, 0); 786 + if (ssm2518->enable_gpio < 0 && ssm2518->enable_gpio != -ENOENT) 787 + return ssm2518->enable_gpio; 788 + } else { 789 + ssm2518->enable_gpio = -1; 790 + } 791 + 792 + if (gpio_is_valid(ssm2518->enable_gpio)) { 793 + ret = devm_gpio_request_one(&i2c->dev, ssm2518->enable_gpio, 794 + GPIOF_OUT_INIT_HIGH, "SSM2518 nSD"); 795 + if (ret) 796 + return ret; 797 + } 798 + 799 + i2c_set_clientdata(i2c, ssm2518); 800 + 801 + ssm2518->regmap = devm_regmap_init_i2c(i2c, &ssm2518_regmap_config); 802 + if (IS_ERR(ssm2518->regmap)) 803 + return PTR_ERR(ssm2518->regmap); 804 + 805 + /* 806 + * The reset bit is obviously volatile, but we need to be able to cache 807 + * the other bits in the register, so we can't just mark the whole 808 + * register as volatile. Since this is the only place where we'll ever 809 + * touch the reset bit just bypass the cache for this operation. 810 + */ 811 + regcache_cache_bypass(ssm2518->regmap, true); 812 + ret = regmap_write(ssm2518->regmap, SSM2518_REG_POWER1, 813 + SSM2518_POWER1_RESET); 814 + regcache_cache_bypass(ssm2518->regmap, false); 815 + if (ret) 816 + return ret; 817 + 818 + ret = regmap_update_bits(ssm2518->regmap, SSM2518_REG_POWER2, 819 + SSM2518_POWER2_APWDN, 0x00); 820 + if (ret) 821 + return ret; 822 + 823 + ret = ssm2518_set_power(ssm2518, false); 824 + if (ret) 825 + return ret; 826 + 827 + return snd_soc_register_codec(&i2c->dev, &ssm2518_codec_driver, 828 + &ssm2518_dai, 1); 829 + } 830 + 831 + static int ssm2518_i2c_remove(struct i2c_client *client) 832 + { 833 + snd_soc_unregister_codec(&client->dev); 834 + return 0; 835 + } 836 + 837 + static const struct i2c_device_id ssm2518_i2c_ids[] = { 838 + { "ssm2518", 0 }, 839 + { } 840 + }; 841 + MODULE_DEVICE_TABLE(i2c, ssm2518_i2c_ids); 842 + 843 + static struct i2c_driver ssm2518_driver = { 844 + .driver = { 845 + .name = "ssm2518", 846 + .owner = THIS_MODULE, 847 + }, 848 + .probe = ssm2518_i2c_probe, 849 + .remove = ssm2518_i2c_remove, 850 + .id_table = ssm2518_i2c_ids, 851 + }; 852 + module_i2c_driver(ssm2518_driver); 853 + 854 + MODULE_DESCRIPTION("ASoC SSM2518 driver"); 855 + MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); 856 + MODULE_LICENSE("GPL");
+20
sound/soc/codecs/ssm2518.h
··· 1 + /* 2 + * SSM2518 amplifier audio driver 3 + * 4 + * Copyright 2013 Analog Devices Inc. 5 + * Author: Lars-Peter Clausen <lars@metafoo.de> 6 + * 7 + * Licensed under the GPL-2. 8 + */ 9 + 10 + #ifndef __SND_SOC_CODECS_SSM2518_H__ 11 + #define __SND_SOC_CODECS_SSM2518_H__ 12 + 13 + #define SSM2518_SYSCLK 0 14 + 15 + enum ssm2518_sysclk_src { 16 + SSM2518_SYSCLK_SRC_MCLK = 0, 17 + SSM2518_SYSCLK_SRC_BCLK = 1, 18 + }; 19 + 20 + #endif