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

ASoC: MAX9877: add MAX9877 amp driver

The MAX9877 combines a high-efficiency Class D audio power amplifier
with a stereo Class AB capacitor-less DirectDrive headphone amplifier.

The max9877_add_controls() is called to register the MAX9877 specific
controls on machine specific init() of the machine driver.

The datasheet for the MAX9877 can find at the following url:
http://datasheets.maxim-ic.com/en/ds/MAX9877.pdf

[Slight edit to sort the ALL_CODECS entries -- broonie.]

Signed-off-by: Joonyoung Shim <jy0922.shim@samsung.com>
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>

authored by

Joonyoung Shim and committed by
Mark Brown
9db9ed97 3ce91d5a

+318
+5
sound/soc/codecs/Kconfig
··· 17 17 select SND_SOC_AK4104 if SPI_MASTER 18 18 select SND_SOC_AK4535 if I2C 19 19 select SND_SOC_CS4270 if I2C 20 + select SND_SOC_MAX9877 if I2C 20 21 select SND_SOC_PCM3008 21 22 select SND_SOC_SPDIF 22 23 select SND_SOC_SSM2602 if I2C ··· 188 187 tristate 189 188 190 189 config SND_SOC_WM9713 190 + tristate 191 + 192 + # Amp 193 + config SND_SOC_MAX9877 191 194 tristate
+6
sound/soc/codecs/Makefile
··· 38 38 snd-soc-wm9712-objs := wm9712.o 39 39 snd-soc-wm9713-objs := wm9713.o 40 40 41 + # Amp 42 + snd-soc-max9877-objs := max9877.o 43 + 41 44 obj-$(CONFIG_SND_SOC_AC97_CODEC) += snd-soc-ac97.o 42 45 obj-$(CONFIG_SND_SOC_AD1980) += snd-soc-ad1980.o 43 46 obj-$(CONFIG_SND_SOC_AD73311) += snd-soc-ad73311.o ··· 80 77 obj-$(CONFIG_SND_SOC_WM9705) += snd-soc-wm9705.o 81 78 obj-$(CONFIG_SND_SOC_WM9712) += snd-soc-wm9712.o 82 79 obj-$(CONFIG_SND_SOC_WM9713) += snd-soc-wm9713.o 80 + 81 + # Amp 82 + obj-$(CONFIG_SND_SOC_MAX9877) += snd-soc-max9877.o
+270
sound/soc/codecs/max9877.c
··· 1 + /* 2 + * max9877.c -- amp driver for max9877 3 + * 4 + * Copyright (C) 2009 Samsung Electronics Co.Ltd 5 + * Author: Joonyoung Shim <jy0922.shim@samsung.com> 6 + * 7 + * This program is free software; you can redistribute it and/or modify it 8 + * under the terms of the GNU General Public License as published by the 9 + * Free Software Foundation; either version 2 of the License, or (at your 10 + * option) any later version. 11 + * 12 + */ 13 + 14 + #include <linux/module.h> 15 + #include <linux/init.h> 16 + #include <linux/i2c.h> 17 + #include <sound/soc.h> 18 + #include <sound/tlv.h> 19 + 20 + #include "max9877.h" 21 + 22 + static struct i2c_client *i2c; 23 + 24 + static u8 max9877_regs[5] = { 0x40, 0x00, 0x00, 0x00, 0x49 }; 25 + 26 + static void max9877_write_regs(void) 27 + { 28 + if (i2c_master_send(i2c, max9877_regs, 5) != 5) 29 + dev_err(&i2c->dev, "i2c write failed\n"); 30 + } 31 + 32 + static int max9877_get_reg(struct snd_kcontrol *kcontrol, 33 + struct snd_ctl_elem_value *ucontrol) 34 + { 35 + struct soc_mixer_control *mc = 36 + (struct soc_mixer_control *)kcontrol->private_value; 37 + int reg = mc->reg; 38 + int reg2 = mc->rreg; 39 + int shift = mc->shift; 40 + int mask = mc->max; 41 + 42 + ucontrol->value.integer.value[0] = (max9877_regs[reg] >> shift) & mask; 43 + 44 + if (reg2) 45 + ucontrol->value.integer.value[1] = 46 + (max9877_regs[reg2] >> shift) & mask; 47 + 48 + return 0; 49 + } 50 + 51 + static int max9877_set_reg(struct snd_kcontrol *kcontrol, 52 + struct snd_ctl_elem_value *ucontrol) 53 + { 54 + struct soc_mixer_control *mc = 55 + (struct soc_mixer_control *)kcontrol->private_value; 56 + int reg = mc->reg; 57 + int reg2 = mc->rreg; 58 + int shift = mc->shift; 59 + int mask = mc->max; 60 + int change = 1; 61 + int change2 = 1; 62 + int ret = 0; 63 + 64 + if (((max9877_regs[reg] >> shift) & mask) == 65 + ucontrol->value.integer.value[0]) 66 + change = 0; 67 + 68 + if (reg2) 69 + if (((max9877_regs[reg2] >> shift) & mask) == 70 + ucontrol->value.integer.value[1]) 71 + change2 = 0; 72 + 73 + if (change) { 74 + max9877_regs[reg] &= ~(mask << shift); 75 + max9877_regs[reg] |= ucontrol->value.integer.value[0] << shift; 76 + ret = change; 77 + } 78 + 79 + if (reg2 && change2) { 80 + max9877_regs[reg2] &= ~(mask << shift); 81 + max9877_regs[reg2] |= ucontrol->value.integer.value[1] << shift; 82 + ret = change2; 83 + } 84 + 85 + if (ret) 86 + max9877_write_regs(); 87 + 88 + return ret; 89 + } 90 + 91 + static int max9877_get_out_mode(struct snd_kcontrol *kcontrol, 92 + struct snd_ctl_elem_value *ucontrol) 93 + { 94 + u8 value = max9877_regs[MAX9877_OUTPUT_MODE] & MAX9877_OUTMODE_MASK; 95 + 96 + if (value) 97 + value -= 1; 98 + 99 + ucontrol->value.integer.value[0] = value; 100 + return 0; 101 + } 102 + 103 + static int max9877_set_out_mode(struct snd_kcontrol *kcontrol, 104 + struct snd_ctl_elem_value *ucontrol) 105 + { 106 + u8 value = ucontrol->value.integer.value[0]; 107 + 108 + if (value) 109 + value += 1; 110 + 111 + if ((max9877_regs[MAX9877_OUTPUT_MODE] & MAX9877_OUTMODE_MASK) == value) 112 + return 0; 113 + 114 + max9877_regs[MAX9877_OUTPUT_MODE] |= value; 115 + max9877_write_regs(); 116 + return 1; 117 + } 118 + 119 + static int max9877_get_osc_mode(struct snd_kcontrol *kcontrol, 120 + struct snd_ctl_elem_value *ucontrol) 121 + { 122 + u8 value = (max9877_regs[MAX9877_OUTPUT_MODE] & MAX9877_OSC_MASK); 123 + 124 + value = value >> MAX9877_OSC_OFFSET; 125 + 126 + ucontrol->value.integer.value[0] = value; 127 + return 0; 128 + } 129 + 130 + static int max9877_set_osc_mode(struct snd_kcontrol *kcontrol, 131 + struct snd_ctl_elem_value *ucontrol) 132 + { 133 + u8 value = ucontrol->value.integer.value[0]; 134 + 135 + value = value << MAX9877_OSC_OFFSET; 136 + if ((max9877_regs[MAX9877_OUTPUT_MODE] & MAX9877_OSC_MASK) == value) 137 + return 0; 138 + 139 + max9877_regs[MAX9877_OUTPUT_MODE] |= value; 140 + max9877_write_regs(); 141 + return 1; 142 + } 143 + 144 + static const unsigned int max9877_pgain_tlv[] = { 145 + TLV_DB_RANGE_HEAD(2), 146 + 0, 1, TLV_DB_SCALE_ITEM(0, 900, 0), 147 + 2, 2, TLV_DB_SCALE_ITEM(2000, 0, 0), 148 + }; 149 + 150 + static const unsigned int max9877_output_tlv[] = { 151 + TLV_DB_RANGE_HEAD(4), 152 + 0, 7, TLV_DB_SCALE_ITEM(-7900, 400, 1), 153 + 8, 15, TLV_DB_SCALE_ITEM(-4700, 300, 0), 154 + 16, 23, TLV_DB_SCALE_ITEM(-2300, 200, 0), 155 + 24, 31, TLV_DB_SCALE_ITEM(-700, 100, 0), 156 + }; 157 + 158 + static const char *max9877_out_mode[] = { 159 + "INA -> SPK", 160 + "INA -> HP", 161 + "INA -> SPK and HP", 162 + "INB -> SPK", 163 + "INB -> HP", 164 + "INB -> SPK and HP", 165 + "INA + INB -> SPK", 166 + "INA + INB -> HP", 167 + "INA + INB -> SPK and HP", 168 + }; 169 + 170 + static const char *max9877_osc_mode[] = { 171 + "1176KHz", 172 + "1100KHz", 173 + "700KHz", 174 + }; 175 + 176 + static const struct soc_enum max9877_enum[] = { 177 + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(max9877_out_mode), max9877_out_mode), 178 + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(max9877_osc_mode), max9877_osc_mode), 179 + }; 180 + 181 + static const struct snd_kcontrol_new max9877_controls[] = { 182 + SOC_SINGLE_EXT_TLV("MAX9877 PGAINA Playback Volume", 183 + MAX9877_INPUT_MODE, 0, 2, 0, 184 + max9877_get_reg, max9877_set_reg, max9877_pgain_tlv), 185 + SOC_SINGLE_EXT_TLV("MAX9877 PGAINB Playback Volume", 186 + MAX9877_INPUT_MODE, 2, 2, 0, 187 + max9877_get_reg, max9877_set_reg, max9877_pgain_tlv), 188 + SOC_SINGLE_EXT_TLV("MAX9877 Amp Speaker Playback Volume", 189 + MAX9877_SPK_VOLUME, 0, 31, 0, 190 + max9877_get_reg, max9877_set_reg, max9877_output_tlv), 191 + SOC_DOUBLE_R_EXT_TLV("MAX9877 Amp HP Playback Volume", 192 + MAX9877_HPL_VOLUME, MAX9877_HPR_VOLUME, 0, 31, 0, 193 + max9877_get_reg, max9877_set_reg, max9877_output_tlv), 194 + SOC_SINGLE_EXT("MAX9877 INB Stereo Switch", 195 + MAX9877_INPUT_MODE, 4, 1, 1, 196 + max9877_get_reg, max9877_set_reg), 197 + SOC_SINGLE_EXT("MAX9877 INA Stereo Switch", 198 + MAX9877_INPUT_MODE, 5, 1, 1, 199 + max9877_get_reg, max9877_set_reg), 200 + SOC_SINGLE_EXT("MAX9877 Zero-crossing detection Switch", 201 + MAX9877_INPUT_MODE, 6, 1, 0, 202 + max9877_get_reg, max9877_set_reg), 203 + SOC_SINGLE_EXT("MAX9877 Bypass Mode Switch", 204 + MAX9877_OUTPUT_MODE, 6, 1, 0, 205 + max9877_get_reg, max9877_set_reg), 206 + SOC_SINGLE_EXT("MAX9877 Shutdown Mode Switch", 207 + MAX9877_OUTPUT_MODE, 7, 1, 1, 208 + max9877_get_reg, max9877_set_reg), 209 + SOC_ENUM_EXT("MAX9877 Output Mode", max9877_enum[0], 210 + max9877_get_out_mode, max9877_set_out_mode), 211 + SOC_ENUM_EXT("MAX9877 Oscillator Mode", max9877_enum[1], 212 + max9877_get_osc_mode, max9877_set_osc_mode), 213 + }; 214 + 215 + /* This function is called from ASoC machine driver */ 216 + int max9877_add_controls(struct snd_soc_codec *codec) 217 + { 218 + return snd_soc_add_controls(codec, max9877_controls, 219 + ARRAY_SIZE(max9877_controls)); 220 + } 221 + EXPORT_SYMBOL_GPL(max9877_add_controls); 222 + 223 + static int __devinit max9877_i2c_probe(struct i2c_client *client, 224 + const struct i2c_device_id *id) 225 + { 226 + i2c = client; 227 + 228 + max9877_write_regs(); 229 + 230 + return 0; 231 + } 232 + 233 + static __devexit int max9877_i2c_remove(struct i2c_client *client) 234 + { 235 + i2c = NULL; 236 + 237 + return 0; 238 + } 239 + 240 + static const struct i2c_device_id max9877_i2c_id[] = { 241 + { "max9877", 0 }, 242 + { } 243 + }; 244 + MODULE_DEVICE_TABLE(i2c, max9877_i2c_id); 245 + 246 + static struct i2c_driver max9877_i2c_driver = { 247 + .driver = { 248 + .name = "max9877", 249 + .owner = THIS_MODULE, 250 + }, 251 + .probe = max9877_i2c_probe, 252 + .remove = __devexit_p(max9877_i2c_remove), 253 + .id_table = max9877_i2c_id, 254 + }; 255 + 256 + static int __init max9877_init(void) 257 + { 258 + return i2c_add_driver(&max9877_i2c_driver); 259 + } 260 + module_init(max9877_init); 261 + 262 + static void __exit max9877_exit(void) 263 + { 264 + i2c_del_driver(&max9877_i2c_driver); 265 + } 266 + module_exit(max9877_exit); 267 + 268 + MODULE_DESCRIPTION("ASoC MAX9877 amp driver"); 269 + MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>"); 270 + MODULE_LICENSE("GPL");
+37
sound/soc/codecs/max9877.h
··· 1 + /* 2 + * max9877.h -- amp driver for max9877 3 + * 4 + * Copyright (C) 2009 Samsung Electronics Co.Ltd 5 + * Author: Joonyoung Shim <jy0922.shim@samsung.com> 6 + * 7 + * This program is free software; you can redistribute it and/or modify it 8 + * under the terms of the GNU General Public License as published by the 9 + * Free Software Foundation; either version 2 of the License, or (at your 10 + * option) any later version. 11 + * 12 + */ 13 + 14 + #ifndef _MAX9877_H 15 + #define _MAX9877_H 16 + 17 + #define MAX9877_INPUT_MODE 0x00 18 + #define MAX9877_SPK_VOLUME 0x01 19 + #define MAX9877_HPL_VOLUME 0x02 20 + #define MAX9877_HPR_VOLUME 0x03 21 + #define MAX9877_OUTPUT_MODE 0x04 22 + 23 + /* MAX9877_INPUT_MODE */ 24 + #define MAX9877_INB (1 << 4) 25 + #define MAX9877_INA (1 << 5) 26 + #define MAX9877_ZCD (1 << 6) 27 + 28 + /* MAX9877_OUTPUT_MODE */ 29 + #define MAX9877_OUTMODE_MASK (15 << 0) 30 + #define MAX9877_OSC_MASK (3 << 4) 31 + #define MAX9877_OSC_OFFSET 4 32 + #define MAX9877_BYPASS (1 << 6) 33 + #define MAX9877_SHDN (1 << 7) 34 + 35 + extern int max9877_add_controls(struct snd_soc_codec *codec); 36 + 37 + #endif