xref: /kernel/linux/linux-5.10/sound/ac97/bus.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2016 Robert Jarzmik <robert.jarzmik@free.fr>
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/module.h>
78c2ecf20Sopenharmony_ci#include <linux/bitops.h>
88c2ecf20Sopenharmony_ci#include <linux/clk.h>
98c2ecf20Sopenharmony_ci#include <linux/device.h>
108c2ecf20Sopenharmony_ci#include <linux/idr.h>
118c2ecf20Sopenharmony_ci#include <linux/list.h>
128c2ecf20Sopenharmony_ci#include <linux/mutex.h>
138c2ecf20Sopenharmony_ci#include <linux/of.h>
148c2ecf20Sopenharmony_ci#include <linux/pm.h>
158c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h>
168c2ecf20Sopenharmony_ci#include <linux/slab.h>
178c2ecf20Sopenharmony_ci#include <linux/sysfs.h>
188c2ecf20Sopenharmony_ci#include <sound/ac97/codec.h>
198c2ecf20Sopenharmony_ci#include <sound/ac97/controller.h>
208c2ecf20Sopenharmony_ci#include <sound/ac97/regs.h>
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#include "ac97_core.h"
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci/*
258c2ecf20Sopenharmony_ci * Protects ac97_controllers and each ac97_controller structure.
268c2ecf20Sopenharmony_ci */
278c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(ac97_controllers_mutex);
288c2ecf20Sopenharmony_cistatic DEFINE_IDR(ac97_adapter_idr);
298c2ecf20Sopenharmony_cistatic LIST_HEAD(ac97_controllers);
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_cistatic struct bus_type ac97_bus_type;
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_cistatic inline struct ac97_controller*
348c2ecf20Sopenharmony_cito_ac97_controller(struct device *ac97_adapter)
358c2ecf20Sopenharmony_ci{
368c2ecf20Sopenharmony_ci	return container_of(ac97_adapter, struct ac97_controller, adap);
378c2ecf20Sopenharmony_ci}
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_cistatic int ac97_unbound_ctrl_write(struct ac97_controller *adrv, int slot,
408c2ecf20Sopenharmony_ci		     unsigned short reg, unsigned short val)
418c2ecf20Sopenharmony_ci{
428c2ecf20Sopenharmony_ci	return -ENODEV;
438c2ecf20Sopenharmony_ci}
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_cistatic int ac97_unbound_ctrl_read(struct ac97_controller *adrv, int slot,
468c2ecf20Sopenharmony_ci				  unsigned short reg)
478c2ecf20Sopenharmony_ci{
488c2ecf20Sopenharmony_ci	return -ENODEV;
498c2ecf20Sopenharmony_ci}
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_cistatic const struct ac97_controller_ops ac97_unbound_ctrl_ops = {
528c2ecf20Sopenharmony_ci	.write = ac97_unbound_ctrl_write,
538c2ecf20Sopenharmony_ci	.read = ac97_unbound_ctrl_read,
548c2ecf20Sopenharmony_ci};
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_cistatic struct ac97_controller ac97_unbound_ctrl = {
578c2ecf20Sopenharmony_ci	.ops = &ac97_unbound_ctrl_ops,
588c2ecf20Sopenharmony_ci};
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cistatic struct ac97_codec_device *
618c2ecf20Sopenharmony_ciac97_codec_find(struct ac97_controller *ac97_ctrl, unsigned int codec_num)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	if (codec_num >= AC97_BUS_MAX_CODECS)
648c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	return ac97_ctrl->codecs[codec_num];
678c2ecf20Sopenharmony_ci}
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_cistatic struct device_node *
708c2ecf20Sopenharmony_ciac97_of_get_child_device(struct ac97_controller *ac97_ctrl, int idx,
718c2ecf20Sopenharmony_ci			 unsigned int vendor_id)
728c2ecf20Sopenharmony_ci{
738c2ecf20Sopenharmony_ci	struct device_node *node;
748c2ecf20Sopenharmony_ci	u32 reg;
758c2ecf20Sopenharmony_ci	char compat[] = "ac97,0000,0000";
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	snprintf(compat, sizeof(compat), "ac97,%04x,%04x",
788c2ecf20Sopenharmony_ci		 vendor_id >> 16, vendor_id & 0xffff);
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	for_each_child_of_node(ac97_ctrl->parent->of_node, node) {
818c2ecf20Sopenharmony_ci		if ((idx != of_property_read_u32(node, "reg", &reg)) ||
828c2ecf20Sopenharmony_ci		    !of_device_is_compatible(node, compat))
838c2ecf20Sopenharmony_ci			continue;
848c2ecf20Sopenharmony_ci		return node;
858c2ecf20Sopenharmony_ci	}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	return NULL;
888c2ecf20Sopenharmony_ci}
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_cistatic void ac97_codec_release(struct device *dev)
918c2ecf20Sopenharmony_ci{
928c2ecf20Sopenharmony_ci	struct ac97_codec_device *adev;
938c2ecf20Sopenharmony_ci	struct ac97_controller *ac97_ctrl;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	adev = to_ac97_device(dev);
968c2ecf20Sopenharmony_ci	ac97_ctrl = adev->ac97_ctrl;
978c2ecf20Sopenharmony_ci	ac97_ctrl->codecs[adev->num] = NULL;
988c2ecf20Sopenharmony_ci	of_node_put(dev->of_node);
998c2ecf20Sopenharmony_ci	kfree(adev);
1008c2ecf20Sopenharmony_ci}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_cistatic int ac97_codec_add(struct ac97_controller *ac97_ctrl, int idx,
1038c2ecf20Sopenharmony_ci		   unsigned int vendor_id)
1048c2ecf20Sopenharmony_ci{
1058c2ecf20Sopenharmony_ci	struct ac97_codec_device *codec;
1068c2ecf20Sopenharmony_ci	int ret;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	codec = kzalloc(sizeof(*codec), GFP_KERNEL);
1098c2ecf20Sopenharmony_ci	if (!codec)
1108c2ecf20Sopenharmony_ci		return -ENOMEM;
1118c2ecf20Sopenharmony_ci	ac97_ctrl->codecs[idx] = codec;
1128c2ecf20Sopenharmony_ci	codec->vendor_id = vendor_id;
1138c2ecf20Sopenharmony_ci	codec->dev.release = ac97_codec_release;
1148c2ecf20Sopenharmony_ci	codec->dev.bus = &ac97_bus_type;
1158c2ecf20Sopenharmony_ci	codec->dev.parent = &ac97_ctrl->adap;
1168c2ecf20Sopenharmony_ci	codec->num = idx;
1178c2ecf20Sopenharmony_ci	codec->ac97_ctrl = ac97_ctrl;
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	device_initialize(&codec->dev);
1208c2ecf20Sopenharmony_ci	dev_set_name(&codec->dev, "%s:%u", dev_name(ac97_ctrl->parent), idx);
1218c2ecf20Sopenharmony_ci	codec->dev.of_node = ac97_of_get_child_device(ac97_ctrl, idx,
1228c2ecf20Sopenharmony_ci						      vendor_id);
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	ret = device_add(&codec->dev);
1258c2ecf20Sopenharmony_ci	if (ret) {
1268c2ecf20Sopenharmony_ci		put_device(&codec->dev);
1278c2ecf20Sopenharmony_ci		return ret;
1288c2ecf20Sopenharmony_ci	}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	return 0;
1318c2ecf20Sopenharmony_ci}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ciunsigned int snd_ac97_bus_scan_one(struct ac97_controller *adrv,
1348c2ecf20Sopenharmony_ci				   unsigned int codec_num)
1358c2ecf20Sopenharmony_ci{
1368c2ecf20Sopenharmony_ci	unsigned short vid1, vid2;
1378c2ecf20Sopenharmony_ci	int ret;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	ret = adrv->ops->read(adrv, codec_num, AC97_VENDOR_ID1);
1408c2ecf20Sopenharmony_ci	vid1 = (ret & 0xffff);
1418c2ecf20Sopenharmony_ci	if (ret < 0)
1428c2ecf20Sopenharmony_ci		return 0;
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	ret = adrv->ops->read(adrv, codec_num, AC97_VENDOR_ID2);
1458c2ecf20Sopenharmony_ci	vid2 = (ret & 0xffff);
1468c2ecf20Sopenharmony_ci	if (ret < 0)
1478c2ecf20Sopenharmony_ci		return 0;
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	dev_dbg(&adrv->adap, "%s(codec_num=%u): vendor_id=0x%08x\n",
1508c2ecf20Sopenharmony_ci		__func__, codec_num, AC97_ID(vid1, vid2));
1518c2ecf20Sopenharmony_ci	return AC97_ID(vid1, vid2);
1528c2ecf20Sopenharmony_ci}
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_cistatic int ac97_bus_scan(struct ac97_controller *ac97_ctrl)
1558c2ecf20Sopenharmony_ci{
1568c2ecf20Sopenharmony_ci	int ret, i;
1578c2ecf20Sopenharmony_ci	unsigned int vendor_id;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	for (i = 0; i < AC97_BUS_MAX_CODECS; i++) {
1608c2ecf20Sopenharmony_ci		if (ac97_codec_find(ac97_ctrl, i))
1618c2ecf20Sopenharmony_ci			continue;
1628c2ecf20Sopenharmony_ci		if (!(ac97_ctrl->slots_available & BIT(i)))
1638c2ecf20Sopenharmony_ci			continue;
1648c2ecf20Sopenharmony_ci		vendor_id = snd_ac97_bus_scan_one(ac97_ctrl, i);
1658c2ecf20Sopenharmony_ci		if (!vendor_id)
1668c2ecf20Sopenharmony_ci			continue;
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci		ret = ac97_codec_add(ac97_ctrl, i, vendor_id);
1698c2ecf20Sopenharmony_ci		if (ret < 0)
1708c2ecf20Sopenharmony_ci			return ret;
1718c2ecf20Sopenharmony_ci	}
1728c2ecf20Sopenharmony_ci	return 0;
1738c2ecf20Sopenharmony_ci}
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_cistatic int ac97_bus_reset(struct ac97_controller *ac97_ctrl)
1768c2ecf20Sopenharmony_ci{
1778c2ecf20Sopenharmony_ci	ac97_ctrl->ops->reset(ac97_ctrl);
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	return 0;
1808c2ecf20Sopenharmony_ci}
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci/**
1838c2ecf20Sopenharmony_ci * snd_ac97_codec_driver_register - register an AC97 codec driver
1848c2ecf20Sopenharmony_ci * @dev: AC97 driver codec to register
1858c2ecf20Sopenharmony_ci *
1868c2ecf20Sopenharmony_ci * Register an AC97 codec driver to the ac97 bus driver, aka. the AC97 digital
1878c2ecf20Sopenharmony_ci * controller.
1888c2ecf20Sopenharmony_ci *
1898c2ecf20Sopenharmony_ci * Returns 0 on success or error code
1908c2ecf20Sopenharmony_ci */
1918c2ecf20Sopenharmony_ciint snd_ac97_codec_driver_register(struct ac97_codec_driver *drv)
1928c2ecf20Sopenharmony_ci{
1938c2ecf20Sopenharmony_ci	drv->driver.bus = &ac97_bus_type;
1948c2ecf20Sopenharmony_ci	return driver_register(&drv->driver);
1958c2ecf20Sopenharmony_ci}
1968c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_ac97_codec_driver_register);
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci/**
1998c2ecf20Sopenharmony_ci * snd_ac97_codec_driver_unregister - unregister an AC97 codec driver
2008c2ecf20Sopenharmony_ci * @dev: AC97 codec driver to unregister
2018c2ecf20Sopenharmony_ci *
2028c2ecf20Sopenharmony_ci * Unregister a previously registered ac97 codec driver.
2038c2ecf20Sopenharmony_ci */
2048c2ecf20Sopenharmony_civoid snd_ac97_codec_driver_unregister(struct ac97_codec_driver *drv)
2058c2ecf20Sopenharmony_ci{
2068c2ecf20Sopenharmony_ci	driver_unregister(&drv->driver);
2078c2ecf20Sopenharmony_ci}
2088c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_ac97_codec_driver_unregister);
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci/**
2118c2ecf20Sopenharmony_ci * snd_ac97_codec_get_platdata - get platform_data
2128c2ecf20Sopenharmony_ci * @adev: the ac97 codec device
2138c2ecf20Sopenharmony_ci *
2148c2ecf20Sopenharmony_ci * For legacy platforms, in order to have platform_data in codec drivers
2158c2ecf20Sopenharmony_ci * available, while ac97 device are auto-created upon probe, this retrieves the
2168c2ecf20Sopenharmony_ci * platdata which was setup on ac97 controller registration.
2178c2ecf20Sopenharmony_ci *
2188c2ecf20Sopenharmony_ci * Returns the platform data pointer
2198c2ecf20Sopenharmony_ci */
2208c2ecf20Sopenharmony_civoid *snd_ac97_codec_get_platdata(const struct ac97_codec_device *adev)
2218c2ecf20Sopenharmony_ci{
2228c2ecf20Sopenharmony_ci	struct ac97_controller *ac97_ctrl = adev->ac97_ctrl;
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	return ac97_ctrl->codecs_pdata[adev->num];
2258c2ecf20Sopenharmony_ci}
2268c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_ac97_codec_get_platdata);
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_cistatic void ac97_ctrl_codecs_unregister(struct ac97_controller *ac97_ctrl)
2298c2ecf20Sopenharmony_ci{
2308c2ecf20Sopenharmony_ci	int i;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	for (i = 0; i < AC97_BUS_MAX_CODECS; i++)
2338c2ecf20Sopenharmony_ci		if (ac97_ctrl->codecs[i]) {
2348c2ecf20Sopenharmony_ci			ac97_ctrl->codecs[i]->ac97_ctrl = &ac97_unbound_ctrl;
2358c2ecf20Sopenharmony_ci			device_unregister(&ac97_ctrl->codecs[i]->dev);
2368c2ecf20Sopenharmony_ci		}
2378c2ecf20Sopenharmony_ci}
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_cistatic ssize_t cold_reset_store(struct device *dev,
2408c2ecf20Sopenharmony_ci				struct device_attribute *attr, const char *buf,
2418c2ecf20Sopenharmony_ci				size_t len)
2428c2ecf20Sopenharmony_ci{
2438c2ecf20Sopenharmony_ci	struct ac97_controller *ac97_ctrl;
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	mutex_lock(&ac97_controllers_mutex);
2468c2ecf20Sopenharmony_ci	ac97_ctrl = to_ac97_controller(dev);
2478c2ecf20Sopenharmony_ci	ac97_ctrl->ops->reset(ac97_ctrl);
2488c2ecf20Sopenharmony_ci	mutex_unlock(&ac97_controllers_mutex);
2498c2ecf20Sopenharmony_ci	return len;
2508c2ecf20Sopenharmony_ci}
2518c2ecf20Sopenharmony_cistatic DEVICE_ATTR_WO(cold_reset);
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_cistatic ssize_t warm_reset_store(struct device *dev,
2548c2ecf20Sopenharmony_ci				struct device_attribute *attr, const char *buf,
2558c2ecf20Sopenharmony_ci				size_t len)
2568c2ecf20Sopenharmony_ci{
2578c2ecf20Sopenharmony_ci	struct ac97_controller *ac97_ctrl;
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	if (!dev)
2608c2ecf20Sopenharmony_ci		return -ENODEV;
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	mutex_lock(&ac97_controllers_mutex);
2638c2ecf20Sopenharmony_ci	ac97_ctrl = to_ac97_controller(dev);
2648c2ecf20Sopenharmony_ci	ac97_ctrl->ops->warm_reset(ac97_ctrl);
2658c2ecf20Sopenharmony_ci	mutex_unlock(&ac97_controllers_mutex);
2668c2ecf20Sopenharmony_ci	return len;
2678c2ecf20Sopenharmony_ci}
2688c2ecf20Sopenharmony_cistatic DEVICE_ATTR_WO(warm_reset);
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_cistatic struct attribute *ac97_controller_device_attrs[] = {
2718c2ecf20Sopenharmony_ci	&dev_attr_cold_reset.attr,
2728c2ecf20Sopenharmony_ci	&dev_attr_warm_reset.attr,
2738c2ecf20Sopenharmony_ci	NULL
2748c2ecf20Sopenharmony_ci};
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_cistatic struct attribute_group ac97_adapter_attr_group = {
2778c2ecf20Sopenharmony_ci	.name	= "ac97_operations",
2788c2ecf20Sopenharmony_ci	.attrs	= ac97_controller_device_attrs,
2798c2ecf20Sopenharmony_ci};
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_cistatic const struct attribute_group *ac97_adapter_groups[] = {
2828c2ecf20Sopenharmony_ci	&ac97_adapter_attr_group,
2838c2ecf20Sopenharmony_ci	NULL,
2848c2ecf20Sopenharmony_ci};
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_cistatic void ac97_del_adapter(struct ac97_controller *ac97_ctrl)
2878c2ecf20Sopenharmony_ci{
2888c2ecf20Sopenharmony_ci	mutex_lock(&ac97_controllers_mutex);
2898c2ecf20Sopenharmony_ci	ac97_ctrl_codecs_unregister(ac97_ctrl);
2908c2ecf20Sopenharmony_ci	list_del(&ac97_ctrl->controllers);
2918c2ecf20Sopenharmony_ci	mutex_unlock(&ac97_controllers_mutex);
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	device_unregister(&ac97_ctrl->adap);
2948c2ecf20Sopenharmony_ci}
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_cistatic void ac97_adapter_release(struct device *dev)
2978c2ecf20Sopenharmony_ci{
2988c2ecf20Sopenharmony_ci	struct ac97_controller *ac97_ctrl;
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	ac97_ctrl = to_ac97_controller(dev);
3018c2ecf20Sopenharmony_ci	idr_remove(&ac97_adapter_idr, ac97_ctrl->nr);
3028c2ecf20Sopenharmony_ci	dev_dbg(&ac97_ctrl->adap, "adapter unregistered by %s\n",
3038c2ecf20Sopenharmony_ci		dev_name(ac97_ctrl->parent));
3048c2ecf20Sopenharmony_ci}
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_cistatic const struct device_type ac97_adapter_type = {
3078c2ecf20Sopenharmony_ci	.groups		= ac97_adapter_groups,
3088c2ecf20Sopenharmony_ci	.release	= ac97_adapter_release,
3098c2ecf20Sopenharmony_ci};
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_cistatic int ac97_add_adapter(struct ac97_controller *ac97_ctrl)
3128c2ecf20Sopenharmony_ci{
3138c2ecf20Sopenharmony_ci	int ret;
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	mutex_lock(&ac97_controllers_mutex);
3168c2ecf20Sopenharmony_ci	ret = idr_alloc(&ac97_adapter_idr, ac97_ctrl, 0, 0, GFP_KERNEL);
3178c2ecf20Sopenharmony_ci	ac97_ctrl->nr = ret;
3188c2ecf20Sopenharmony_ci	if (ret >= 0) {
3198c2ecf20Sopenharmony_ci		dev_set_name(&ac97_ctrl->adap, "ac97-%d", ret);
3208c2ecf20Sopenharmony_ci		ac97_ctrl->adap.type = &ac97_adapter_type;
3218c2ecf20Sopenharmony_ci		ac97_ctrl->adap.parent = ac97_ctrl->parent;
3228c2ecf20Sopenharmony_ci		ret = device_register(&ac97_ctrl->adap);
3238c2ecf20Sopenharmony_ci		if (ret)
3248c2ecf20Sopenharmony_ci			put_device(&ac97_ctrl->adap);
3258c2ecf20Sopenharmony_ci	}
3268c2ecf20Sopenharmony_ci	if (!ret)
3278c2ecf20Sopenharmony_ci		list_add(&ac97_ctrl->controllers, &ac97_controllers);
3288c2ecf20Sopenharmony_ci	mutex_unlock(&ac97_controllers_mutex);
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	if (!ret)
3318c2ecf20Sopenharmony_ci		dev_dbg(&ac97_ctrl->adap, "adapter registered by %s\n",
3328c2ecf20Sopenharmony_ci			dev_name(ac97_ctrl->parent));
3338c2ecf20Sopenharmony_ci	return ret;
3348c2ecf20Sopenharmony_ci}
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci/**
3378c2ecf20Sopenharmony_ci * snd_ac97_controller_register - register an ac97 controller
3388c2ecf20Sopenharmony_ci * @ops: the ac97 bus operations
3398c2ecf20Sopenharmony_ci * @dev: the device providing the ac97 DC function
3408c2ecf20Sopenharmony_ci * @slots_available: mask of the ac97 codecs that can be scanned and probed
3418c2ecf20Sopenharmony_ci *                   bit0 => codec 0, bit1 => codec 1 ... bit 3 => codec 3
3428c2ecf20Sopenharmony_ci *
3438c2ecf20Sopenharmony_ci * Register a digital controller which can control up to 4 ac97 codecs. This is
3448c2ecf20Sopenharmony_ci * the controller side of the AC97 AC-link, while the slave side are the codecs.
3458c2ecf20Sopenharmony_ci *
3468c2ecf20Sopenharmony_ci * Returns a valid controller upon success, negative pointer value upon error
3478c2ecf20Sopenharmony_ci */
3488c2ecf20Sopenharmony_cistruct ac97_controller *snd_ac97_controller_register(
3498c2ecf20Sopenharmony_ci	const struct ac97_controller_ops *ops, struct device *dev,
3508c2ecf20Sopenharmony_ci	unsigned short slots_available, void **codecs_pdata)
3518c2ecf20Sopenharmony_ci{
3528c2ecf20Sopenharmony_ci	struct ac97_controller *ac97_ctrl;
3538c2ecf20Sopenharmony_ci	int ret, i;
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	ac97_ctrl = kzalloc(sizeof(*ac97_ctrl), GFP_KERNEL);
3568c2ecf20Sopenharmony_ci	if (!ac97_ctrl)
3578c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	for (i = 0; i < AC97_BUS_MAX_CODECS && codecs_pdata; i++)
3608c2ecf20Sopenharmony_ci		ac97_ctrl->codecs_pdata[i] = codecs_pdata[i];
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	ac97_ctrl->ops = ops;
3638c2ecf20Sopenharmony_ci	ac97_ctrl->slots_available = slots_available;
3648c2ecf20Sopenharmony_ci	ac97_ctrl->parent = dev;
3658c2ecf20Sopenharmony_ci	ret = ac97_add_adapter(ac97_ctrl);
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	if (ret)
3688c2ecf20Sopenharmony_ci		goto err;
3698c2ecf20Sopenharmony_ci	ac97_bus_reset(ac97_ctrl);
3708c2ecf20Sopenharmony_ci	ac97_bus_scan(ac97_ctrl);
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	return ac97_ctrl;
3738c2ecf20Sopenharmony_cierr:
3748c2ecf20Sopenharmony_ci	kfree(ac97_ctrl);
3758c2ecf20Sopenharmony_ci	return ERR_PTR(ret);
3768c2ecf20Sopenharmony_ci}
3778c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_ac97_controller_register);
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci/**
3808c2ecf20Sopenharmony_ci * snd_ac97_controller_unregister - unregister an ac97 controller
3818c2ecf20Sopenharmony_ci * @ac97_ctrl: the device previously provided to ac97_controller_register()
3828c2ecf20Sopenharmony_ci *
3838c2ecf20Sopenharmony_ci */
3848c2ecf20Sopenharmony_civoid snd_ac97_controller_unregister(struct ac97_controller *ac97_ctrl)
3858c2ecf20Sopenharmony_ci{
3868c2ecf20Sopenharmony_ci	ac97_del_adapter(ac97_ctrl);
3878c2ecf20Sopenharmony_ci}
3888c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_ac97_controller_unregister);
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
3918c2ecf20Sopenharmony_cistatic int ac97_pm_runtime_suspend(struct device *dev)
3928c2ecf20Sopenharmony_ci{
3938c2ecf20Sopenharmony_ci	struct ac97_codec_device *codec = to_ac97_device(dev);
3948c2ecf20Sopenharmony_ci	int ret = pm_generic_runtime_suspend(dev);
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	if (ret == 0 && dev->driver) {
3978c2ecf20Sopenharmony_ci		if (pm_runtime_is_irq_safe(dev))
3988c2ecf20Sopenharmony_ci			clk_disable(codec->clk);
3998c2ecf20Sopenharmony_ci		else
4008c2ecf20Sopenharmony_ci			clk_disable_unprepare(codec->clk);
4018c2ecf20Sopenharmony_ci	}
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	return ret;
4048c2ecf20Sopenharmony_ci}
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_cistatic int ac97_pm_runtime_resume(struct device *dev)
4078c2ecf20Sopenharmony_ci{
4088c2ecf20Sopenharmony_ci	struct ac97_codec_device *codec = to_ac97_device(dev);
4098c2ecf20Sopenharmony_ci	int ret;
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	if (dev->driver) {
4128c2ecf20Sopenharmony_ci		if (pm_runtime_is_irq_safe(dev))
4138c2ecf20Sopenharmony_ci			ret = clk_enable(codec->clk);
4148c2ecf20Sopenharmony_ci		else
4158c2ecf20Sopenharmony_ci			ret = clk_prepare_enable(codec->clk);
4168c2ecf20Sopenharmony_ci		if (ret)
4178c2ecf20Sopenharmony_ci			return ret;
4188c2ecf20Sopenharmony_ci	}
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	return pm_generic_runtime_resume(dev);
4218c2ecf20Sopenharmony_ci}
4228c2ecf20Sopenharmony_ci#endif /* CONFIG_PM */
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_cistatic const struct dev_pm_ops ac97_pm = {
4258c2ecf20Sopenharmony_ci	.suspend	= pm_generic_suspend,
4268c2ecf20Sopenharmony_ci	.resume		= pm_generic_resume,
4278c2ecf20Sopenharmony_ci	.freeze		= pm_generic_freeze,
4288c2ecf20Sopenharmony_ci	.thaw		= pm_generic_thaw,
4298c2ecf20Sopenharmony_ci	.poweroff	= pm_generic_poweroff,
4308c2ecf20Sopenharmony_ci	.restore	= pm_generic_restore,
4318c2ecf20Sopenharmony_ci	SET_RUNTIME_PM_OPS(
4328c2ecf20Sopenharmony_ci		ac97_pm_runtime_suspend,
4338c2ecf20Sopenharmony_ci		ac97_pm_runtime_resume,
4348c2ecf20Sopenharmony_ci		NULL)
4358c2ecf20Sopenharmony_ci};
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_cistatic int ac97_get_enable_clk(struct ac97_codec_device *adev)
4388c2ecf20Sopenharmony_ci{
4398c2ecf20Sopenharmony_ci	int ret;
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	adev->clk = clk_get(&adev->dev, "ac97_clk");
4428c2ecf20Sopenharmony_ci	if (IS_ERR(adev->clk))
4438c2ecf20Sopenharmony_ci		return PTR_ERR(adev->clk);
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(adev->clk);
4468c2ecf20Sopenharmony_ci	if (ret)
4478c2ecf20Sopenharmony_ci		clk_put(adev->clk);
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	return ret;
4508c2ecf20Sopenharmony_ci}
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_cistatic void ac97_put_disable_clk(struct ac97_codec_device *adev)
4538c2ecf20Sopenharmony_ci{
4548c2ecf20Sopenharmony_ci	clk_disable_unprepare(adev->clk);
4558c2ecf20Sopenharmony_ci	clk_put(adev->clk);
4568c2ecf20Sopenharmony_ci}
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_cistatic ssize_t vendor_id_show(struct device *dev,
4598c2ecf20Sopenharmony_ci			      struct device_attribute *attr, char *buf)
4608c2ecf20Sopenharmony_ci{
4618c2ecf20Sopenharmony_ci	struct ac97_codec_device *codec = to_ac97_device(dev);
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci	return sprintf(buf, "%08x", codec->vendor_id);
4648c2ecf20Sopenharmony_ci}
4658c2ecf20Sopenharmony_ciDEVICE_ATTR_RO(vendor_id);
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_cistatic struct attribute *ac97_dev_attrs[] = {
4688c2ecf20Sopenharmony_ci	&dev_attr_vendor_id.attr,
4698c2ecf20Sopenharmony_ci	NULL,
4708c2ecf20Sopenharmony_ci};
4718c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(ac97_dev);
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_cistatic int ac97_bus_match(struct device *dev, struct device_driver *drv)
4748c2ecf20Sopenharmony_ci{
4758c2ecf20Sopenharmony_ci	struct ac97_codec_device *adev = to_ac97_device(dev);
4768c2ecf20Sopenharmony_ci	struct ac97_codec_driver *adrv = to_ac97_driver(drv);
4778c2ecf20Sopenharmony_ci	const struct ac97_id *id = adrv->id_table;
4788c2ecf20Sopenharmony_ci	int i = 0;
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci	if (adev->vendor_id == 0x0 || adev->vendor_id == 0xffffffff)
4818c2ecf20Sopenharmony_ci		return false;
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	do {
4848c2ecf20Sopenharmony_ci		if (ac97_ids_match(id[i].id, adev->vendor_id, id[i].mask))
4858c2ecf20Sopenharmony_ci			return true;
4868c2ecf20Sopenharmony_ci	} while (id[i++].id);
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci	return false;
4898c2ecf20Sopenharmony_ci}
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_cistatic int ac97_bus_probe(struct device *dev)
4928c2ecf20Sopenharmony_ci{
4938c2ecf20Sopenharmony_ci	struct ac97_codec_device *adev = to_ac97_device(dev);
4948c2ecf20Sopenharmony_ci	struct ac97_codec_driver *adrv = to_ac97_driver(dev->driver);
4958c2ecf20Sopenharmony_ci	int ret;
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci	ret = ac97_get_enable_clk(adev);
4988c2ecf20Sopenharmony_ci	if (ret)
4998c2ecf20Sopenharmony_ci		return ret;
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci	pm_runtime_get_noresume(dev);
5028c2ecf20Sopenharmony_ci	pm_runtime_set_active(dev);
5038c2ecf20Sopenharmony_ci	pm_runtime_enable(dev);
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci	ret = adrv->probe(adev);
5068c2ecf20Sopenharmony_ci	if (ret == 0)
5078c2ecf20Sopenharmony_ci		return 0;
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci	pm_runtime_disable(dev);
5108c2ecf20Sopenharmony_ci	pm_runtime_set_suspended(dev);
5118c2ecf20Sopenharmony_ci	pm_runtime_put_noidle(dev);
5128c2ecf20Sopenharmony_ci	ac97_put_disable_clk(adev);
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci	return ret;
5158c2ecf20Sopenharmony_ci}
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_cistatic int ac97_bus_remove(struct device *dev)
5188c2ecf20Sopenharmony_ci{
5198c2ecf20Sopenharmony_ci	struct ac97_codec_device *adev = to_ac97_device(dev);
5208c2ecf20Sopenharmony_ci	struct ac97_codec_driver *adrv = to_ac97_driver(dev->driver);
5218c2ecf20Sopenharmony_ci	int ret;
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci	ret = pm_runtime_resume_and_get(dev);
5248c2ecf20Sopenharmony_ci	if (ret < 0)
5258c2ecf20Sopenharmony_ci		return ret;
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ci	ret = adrv->remove(adev);
5288c2ecf20Sopenharmony_ci	pm_runtime_put_noidle(dev);
5298c2ecf20Sopenharmony_ci	if (ret == 0)
5308c2ecf20Sopenharmony_ci		ac97_put_disable_clk(adev);
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci	pm_runtime_disable(dev);
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci	return ret;
5358c2ecf20Sopenharmony_ci}
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_cistatic struct bus_type ac97_bus_type = {
5388c2ecf20Sopenharmony_ci	.name		= "ac97bus",
5398c2ecf20Sopenharmony_ci	.dev_groups	= ac97_dev_groups,
5408c2ecf20Sopenharmony_ci	.match		= ac97_bus_match,
5418c2ecf20Sopenharmony_ci	.pm		= &ac97_pm,
5428c2ecf20Sopenharmony_ci	.probe		= ac97_bus_probe,
5438c2ecf20Sopenharmony_ci	.remove		= ac97_bus_remove,
5448c2ecf20Sopenharmony_ci};
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_cistatic int __init ac97_bus_init(void)
5478c2ecf20Sopenharmony_ci{
5488c2ecf20Sopenharmony_ci	return bus_register(&ac97_bus_type);
5498c2ecf20Sopenharmony_ci}
5508c2ecf20Sopenharmony_cisubsys_initcall(ac97_bus_init);
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_cistatic void __exit ac97_bus_exit(void)
5538c2ecf20Sopenharmony_ci{
5548c2ecf20Sopenharmony_ci	bus_unregister(&ac97_bus_type);
5558c2ecf20Sopenharmony_ci}
5568c2ecf20Sopenharmony_cimodule_exit(ac97_bus_exit);
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
5598c2ecf20Sopenharmony_ciMODULE_AUTHOR("Robert Jarzmik <robert.jarzmik@free.fr>");
560