]>
Commit | Line | Data |
---|---|---|
9b0a25f0 LPC |
1 | /* |
2 | * LM4857 AMP driver | |
3 | * | |
4 | * Copyright 2007 Wolfson Microelectronics PLC. | |
5 | * Author: Graeme Gregory | |
9a185b9a | 6 | * [email protected] |
9b0a25f0 LPC |
7 | * Copyright 2011 Lars-Peter Clausen <[email protected]> |
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> | |
9b270968 | 19 | #include <linux/regmap.h> |
9b0a25f0 LPC |
20 | #include <linux/slab.h> |
21 | ||
22 | #include <sound/core.h> | |
23 | #include <sound/soc.h> | |
24 | #include <sound/tlv.h> | |
25 | ||
9b270968 LPC |
26 | static const struct reg_default lm4857_default_regs[] = { |
27 | { 0x0, 0x00 }, | |
28 | { 0x1, 0x00 }, | |
29 | { 0x2, 0x00 }, | |
30 | { 0x3, 0x00 }, | |
9b0a25f0 LPC |
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 | ||
0eb93ef0 LPC |
44 | static const unsigned int lm4857_mode_values[] = { |
45 | 0, | |
46 | 6, | |
47 | 7, | |
48 | 8, | |
49 | 9, | |
50 | }; | |
9b0a25f0 | 51 | |
0eb93ef0 LPC |
52 | static const char * const lm4857_mode_texts[] = { |
53 | "Off", | |
9b0a25f0 LPC |
54 | "Earpiece", |
55 | "Loudspeaker", | |
56 | "Loudspeaker + Headphone", | |
57 | "Headphone", | |
58 | }; | |
59 | ||
0eb93ef0 LPC |
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); | |
9b0a25f0 LPC |
65 | |
66 | static const struct snd_soc_dapm_widget lm4857_dapm_widgets[] = { | |
67 | SND_SOC_DAPM_INPUT("IN"), | |
68 | ||
0eb93ef0 LPC |
69 | SND_SOC_DAPM_DEMUX("Mode", SND_SOC_NOPM, 0, 0, &lm4857_mode_ctrl), |
70 | ||
9b0a25f0 LPC |
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), | |
9b0a25f0 LPC |
92 | }; |
93 | ||
9b0a25f0 | 94 | static const struct snd_soc_dapm_route lm4857_routes[] = { |
0eb93ef0 LPC |
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" }, | |
9b0a25f0 LPC |
101 | }; |
102 | ||
6e37f933 | 103 | static const struct snd_soc_component_driver lm4857_component_driver = { |
07ccc0f4 LPC |
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), | |
9b0a25f0 LPC |
110 | }; |
111 | ||
9b270968 LPC |
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 | ||
7a79e94e BP |
123 | static int lm4857_i2c_probe(struct i2c_client *i2c, |
124 | const struct i2c_device_id *id) | |
9b0a25f0 | 125 | { |
0eb93ef0 | 126 | struct regmap *regmap; |
9b0a25f0 | 127 | |
0eb93ef0 LPC |
128 | regmap = devm_regmap_init_i2c(i2c, &lm4857_regmap_config); |
129 | if (IS_ERR(regmap)) | |
130 | return PTR_ERR(regmap); | |
9b0a25f0 | 131 | |
08a1e646 LPC |
132 | return devm_snd_soc_register_component(&i2c->dev, |
133 | &lm4857_component_driver, NULL, 0); | |
9b0a25f0 LPC |
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", | |
9b0a25f0 LPC |
145 | }, |
146 | .probe = lm4857_i2c_probe, | |
9b0a25f0 LPC |
147 | .id_table = lm4857_i2c_id, |
148 | }; | |
149 | ||
f6ec139f | 150 | module_i2c_driver(lm4857_i2c_driver); |
9b0a25f0 LPC |
151 | |
152 | MODULE_AUTHOR("Lars-Peter Clausen <[email protected]>"); | |
153 | MODULE_DESCRIPTION("LM4857 amplifier driver"); | |
154 | MODULE_LICENSE("GPL"); |