1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (c) 2020 Bootlin SA
4 * Author: Alexandre Belloni <alexandre.belloni@bootlin.com>
7 #include <linux/gpio/consumer.h>
8 #include <linux/module.h>
9 #include <linux/mux/driver.h>
10 #include <linux/regulator/consumer.h>
11 #include <sound/soc.h>
13 #define MUX_TEXT_SIZE 2
14 #define MUX_WIDGET_SIZE 4
15 #define MUX_ROUTE_SIZE 3
17 struct gpio_desc
*gpiod_mux
;
19 const char *mux_texts
[MUX_TEXT_SIZE
];
20 unsigned int idle_state
;
21 struct soc_enum mux_enum
;
22 struct snd_kcontrol_new mux_mux
;
23 struct snd_soc_dapm_widget mux_widgets
[MUX_WIDGET_SIZE
];
24 struct snd_soc_dapm_route mux_routes
[MUX_ROUTE_SIZE
];
25 struct snd_soc_component_driver mux_driver
;
28 static const char * const simple_mux_texts
[MUX_TEXT_SIZE
] = {
32 static SOC_ENUM_SINGLE_EXT_DECL(simple_mux_enum
, simple_mux_texts
);
34 static int simple_mux_control_get(struct snd_kcontrol
*kcontrol
,
35 struct snd_ctl_elem_value
*ucontrol
)
37 struct snd_soc_dapm_context
*dapm
= snd_soc_dapm_kcontrol_dapm(kcontrol
);
38 struct snd_soc_component
*c
= snd_soc_dapm_to_component(dapm
);
39 struct simple_mux
*priv
= snd_soc_component_get_drvdata(c
);
41 ucontrol
->value
.enumerated
.item
[0] = priv
->mux
;
46 static int simple_mux_control_put(struct snd_kcontrol
*kcontrol
,
47 struct snd_ctl_elem_value
*ucontrol
)
49 struct snd_soc_dapm_context
*dapm
= snd_soc_dapm_kcontrol_dapm(kcontrol
);
50 struct soc_enum
*e
= (struct soc_enum
*)kcontrol
->private_value
;
51 struct snd_soc_component
*c
= snd_soc_dapm_to_component(dapm
);
52 struct simple_mux
*priv
= snd_soc_component_get_drvdata(c
);
54 if (ucontrol
->value
.enumerated
.item
[0] > e
->items
)
57 if (priv
->mux
== ucontrol
->value
.enumerated
.item
[0])
60 priv
->mux
= ucontrol
->value
.enumerated
.item
[0];
62 if (priv
->idle_state
!= MUX_IDLE_AS_IS
&& dapm
->bias_level
< SND_SOC_BIAS_PREPARE
)
65 gpiod_set_value_cansleep(priv
->gpiod_mux
, priv
->mux
);
67 return snd_soc_dapm_mux_update_power(dapm
, kcontrol
,
68 ucontrol
->value
.enumerated
.item
[0],
72 static unsigned int simple_mux_read(struct snd_soc_component
*component
,
75 struct simple_mux
*priv
= snd_soc_component_get_drvdata(component
);
80 static const struct snd_kcontrol_new simple_mux_mux
=
81 SOC_DAPM_ENUM_EXT("Muxer", simple_mux_enum
, simple_mux_control_get
, simple_mux_control_put
);
83 static int simple_mux_event(struct snd_soc_dapm_widget
*w
,
84 struct snd_kcontrol
*kcontrol
, int event
)
86 struct snd_soc_component
*c
= snd_soc_dapm_to_component(w
->dapm
);
87 struct simple_mux
*priv
= snd_soc_component_get_drvdata(c
);
89 if (priv
->idle_state
!= MUX_IDLE_AS_IS
) {
91 case SND_SOC_DAPM_PRE_PMU
:
92 gpiod_set_value_cansleep(priv
->gpiod_mux
, priv
->mux
);
94 case SND_SOC_DAPM_POST_PMD
:
95 gpiod_set_value_cansleep(priv
->gpiod_mux
, priv
->idle_state
);
105 static const struct snd_soc_dapm_widget simple_mux_dapm_widgets
[MUX_WIDGET_SIZE
] = {
106 SND_SOC_DAPM_INPUT("IN1"),
107 SND_SOC_DAPM_INPUT("IN2"),
108 SND_SOC_DAPM_MUX_E("MUX", SND_SOC_NOPM
, 0, 0, &simple_mux_mux
, // see simple_mux_probe()
109 simple_mux_event
, SND_SOC_DAPM_PRE_PMU
| SND_SOC_DAPM_POST_PMD
),
110 SND_SOC_DAPM_OUTPUT("OUT"),
113 static const struct snd_soc_dapm_route simple_mux_dapm_routes
[MUX_ROUTE_SIZE
] = {
114 { "OUT", NULL
, "MUX" },
115 { "MUX", "Input 1", "IN1" }, // see simple_mux_probe()
116 { "MUX", "Input 2", "IN2" }, // see simple_mux_probe()
119 static int simple_mux_probe(struct platform_device
*pdev
)
121 struct device
*dev
= &pdev
->dev
;
122 struct device_node
*np
= dev
->of_node
;
123 struct simple_mux
*priv
;
126 priv
= devm_kzalloc(dev
, sizeof(*priv
), GFP_KERNEL
);
130 dev_set_drvdata(dev
, priv
);
132 priv
->gpiod_mux
= devm_gpiod_get(dev
, "mux", GPIOD_OUT_LOW
);
133 if (IS_ERR(priv
->gpiod_mux
))
134 return dev_err_probe(dev
, PTR_ERR(priv
->gpiod_mux
),
135 "Failed to get 'mux' gpio");
137 /* Copy default settings */
138 memcpy(&priv
->mux_texts
, &simple_mux_texts
, sizeof(priv
->mux_texts
));
139 memcpy(&priv
->mux_enum
, &simple_mux_enum
, sizeof(priv
->mux_enum
));
140 memcpy(&priv
->mux_mux
, &simple_mux_mux
, sizeof(priv
->mux_mux
));
141 memcpy(&priv
->mux_widgets
, &simple_mux_dapm_widgets
, sizeof(priv
->mux_widgets
));
142 memcpy(&priv
->mux_routes
, &simple_mux_dapm_routes
, sizeof(priv
->mux_routes
));
144 priv
->mux_driver
.dapm_widgets
= priv
->mux_widgets
;
145 priv
->mux_driver
.num_dapm_widgets
= MUX_WIDGET_SIZE
;
146 priv
->mux_driver
.dapm_routes
= priv
->mux_routes
;
147 priv
->mux_driver
.num_dapm_routes
= MUX_ROUTE_SIZE
;
148 priv
->mux_driver
.read
= simple_mux_read
;
150 /* Overwrite text ("Input 1", "Input 2") if property exists */
151 of_property_read_string_array(np
, "state-labels", priv
->mux_texts
, MUX_TEXT_SIZE
);
153 ret
= of_property_read_u32(np
, "idle-state", &priv
->idle_state
);
155 priv
->idle_state
= MUX_IDLE_AS_IS
;
156 } else if (priv
->idle_state
!= MUX_IDLE_AS_IS
&& priv
->idle_state
>= 2) {
157 dev_err(dev
, "invalid idle-state %u\n", priv
->idle_state
);
161 /* switch to use priv data instead of default */
162 priv
->mux_enum
.texts
= priv
->mux_texts
;
163 priv
->mux_mux
.private_value
= (unsigned long)&priv
->mux_enum
;
164 priv
->mux_widgets
[2].kcontrol_news
= &priv
->mux_mux
;
165 priv
->mux_routes
[1].control
= priv
->mux_texts
[0]; // "Input 1"
166 priv
->mux_routes
[2].control
= priv
->mux_texts
[1]; // "Input 2"
168 return devm_snd_soc_register_component(dev
, &priv
->mux_driver
, NULL
, 0);
172 static const struct of_device_id simple_mux_ids
[] = {
173 { .compatible
= "simple-audio-mux", },
176 MODULE_DEVICE_TABLE(of
, simple_mux_ids
);
179 static struct platform_driver simple_mux_driver
= {
181 .name
= "simple-mux",
182 .of_match_table
= of_match_ptr(simple_mux_ids
),
184 .probe
= simple_mux_probe
,
187 module_platform_driver(simple_mux_driver
);
189 MODULE_DESCRIPTION("ASoC Simple Audio Mux driver");
190 MODULE_AUTHOR("Alexandre Belloni <alexandre.belloni@bootlin.com>");
191 MODULE_LICENSE("GPL");