1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * LM4857 AMP driver |
4 | * |
5 | * Copyright 2007 Wolfson Microelectronics PLC. |
6 | * Author: Graeme Gregory |
7 | * graeme.gregory@wolfsonmicro.com |
8 | * Copyright 2011 Lars-Peter Clausen <lars@metafoo.de> |
9 | */ |
10 | |
11 | #include <linux/init.h> |
12 | #include <linux/module.h> |
13 | #include <linux/i2c.h> |
14 | #include <linux/regmap.h> |
15 | #include <linux/slab.h> |
16 | |
17 | #include <sound/core.h> |
18 | #include <sound/soc.h> |
19 | #include <sound/tlv.h> |
20 | |
21 | static const struct reg_default lm4857_default_regs[] = { |
22 | { 0x0, 0x00 }, |
23 | { 0x1, 0x00 }, |
24 | { 0x2, 0x00 }, |
25 | { 0x3, 0x00 }, |
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 | |
39 | static const unsigned int lm4857_mode_values[] = { |
40 | 0, |
41 | 6, |
42 | 7, |
43 | 8, |
44 | 9, |
45 | }; |
46 | |
47 | static const char * const lm4857_mode_texts[] = { |
48 | "Off" , |
49 | "Earpiece" , |
50 | "Loudspeaker" , |
51 | "Loudspeaker + Headphone" , |
52 | "Headphone" , |
53 | }; |
54 | |
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); |
60 | |
61 | static const struct snd_soc_dapm_widget lm4857_dapm_widgets[] = { |
62 | SND_SOC_DAPM_INPUT("IN" ), |
63 | |
64 | SND_SOC_DAPM_DEMUX("Mode" , SND_SOC_NOPM, 0, 0, &lm4857_mode_ctrl), |
65 | |
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), |
87 | }; |
88 | |
89 | static const struct snd_soc_dapm_route lm4857_routes[] = { |
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" }, |
96 | }; |
97 | |
98 | static const struct snd_soc_component_driver lm4857_component_driver = { |
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), |
105 | }; |
106 | |
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 | |
118 | static int lm4857_i2c_probe(struct i2c_client *i2c) |
119 | { |
120 | struct regmap *regmap; |
121 | |
122 | regmap = devm_regmap_init_i2c(i2c, &lm4857_regmap_config); |
123 | if (IS_ERR(ptr: regmap)) |
124 | return PTR_ERR(ptr: regmap); |
125 | |
126 | return devm_snd_soc_register_component(dev: &i2c->dev, |
127 | component_driver: &lm4857_component_driver, NULL, num_dai: 0); |
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" , |
139 | }, |
140 | .probe = lm4857_i2c_probe, |
141 | .id_table = lm4857_i2c_id, |
142 | }; |
143 | |
144 | module_i2c_driver(lm4857_i2c_driver); |
145 | |
146 | MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>" ); |
147 | MODULE_DESCRIPTION("LM4857 amplifier driver" ); |
148 | MODULE_LICENSE("GPL" ); |
149 | |