162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * soundbus 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright 2006 Johannes Berg <johannes@sipsolutions.net> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/module.h> 962306a36Sopenharmony_ci#include "soundbus.h" 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ciMODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>"); 1262306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 1362306a36Sopenharmony_ciMODULE_DESCRIPTION("Apple Soundbus"); 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_cistruct soundbus_dev *soundbus_dev_get(struct soundbus_dev *dev) 1662306a36Sopenharmony_ci{ 1762306a36Sopenharmony_ci struct device *tmp; 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci if (!dev) 2062306a36Sopenharmony_ci return NULL; 2162306a36Sopenharmony_ci tmp = get_device(&dev->ofdev.dev); 2262306a36Sopenharmony_ci if (tmp) 2362306a36Sopenharmony_ci return to_soundbus_device(tmp); 2462306a36Sopenharmony_ci else 2562306a36Sopenharmony_ci return NULL; 2662306a36Sopenharmony_ci} 2762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(soundbus_dev_get); 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_civoid soundbus_dev_put(struct soundbus_dev *dev) 3062306a36Sopenharmony_ci{ 3162306a36Sopenharmony_ci if (dev) 3262306a36Sopenharmony_ci put_device(&dev->ofdev.dev); 3362306a36Sopenharmony_ci} 3462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(soundbus_dev_put); 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistatic int soundbus_probe(struct device *dev) 3762306a36Sopenharmony_ci{ 3862306a36Sopenharmony_ci int error = -ENODEV; 3962306a36Sopenharmony_ci struct soundbus_driver *drv; 4062306a36Sopenharmony_ci struct soundbus_dev *soundbus_dev; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci drv = to_soundbus_driver(dev->driver); 4362306a36Sopenharmony_ci soundbus_dev = to_soundbus_device(dev); 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci if (!drv->probe) 4662306a36Sopenharmony_ci return error; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci soundbus_dev_get(soundbus_dev); 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci error = drv->probe(soundbus_dev); 5162306a36Sopenharmony_ci if (error) 5262306a36Sopenharmony_ci soundbus_dev_put(soundbus_dev); 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci return error; 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistatic int soundbus_uevent(const struct device *dev, struct kobj_uevent_env *env) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci const struct soundbus_dev * soundbus_dev; 6162306a36Sopenharmony_ci const struct platform_device * of; 6262306a36Sopenharmony_ci const char *compat; 6362306a36Sopenharmony_ci int retval = 0; 6462306a36Sopenharmony_ci int cplen, seen = 0; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci if (!dev) 6762306a36Sopenharmony_ci return -ENODEV; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci soundbus_dev = to_soundbus_device(dev); 7062306a36Sopenharmony_ci if (!soundbus_dev) 7162306a36Sopenharmony_ci return -ENODEV; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci of = &soundbus_dev->ofdev; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci /* stuff we want to pass to /sbin/hotplug */ 7662306a36Sopenharmony_ci retval = add_uevent_var(env, "OF_NAME=%pOFn", of->dev.of_node); 7762306a36Sopenharmony_ci if (retval) 7862306a36Sopenharmony_ci return retval; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci retval = add_uevent_var(env, "OF_TYPE=%s", of_node_get_device_type(of->dev.of_node)); 8162306a36Sopenharmony_ci if (retval) 8262306a36Sopenharmony_ci return retval; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci /* Since the compatible field can contain pretty much anything 8562306a36Sopenharmony_ci * it's not really legal to split it out with commas. We split it 8662306a36Sopenharmony_ci * up using a number of environment variables instead. */ 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci compat = of_get_property(of->dev.of_node, "compatible", &cplen); 8962306a36Sopenharmony_ci while (compat && cplen > 0) { 9062306a36Sopenharmony_ci int tmp = env->buflen; 9162306a36Sopenharmony_ci retval = add_uevent_var(env, "OF_COMPATIBLE_%d=%s", seen, compat); 9262306a36Sopenharmony_ci if (retval) 9362306a36Sopenharmony_ci return retval; 9462306a36Sopenharmony_ci compat += env->buflen - tmp; 9562306a36Sopenharmony_ci cplen -= env->buflen - tmp; 9662306a36Sopenharmony_ci seen += 1; 9762306a36Sopenharmony_ci } 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci retval = add_uevent_var(env, "OF_COMPATIBLE_N=%d", seen); 10062306a36Sopenharmony_ci if (retval) 10162306a36Sopenharmony_ci return retval; 10262306a36Sopenharmony_ci retval = add_uevent_var(env, "MODALIAS=%s", soundbus_dev->modalias); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci return retval; 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistatic void soundbus_device_remove(struct device *dev) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci struct soundbus_dev * soundbus_dev = to_soundbus_device(dev); 11062306a36Sopenharmony_ci struct soundbus_driver * drv = to_soundbus_driver(dev->driver); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci if (dev->driver && drv->remove) 11362306a36Sopenharmony_ci drv->remove(soundbus_dev); 11462306a36Sopenharmony_ci soundbus_dev_put(soundbus_dev); 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic void soundbus_device_shutdown(struct device *dev) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci struct soundbus_dev * soundbus_dev = to_soundbus_device(dev); 12062306a36Sopenharmony_ci struct soundbus_driver * drv = to_soundbus_driver(dev->driver); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci if (dev->driver && drv->shutdown) 12362306a36Sopenharmony_ci drv->shutdown(soundbus_dev); 12462306a36Sopenharmony_ci} 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci/* soundbus_dev_attrs is declared in sysfs.c */ 12762306a36Sopenharmony_ciATTRIBUTE_GROUPS(soundbus_dev); 12862306a36Sopenharmony_cistatic struct bus_type soundbus_bus_type = { 12962306a36Sopenharmony_ci .name = "aoa-soundbus", 13062306a36Sopenharmony_ci .probe = soundbus_probe, 13162306a36Sopenharmony_ci .uevent = soundbus_uevent, 13262306a36Sopenharmony_ci .remove = soundbus_device_remove, 13362306a36Sopenharmony_ci .shutdown = soundbus_device_shutdown, 13462306a36Sopenharmony_ci .dev_groups = soundbus_dev_groups, 13562306a36Sopenharmony_ci}; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ciint soundbus_add_one(struct soundbus_dev *dev) 13862306a36Sopenharmony_ci{ 13962306a36Sopenharmony_ci static int devcount; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci /* sanity checks */ 14262306a36Sopenharmony_ci if (!dev->attach_codec || 14362306a36Sopenharmony_ci !dev->ofdev.dev.of_node || 14462306a36Sopenharmony_ci dev->pcmname || 14562306a36Sopenharmony_ci dev->pcmid != -1) { 14662306a36Sopenharmony_ci printk(KERN_ERR "soundbus: adding device failed sanity check!\n"); 14762306a36Sopenharmony_ci return -EINVAL; 14862306a36Sopenharmony_ci } 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci dev_set_name(&dev->ofdev.dev, "soundbus:%x", ++devcount); 15162306a36Sopenharmony_ci dev->ofdev.dev.bus = &soundbus_bus_type; 15262306a36Sopenharmony_ci return of_device_register(&dev->ofdev); 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(soundbus_add_one); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_civoid soundbus_remove_one(struct soundbus_dev *dev) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci of_device_unregister(&dev->ofdev); 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(soundbus_remove_one); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ciint soundbus_register_driver(struct soundbus_driver *drv) 16362306a36Sopenharmony_ci{ 16462306a36Sopenharmony_ci /* initialize common driver fields */ 16562306a36Sopenharmony_ci drv->driver.name = drv->name; 16662306a36Sopenharmony_ci drv->driver.bus = &soundbus_bus_type; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci /* register with core */ 16962306a36Sopenharmony_ci return driver_register(&drv->driver); 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(soundbus_register_driver); 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_civoid soundbus_unregister_driver(struct soundbus_driver *drv) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci driver_unregister(&drv->driver); 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(soundbus_unregister_driver); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_cistatic int __init soundbus_init(void) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci return bus_register(&soundbus_bus_type); 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_cistatic void __exit soundbus_exit(void) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci bus_unregister(&soundbus_bus_type); 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_cisubsys_initcall(soundbus_init); 19062306a36Sopenharmony_cimodule_exit(soundbus_exit); 191