| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * HD-audio codec core device |
|---|
| 3 | 4 | */ |
|---|
| .. | .. |
|---|
| 19 | 20 | |
|---|
| 20 | 21 | static void default_release(struct device *dev) |
|---|
| 21 | 22 | { |
|---|
| 22 | | - snd_hdac_device_exit(container_of(dev, struct hdac_device, dev)); |
|---|
| 23 | + snd_hdac_device_exit(dev_to_hdac_dev(dev)); |
|---|
| 23 | 24 | } |
|---|
| 24 | 25 | |
|---|
| 25 | 26 | /** |
|---|
| .. | .. |
|---|
| 55 | 56 | codec->bus = bus; |
|---|
| 56 | 57 | codec->addr = addr; |
|---|
| 57 | 58 | codec->type = HDA_DEV_CORE; |
|---|
| 59 | + mutex_init(&codec->widget_lock); |
|---|
| 60 | + mutex_init(&codec->regmap_lock); |
|---|
| 58 | 61 | pm_runtime_set_active(&codec->dev); |
|---|
| 59 | 62 | pm_runtime_get_noresume(&codec->dev); |
|---|
| 60 | 63 | atomic_set(&codec->in_pm, 0); |
|---|
| .. | .. |
|---|
| 88 | 91 | |
|---|
| 89 | 92 | fg = codec->afg ? codec->afg : codec->mfg; |
|---|
| 90 | 93 | |
|---|
| 91 | | - err = snd_hdac_refresh_widgets(codec, false); |
|---|
| 94 | + err = snd_hdac_refresh_widgets(codec); |
|---|
| 92 | 95 | if (err < 0) |
|---|
| 93 | 96 | goto error; |
|---|
| 94 | 97 | |
|---|
| .. | .. |
|---|
| 134 | 137 | |
|---|
| 135 | 138 | /** |
|---|
| 136 | 139 | * snd_hdac_device_register - register the hd-audio codec base device |
|---|
| 137 | | - * codec: the device to register |
|---|
| 140 | + * @codec: the device to register |
|---|
| 138 | 141 | */ |
|---|
| 139 | 142 | int snd_hdac_device_register(struct hdac_device *codec) |
|---|
| 140 | 143 | { |
|---|
| .. | .. |
|---|
| 143 | 146 | err = device_add(&codec->dev); |
|---|
| 144 | 147 | if (err < 0) |
|---|
| 145 | 148 | return err; |
|---|
| 149 | + mutex_lock(&codec->widget_lock); |
|---|
| 146 | 150 | err = hda_widget_sysfs_init(codec); |
|---|
| 151 | + mutex_unlock(&codec->widget_lock); |
|---|
| 147 | 152 | if (err < 0) { |
|---|
| 148 | 153 | device_del(&codec->dev); |
|---|
| 149 | 154 | return err; |
|---|
| .. | .. |
|---|
| 155 | 160 | |
|---|
| 156 | 161 | /** |
|---|
| 157 | 162 | * snd_hdac_device_unregister - unregister the hd-audio codec base device |
|---|
| 158 | | - * codec: the device to unregister |
|---|
| 163 | + * @codec: the device to unregister |
|---|
| 159 | 164 | */ |
|---|
| 160 | 165 | void snd_hdac_device_unregister(struct hdac_device *codec) |
|---|
| 161 | 166 | { |
|---|
| 162 | 167 | if (device_is_registered(&codec->dev)) { |
|---|
| 168 | + mutex_lock(&codec->widget_lock); |
|---|
| 163 | 169 | hda_widget_sysfs_exit(codec); |
|---|
| 170 | + mutex_unlock(&codec->widget_lock); |
|---|
| 164 | 171 | device_del(&codec->dev); |
|---|
| 165 | 172 | snd_hdac_bus_remove_device(codec->bus, codec); |
|---|
| 166 | 173 | } |
|---|
| .. | .. |
|---|
| 199 | 206 | */ |
|---|
| 200 | 207 | int snd_hdac_codec_modalias(struct hdac_device *codec, char *buf, size_t size) |
|---|
| 201 | 208 | { |
|---|
| 202 | | - return snprintf(buf, size, "hdaudio:v%08Xr%08Xa%02X\n", |
|---|
| 209 | + return scnprintf(buf, size, "hdaudio:v%08Xr%08Xa%02X\n", |
|---|
| 203 | 210 | codec->vendor_id, codec->revision_id, codec->type); |
|---|
| 204 | 211 | } |
|---|
| 205 | 212 | EXPORT_SYMBOL_GPL(snd_hdac_codec_modalias); |
|---|
| .. | .. |
|---|
| 214 | 221 | * |
|---|
| 215 | 222 | * Return an encoded command verb or -1 for error. |
|---|
| 216 | 223 | */ |
|---|
| 217 | | -unsigned int snd_hdac_make_cmd(struct hdac_device *codec, hda_nid_t nid, |
|---|
| 218 | | - unsigned int verb, unsigned int parm) |
|---|
| 224 | +static unsigned int snd_hdac_make_cmd(struct hdac_device *codec, hda_nid_t nid, |
|---|
| 225 | + unsigned int verb, unsigned int parm) |
|---|
| 219 | 226 | { |
|---|
| 220 | 227 | u32 val, addr; |
|---|
| 221 | 228 | |
|---|
| .. | .. |
|---|
| 233 | 240 | val |= parm; |
|---|
| 234 | 241 | return val; |
|---|
| 235 | 242 | } |
|---|
| 236 | | -EXPORT_SYMBOL_GPL(snd_hdac_make_cmd); |
|---|
| 237 | 243 | |
|---|
| 238 | 244 | /** |
|---|
| 239 | 245 | * snd_hdac_exec_verb - execute an encoded verb |
|---|
| .. | .. |
|---|
| 254 | 260 | return codec->exec_verb(codec, cmd, flags, res); |
|---|
| 255 | 261 | return snd_hdac_bus_exec_verb(codec->bus, codec->addr, cmd, res); |
|---|
| 256 | 262 | } |
|---|
| 257 | | -EXPORT_SYMBOL_GPL(snd_hdac_exec_verb); |
|---|
| 258 | 263 | |
|---|
| 259 | 264 | |
|---|
| 260 | 265 | /** |
|---|
| .. | .. |
|---|
| 278 | 283 | |
|---|
| 279 | 284 | /** |
|---|
| 280 | 285 | * _snd_hdac_read_parm - read a parmeter |
|---|
| 286 | + * @codec: the codec object |
|---|
| 287 | + * @nid: NID to read a parameter |
|---|
| 288 | + * @parm: parameter to read |
|---|
| 289 | + * @res: pointer to store the read value |
|---|
| 281 | 290 | * |
|---|
| 282 | 291 | * This function returns zero or an error unlike snd_hdac_read_parm(). |
|---|
| 283 | 292 | */ |
|---|
| .. | .. |
|---|
| 391 | 400 | /** |
|---|
| 392 | 401 | * snd_hdac_refresh_widgets - Reset the widget start/end nodes |
|---|
| 393 | 402 | * @codec: the codec object |
|---|
| 394 | | - * @sysfs: re-initialize sysfs tree, too |
|---|
| 395 | 403 | */ |
|---|
| 396 | | -int snd_hdac_refresh_widgets(struct hdac_device *codec, bool sysfs) |
|---|
| 404 | +int snd_hdac_refresh_widgets(struct hdac_device *codec) |
|---|
| 397 | 405 | { |
|---|
| 398 | 406 | hda_nid_t start_nid; |
|---|
| 399 | | - int nums, err; |
|---|
| 407 | + int nums, err = 0; |
|---|
| 400 | 408 | |
|---|
| 409 | + /* |
|---|
| 410 | + * Serialize against multiple threads trying to update the sysfs |
|---|
| 411 | + * widgets array. |
|---|
| 412 | + */ |
|---|
| 413 | + mutex_lock(&codec->widget_lock); |
|---|
| 401 | 414 | nums = snd_hdac_get_sub_nodes(codec, codec->afg, &start_nid); |
|---|
| 402 | 415 | if (!start_nid || nums <= 0 || nums >= 0xff) { |
|---|
| 403 | 416 | dev_err(&codec->dev, "cannot read sub nodes for FG 0x%02x\n", |
|---|
| 404 | 417 | codec->afg); |
|---|
| 405 | | - return -EINVAL; |
|---|
| 418 | + err = -EINVAL; |
|---|
| 419 | + goto unlock; |
|---|
| 406 | 420 | } |
|---|
| 407 | 421 | |
|---|
| 408 | | - if (sysfs) { |
|---|
| 409 | | - err = hda_widget_sysfs_reinit(codec, start_nid, nums); |
|---|
| 410 | | - if (err < 0) |
|---|
| 411 | | - return err; |
|---|
| 412 | | - } |
|---|
| 422 | + err = hda_widget_sysfs_reinit(codec, start_nid, nums); |
|---|
| 423 | + if (err < 0) |
|---|
| 424 | + goto unlock; |
|---|
| 413 | 425 | |
|---|
| 414 | 426 | codec->num_nodes = nums; |
|---|
| 415 | 427 | codec->start_nid = start_nid; |
|---|
| 416 | 428 | codec->end_nid = start_nid + nums; |
|---|
| 417 | | - return 0; |
|---|
| 429 | +unlock: |
|---|
| 430 | + mutex_unlock(&codec->widget_lock); |
|---|
| 431 | + return err; |
|---|
| 418 | 432 | } |
|---|
| 419 | 433 | EXPORT_SYMBOL_GPL(snd_hdac_refresh_widgets); |
|---|
| 420 | 434 | |
|---|
| .. | .. |
|---|
| 624 | 638 | EXPORT_SYMBOL_GPL(snd_hdac_power_down_pm); |
|---|
| 625 | 639 | #endif |
|---|
| 626 | 640 | |
|---|
| 627 | | -/** |
|---|
| 628 | | - * snd_hdac_link_power - Enable/disable the link power for a codec |
|---|
| 629 | | - * @codec: the codec object |
|---|
| 630 | | - * @bool: enable or disable the link power |
|---|
| 631 | | - */ |
|---|
| 632 | | -int snd_hdac_link_power(struct hdac_device *codec, bool enable) |
|---|
| 633 | | -{ |
|---|
| 634 | | - if (!codec->link_power_control) |
|---|
| 635 | | - return 0; |
|---|
| 636 | | - |
|---|
| 637 | | - if (codec->bus->ops->link_power) |
|---|
| 638 | | - return codec->bus->ops->link_power(codec->bus, enable); |
|---|
| 639 | | - else |
|---|
| 640 | | - return -EINVAL; |
|---|
| 641 | | -} |
|---|
| 642 | | -EXPORT_SYMBOL_GPL(snd_hdac_link_power); |
|---|
| 643 | | - |
|---|
| 644 | 641 | /* codec vendor labels */ |
|---|
| 645 | 642 | struct hda_vendor_id { |
|---|
| 646 | 643 | unsigned int id; |
|---|
| 647 | 644 | const char *name; |
|---|
| 648 | 645 | }; |
|---|
| 649 | 646 | |
|---|
| 650 | | -static struct hda_vendor_id hda_vendor_ids[] = { |
|---|
| 647 | +static const struct hda_vendor_id hda_vendor_ids[] = { |
|---|
| 651 | 648 | { 0x1002, "ATI" }, |
|---|
| 652 | 649 | { 0x1013, "Cirrus Logic" }, |
|---|
| 653 | 650 | { 0x1057, "Motorola" }, |
|---|
| .. | .. |
|---|
| 663 | 660 | { 0x14f1, "Conexant" }, |
|---|
| 664 | 661 | { 0x17e8, "Chrontel" }, |
|---|
| 665 | 662 | { 0x1854, "LG" }, |
|---|
| 663 | + { 0x19e5, "Huawei" }, |
|---|
| 666 | 664 | { 0x1aec, "Wolfson Microelectronics" }, |
|---|
| 667 | 665 | { 0x1af4, "QEMU" }, |
|---|
| 668 | 666 | { 0x434d, "C-Media" }, |
|---|
| .. | .. |
|---|
| 702 | 700 | (AC_FMT_BASE_##base##K | (((mult) - 1) << AC_FMT_MULT_SHIFT) | \ |
|---|
| 703 | 701 | (((div) - 1) << AC_FMT_DIV_SHIFT)) |
|---|
| 704 | 702 | |
|---|
| 705 | | -static struct hda_rate_tbl rate_bits[] = { |
|---|
| 703 | +static const struct hda_rate_tbl rate_bits[] = { |
|---|
| 706 | 704 | /* rate in Hz, ALSA rate bitmask, HDA format value */ |
|---|
| 707 | 705 | |
|---|
| 708 | 706 | /* autodetected value used in snd_hda_query_supported_pcm */ |
|---|
| .. | .. |
|---|
| 1071 | 1069 | * snd_hdac_sync_power_state - wait until actual power state matches |
|---|
| 1072 | 1070 | * with the target state |
|---|
| 1073 | 1071 | * |
|---|
| 1074 | | - * @hdac: the HDAC device |
|---|
| 1072 | + * @codec: the HDAC device |
|---|
| 1075 | 1073 | * @nid: NID to send the command |
|---|
| 1076 | | - * @target_state: target state to check for |
|---|
| 1074 | + * @power_state: target power state to wait for |
|---|
| 1077 | 1075 | * |
|---|
| 1078 | 1076 | * Return power state or PS_ERROR if codec rejects GET verb. |
|---|
| 1079 | 1077 | */ |
|---|