1 /* 2 * LM4857 AMP driver 3 * 4 * Copyright 2007 Wolfson Microelectronics PLC. 5 * Author: Graeme Gregory 6 * graeme.gregory@wolfsonmicro.com 7 * Copyright 2011 Lars-Peter Clausen <lars@metafoo.de> 8 * 9 * This program is free software; you can redistribute it and/or modify it 10 * under the terms of the GNU General Public License as published by the 11 * Free Software Foundation; either version 2 of the License, or (at your 12 * option) any later version. 13 * 14 */ 15 16 #include <linux/init.h> 17 #include <linux/module.h> 18 #include <linux/i2c.h> 19 #include <linux/regmap.h> 20 #include <linux/slab.h> 21 22 #include <sound/core.h> 23 #include <sound/soc.h> 24 #include <sound/tlv.h> 25 26 static const struct reg_default lm4857_default_regs[] = { 27 { 0x0, 0x00 }, 28 { 0x1, 0x00 }, 29 { 0x2, 0x00 }, 30 { 0x3, 0x00 }, 31 }; 32 33 /* The register offsets in the cache array */ 34 #define LM4857_MVOL 0 35 #define LM4857_LVOL 1 36 #define LM4857_RVOL 2 37 #define LM4857_CTRL 3 38 39 /* the shifts required to set these bits */ 40 #define LM4857_3D 5 41 #define LM4857_WAKEUP 5 42 #define LM4857_EPGAIN 4 43 44 static const unsigned int lm4857_mode_values[] = { 45 0, 46 6, 47 7, 48 8, 49 9, 50 }; 51 52 static const char * const lm4857_mode_texts[] = { 53 "Off", 54 "Earpiece", 55 "Loudspeaker", 56 "Loudspeaker + Headphone", 57 "Headphone", 58 }; 59 60 static SOC_VALUE_ENUM_SINGLE_AUTODISABLE_DECL(lm4857_mode_enum, 61 LM4857_CTRL, 0, 0xf, lm4857_mode_texts, lm4857_mode_values); 62 63 static const struct snd_kcontrol_new lm4857_mode_ctrl = 64 SOC_DAPM_ENUM("Mode", lm4857_mode_enum); 65 66 static const struct snd_soc_dapm_widget lm4857_dapm_widgets[] = { 67 SND_SOC_DAPM_INPUT("IN"), 68 69 SND_SOC_DAPM_DEMUX("Mode", SND_SOC_NOPM, 0, 0, &lm4857_mode_ctrl), 70 71 SND_SOC_DAPM_OUTPUT("LS"), 72 SND_SOC_DAPM_OUTPUT("HP"), 73 SND_SOC_DAPM_OUTPUT("EP"), 74 }; 75 76 static const DECLARE_TLV_DB_SCALE(stereo_tlv, -4050, 150, 0); 77 static const DECLARE_TLV_DB_SCALE(mono_tlv, -3450, 150, 0); 78 79 static const struct snd_kcontrol_new lm4857_controls[] = { 80 SOC_SINGLE_TLV("Left Playback Volume", LM4857_LVOL, 0, 31, 0, 81 stereo_tlv), 82 SOC_SINGLE_TLV("Right Playback Volume", LM4857_RVOL, 0, 31, 0, 83 stereo_tlv), 84 SOC_SINGLE_TLV("Mono Playback Volume", LM4857_MVOL, 0, 31, 0, 85 mono_tlv), 86 SOC_SINGLE("Spk 3D Playback Switch", LM4857_LVOL, LM4857_3D, 1, 0), 87 SOC_SINGLE("HP 3D Playback Switch", LM4857_RVOL, LM4857_3D, 1, 0), 88 SOC_SINGLE("Fast Wakeup Playback Switch", LM4857_CTRL, 89 LM4857_WAKEUP, 1, 0), 90 SOC_SINGLE("Earpiece 6dB Playback Switch", LM4857_CTRL, 91 LM4857_EPGAIN, 1, 0), 92 }; 93 94 static const struct snd_soc_dapm_route lm4857_routes[] = { 95 { "Mode", NULL, "IN" }, 96 { "LS", "Loudspeaker", "Mode" }, 97 { "LS", "Loudspeaker + Headphone", "Mode" }, 98 { "HP", "Headphone", "Mode" }, 99 { "HP", "Loudspeaker + Headphone", "Mode" }, 100 { "EP", "Earpiece", "Mode" }, 101 }; 102 103 static const struct snd_soc_component_driver lm4857_component_driver = { 104 .controls = lm4857_controls, 105 .num_controls = ARRAY_SIZE(lm4857_controls), 106 .dapm_widgets = lm4857_dapm_widgets, 107 .num_dapm_widgets = ARRAY_SIZE(lm4857_dapm_widgets), 108 .dapm_routes = lm4857_routes, 109 .num_dapm_routes = ARRAY_SIZE(lm4857_routes), 110 }; 111 112 static const struct regmap_config lm4857_regmap_config = { 113 .val_bits = 6, 114 .reg_bits = 2, 115 116 .max_register = LM4857_CTRL, 117 118 .cache_type = REGCACHE_FLAT, 119 .reg_defaults = lm4857_default_regs, 120 .num_reg_defaults = ARRAY_SIZE(lm4857_default_regs), 121 }; 122 123 static int lm4857_i2c_probe(struct i2c_client *i2c, 124 const struct i2c_device_id *id) 125 { 126 struct regmap *regmap; 127 128 regmap = devm_regmap_init_i2c(i2c, &lm4857_regmap_config); 129 if (IS_ERR(regmap)) 130 return PTR_ERR(regmap); 131 132 return devm_snd_soc_register_component(&i2c->dev, 133 &lm4857_component_driver, NULL, 0); 134 } 135 136 static const struct i2c_device_id lm4857_i2c_id[] = { 137 { "lm4857", 0 }, 138 { } 139 }; 140 MODULE_DEVICE_TABLE(i2c, lm4857_i2c_id); 141 142 static struct i2c_driver lm4857_i2c_driver = { 143 .driver = { 144 .name = "lm4857", 145 }, 146 .probe = lm4857_i2c_probe, 147 .id_table = lm4857_i2c_id, 148 }; 149 150 module_i2c_driver(lm4857_i2c_driver); 151 152 MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); 153 MODULE_DESCRIPTION("LM4857 amplifier driver"); 154 MODULE_LICENSE("GPL"); 155