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