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", ®)) || 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