1d6869352SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2f3d9478bSJohannes Berg /*
3f3d9478bSJohannes Berg * soundbus
4f3d9478bSJohannes Berg *
5f3d9478bSJohannes Berg * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
6f3d9478bSJohannes Berg */
7f3d9478bSJohannes Berg
8f3d9478bSJohannes Berg #include <linux/module.h>
9f3d9478bSJohannes Berg #include "soundbus.h"
10f3d9478bSJohannes Berg
11f3d9478bSJohannes Berg MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>");
12f3d9478bSJohannes Berg MODULE_LICENSE("GPL");
13f3d9478bSJohannes Berg MODULE_DESCRIPTION("Apple Soundbus");
14f3d9478bSJohannes Berg
soundbus_dev_get(struct soundbus_dev * dev)15f3d9478bSJohannes Berg struct soundbus_dev *soundbus_dev_get(struct soundbus_dev *dev)
16f3d9478bSJohannes Berg {
17f3d9478bSJohannes Berg struct device *tmp;
18f3d9478bSJohannes Berg
19f3d9478bSJohannes Berg if (!dev)
20f3d9478bSJohannes Berg return NULL;
21f3d9478bSJohannes Berg tmp = get_device(&dev->ofdev.dev);
22f3d9478bSJohannes Berg if (tmp)
23f3d9478bSJohannes Berg return to_soundbus_device(tmp);
24f3d9478bSJohannes Berg else
25f3d9478bSJohannes Berg return NULL;
26f3d9478bSJohannes Berg }
27f3d9478bSJohannes Berg EXPORT_SYMBOL_GPL(soundbus_dev_get);
28f3d9478bSJohannes Berg
soundbus_dev_put(struct soundbus_dev * dev)29f3d9478bSJohannes Berg void soundbus_dev_put(struct soundbus_dev *dev)
30f3d9478bSJohannes Berg {
31f3d9478bSJohannes Berg if (dev)
32f3d9478bSJohannes Berg put_device(&dev->ofdev.dev);
33f3d9478bSJohannes Berg }
34f3d9478bSJohannes Berg EXPORT_SYMBOL_GPL(soundbus_dev_put);
35f3d9478bSJohannes Berg
soundbus_probe(struct device * dev)36f3d9478bSJohannes Berg static int soundbus_probe(struct device *dev)
37f3d9478bSJohannes Berg {
38f3d9478bSJohannes Berg int error = -ENODEV;
39f3d9478bSJohannes Berg struct soundbus_driver *drv;
40f3d9478bSJohannes Berg struct soundbus_dev *soundbus_dev;
41f3d9478bSJohannes Berg
42f3d9478bSJohannes Berg drv = to_soundbus_driver(dev->driver);
43f3d9478bSJohannes Berg soundbus_dev = to_soundbus_device(dev);
44f3d9478bSJohannes Berg
45f3d9478bSJohannes Berg if (!drv->probe)
46f3d9478bSJohannes Berg return error;
47f3d9478bSJohannes Berg
48f3d9478bSJohannes Berg soundbus_dev_get(soundbus_dev);
49f3d9478bSJohannes Berg
50f3d9478bSJohannes Berg error = drv->probe(soundbus_dev);
51f3d9478bSJohannes Berg if (error)
52f3d9478bSJohannes Berg soundbus_dev_put(soundbus_dev);
53f3d9478bSJohannes Berg
54f3d9478bSJohannes Berg return error;
55f3d9478bSJohannes Berg }
56f3d9478bSJohannes Berg
57f3d9478bSJohannes Berg
soundbus_uevent(const struct device * dev,struct kobj_uevent_env * env)58*2a81ada3SGreg Kroah-Hartman static int soundbus_uevent(const struct device *dev, struct kobj_uevent_env *env)
59f3d9478bSJohannes Berg {
60*2a81ada3SGreg Kroah-Hartman const struct soundbus_dev * soundbus_dev;
61*2a81ada3SGreg Kroah-Hartman const struct platform_device * of;
62a7edd0e6SStephen Rothwell const char *compat;
637eff2e7aSKay Sievers int retval = 0;
64bf62456eSEric Rannaud int cplen, seen = 0;
65f3d9478bSJohannes Berg
66f3d9478bSJohannes Berg if (!dev)
67f3d9478bSJohannes Berg return -ENODEV;
68f3d9478bSJohannes Berg
69f3d9478bSJohannes Berg soundbus_dev = to_soundbus_device(dev);
70f3d9478bSJohannes Berg if (!soundbus_dev)
71f3d9478bSJohannes Berg return -ENODEV;
72f3d9478bSJohannes Berg
73f3d9478bSJohannes Berg of = &soundbus_dev->ofdev;
74f3d9478bSJohannes Berg
75f3d9478bSJohannes Berg /* stuff we want to pass to /sbin/hotplug */
76192a7122SRob Herring retval = add_uevent_var(env, "OF_NAME=%pOFn", of->dev.of_node);
77bf62456eSEric Rannaud if (retval)
78bf62456eSEric Rannaud return retval;
79f3d9478bSJohannes Berg
80c12faa2bSRob Herring retval = add_uevent_var(env, "OF_TYPE=%s", of_node_get_device_type(of->dev.of_node));
81bf62456eSEric Rannaud if (retval)
82bf62456eSEric Rannaud return retval;
83f3d9478bSJohannes Berg
84f3d9478bSJohannes Berg /* Since the compatible field can contain pretty much anything
85f3d9478bSJohannes Berg * it's not really legal to split it out with commas. We split it
86f3d9478bSJohannes Berg * up using a number of environment variables instead. */
87f3d9478bSJohannes Berg
8861c7a080SGrant Likely compat = of_get_property(of->dev.of_node, "compatible", &cplen);
89f3d9478bSJohannes Berg while (compat && cplen > 0) {
907eff2e7aSKay Sievers int tmp = env->buflen;
917eff2e7aSKay Sievers retval = add_uevent_var(env, "OF_COMPATIBLE_%d=%s", seen, compat);
92bf62456eSEric Rannaud if (retval)
93bf62456eSEric Rannaud return retval;
947eff2e7aSKay Sievers compat += env->buflen - tmp;
957eff2e7aSKay Sievers cplen -= env->buflen - tmp;
96bf62456eSEric Rannaud seen += 1;
97f3d9478bSJohannes Berg }
98f3d9478bSJohannes Berg
997eff2e7aSKay Sievers retval = add_uevent_var(env, "OF_COMPATIBLE_N=%d", seen);
100bf62456eSEric Rannaud if (retval)
101bf62456eSEric Rannaud return retval;
1027eff2e7aSKay Sievers retval = add_uevent_var(env, "MODALIAS=%s", soundbus_dev->modalias);
103f3d9478bSJohannes Berg
104bf62456eSEric Rannaud return retval;
105f3d9478bSJohannes Berg }
106f3d9478bSJohannes Berg
soundbus_device_remove(struct device * dev)107fc7a6209SUwe Kleine-König static void soundbus_device_remove(struct device *dev)
108f3d9478bSJohannes Berg {
109f3d9478bSJohannes Berg struct soundbus_dev * soundbus_dev = to_soundbus_device(dev);
110f3d9478bSJohannes Berg struct soundbus_driver * drv = to_soundbus_driver(dev->driver);
111f3d9478bSJohannes Berg
112f3d9478bSJohannes Berg if (dev->driver && drv->remove)
113f3d9478bSJohannes Berg drv->remove(soundbus_dev);
114f3d9478bSJohannes Berg soundbus_dev_put(soundbus_dev);
115f3d9478bSJohannes Berg }
116f3d9478bSJohannes Berg
soundbus_device_shutdown(struct device * dev)117f3d9478bSJohannes Berg static void soundbus_device_shutdown(struct device *dev)
118f3d9478bSJohannes Berg {
119f3d9478bSJohannes Berg struct soundbus_dev * soundbus_dev = to_soundbus_device(dev);
120f3d9478bSJohannes Berg struct soundbus_driver * drv = to_soundbus_driver(dev->driver);
121f3d9478bSJohannes Berg
122f3d9478bSJohannes Berg if (dev->driver && drv->shutdown)
123f3d9478bSJohannes Berg drv->shutdown(soundbus_dev);
124f3d9478bSJohannes Berg }
125f3d9478bSJohannes Berg
126a038b979SQuentin Lambert /* soundbus_dev_attrs is declared in sysfs.c */
127a038b979SQuentin Lambert ATTRIBUTE_GROUPS(soundbus_dev);
128f3d9478bSJohannes Berg static struct bus_type soundbus_bus_type = {
129f3d9478bSJohannes Berg .name = "aoa-soundbus",
130f3d9478bSJohannes Berg .probe = soundbus_probe,
131f3d9478bSJohannes Berg .uevent = soundbus_uevent,
132f3d9478bSJohannes Berg .remove = soundbus_device_remove,
133f3d9478bSJohannes Berg .shutdown = soundbus_device_shutdown,
134a038b979SQuentin Lambert .dev_groups = soundbus_dev_groups,
135f3d9478bSJohannes Berg };
136f3d9478bSJohannes Berg
soundbus_add_one(struct soundbus_dev * dev)137f3d9478bSJohannes Berg int soundbus_add_one(struct soundbus_dev *dev)
138f3d9478bSJohannes Berg {
139f3d9478bSJohannes Berg static int devcount;
140f3d9478bSJohannes Berg
141f3d9478bSJohannes Berg /* sanity checks */
142f3d9478bSJohannes Berg if (!dev->attach_codec ||
14361c7a080SGrant Likely !dev->ofdev.dev.of_node ||
144f3d9478bSJohannes Berg dev->pcmname ||
145f3d9478bSJohannes Berg dev->pcmid != -1) {
146f3d9478bSJohannes Berg printk(KERN_ERR "soundbus: adding device failed sanity check!\n");
147f3d9478bSJohannes Berg return -EINVAL;
148f3d9478bSJohannes Berg }
149f3d9478bSJohannes Berg
150bb072bf0SKay Sievers dev_set_name(&dev->ofdev.dev, "soundbus:%x", ++devcount);
151f3d9478bSJohannes Berg dev->ofdev.dev.bus = &soundbus_bus_type;
152f3d9478bSJohannes Berg return of_device_register(&dev->ofdev);
153f3d9478bSJohannes Berg }
154f3d9478bSJohannes Berg EXPORT_SYMBOL_GPL(soundbus_add_one);
155f3d9478bSJohannes Berg
soundbus_remove_one(struct soundbus_dev * dev)156f3d9478bSJohannes Berg void soundbus_remove_one(struct soundbus_dev *dev)
157f3d9478bSJohannes Berg {
158f3d9478bSJohannes Berg of_device_unregister(&dev->ofdev);
159f3d9478bSJohannes Berg }
160f3d9478bSJohannes Berg EXPORT_SYMBOL_GPL(soundbus_remove_one);
161f3d9478bSJohannes Berg
soundbus_register_driver(struct soundbus_driver * drv)162f3d9478bSJohannes Berg int soundbus_register_driver(struct soundbus_driver *drv)
163f3d9478bSJohannes Berg {
164f3d9478bSJohannes Berg /* initialize common driver fields */
165f3d9478bSJohannes Berg drv->driver.name = drv->name;
166f3d9478bSJohannes Berg drv->driver.bus = &soundbus_bus_type;
167f3d9478bSJohannes Berg
168f3d9478bSJohannes Berg /* register with core */
169f3d9478bSJohannes Berg return driver_register(&drv->driver);
170f3d9478bSJohannes Berg }
171f3d9478bSJohannes Berg EXPORT_SYMBOL_GPL(soundbus_register_driver);
172f3d9478bSJohannes Berg
soundbus_unregister_driver(struct soundbus_driver * drv)173f3d9478bSJohannes Berg void soundbus_unregister_driver(struct soundbus_driver *drv)
174f3d9478bSJohannes Berg {
175f3d9478bSJohannes Berg driver_unregister(&drv->driver);
176f3d9478bSJohannes Berg }
177f3d9478bSJohannes Berg EXPORT_SYMBOL_GPL(soundbus_unregister_driver);
178f3d9478bSJohannes Berg
soundbus_init(void)179a08bc4cbSBenjamin Herrenschmidt static int __init soundbus_init(void)
180a08bc4cbSBenjamin Herrenschmidt {
181a08bc4cbSBenjamin Herrenschmidt return bus_register(&soundbus_bus_type);
182a08bc4cbSBenjamin Herrenschmidt }
183a08bc4cbSBenjamin Herrenschmidt
soundbus_exit(void)184a08bc4cbSBenjamin Herrenschmidt static void __exit soundbus_exit(void)
185a08bc4cbSBenjamin Herrenschmidt {
186a08bc4cbSBenjamin Herrenschmidt bus_unregister(&soundbus_bus_type);
187a08bc4cbSBenjamin Herrenschmidt }
188a08bc4cbSBenjamin Herrenschmidt
189a08bc4cbSBenjamin Herrenschmidt subsys_initcall(soundbus_init);
190f3d9478bSJohannes Berg module_exit(soundbus_exit);
191