| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Regmap support for HD-audio verbs |
|---|
| 3 | 4 | * |
|---|
| .. | .. |
|---|
| 20 | 21 | #include <sound/core.h> |
|---|
| 21 | 22 | #include <sound/hdaudio.h> |
|---|
| 22 | 23 | #include <sound/hda_regmap.h> |
|---|
| 24 | +#include "local.h" |
|---|
| 23 | 25 | |
|---|
| 24 | 26 | static int codec_pm_lock(struct hdac_device *codec) |
|---|
| 25 | 27 | { |
|---|
| .. | .. |
|---|
| 359 | 361 | .cache_type = REGCACHE_RBTREE, |
|---|
| 360 | 362 | .reg_read = hda_reg_read, |
|---|
| 361 | 363 | .reg_write = hda_reg_write, |
|---|
| 362 | | - .use_single_rw = true, |
|---|
| 364 | + .use_single_read = true, |
|---|
| 365 | + .use_single_write = true, |
|---|
| 366 | + .disable_locking = true, |
|---|
| 363 | 367 | }; |
|---|
| 364 | 368 | |
|---|
| 365 | 369 | /** |
|---|
| .. | .. |
|---|
| 422 | 426 | static int reg_raw_write(struct hdac_device *codec, unsigned int reg, |
|---|
| 423 | 427 | unsigned int val) |
|---|
| 424 | 428 | { |
|---|
| 429 | + int err; |
|---|
| 430 | + |
|---|
| 431 | + mutex_lock(&codec->regmap_lock); |
|---|
| 425 | 432 | if (!codec->regmap) |
|---|
| 426 | | - return hda_reg_write(codec, reg, val); |
|---|
| 433 | + err = hda_reg_write(codec, reg, val); |
|---|
| 427 | 434 | else |
|---|
| 428 | | - return regmap_write(codec->regmap, reg, val); |
|---|
| 435 | + err = regmap_write(codec->regmap, reg, val); |
|---|
| 436 | + mutex_unlock(&codec->regmap_lock); |
|---|
| 437 | + return err; |
|---|
| 429 | 438 | } |
|---|
| 439 | + |
|---|
| 440 | +/* a helper macro to call @func_call; retry with power-up if failed */ |
|---|
| 441 | +#define CALL_RAW_FUNC(codec, func_call) \ |
|---|
| 442 | + ({ \ |
|---|
| 443 | + int _err = func_call; \ |
|---|
| 444 | + if (_err == -EAGAIN) { \ |
|---|
| 445 | + _err = snd_hdac_power_up_pm(codec); \ |
|---|
| 446 | + if (_err >= 0) \ |
|---|
| 447 | + _err = func_call; \ |
|---|
| 448 | + snd_hdac_power_down_pm(codec); \ |
|---|
| 449 | + } \ |
|---|
| 450 | + _err;}) |
|---|
| 430 | 451 | |
|---|
| 431 | 452 | /** |
|---|
| 432 | 453 | * snd_hdac_regmap_write_raw - write a pseudo register with power mgmt |
|---|
| .. | .. |
|---|
| 439 | 460 | int snd_hdac_regmap_write_raw(struct hdac_device *codec, unsigned int reg, |
|---|
| 440 | 461 | unsigned int val) |
|---|
| 441 | 462 | { |
|---|
| 442 | | - int err; |
|---|
| 443 | | - |
|---|
| 444 | | - err = reg_raw_write(codec, reg, val); |
|---|
| 445 | | - if (err == -EAGAIN) { |
|---|
| 446 | | - err = snd_hdac_power_up_pm(codec); |
|---|
| 447 | | - if (err >= 0) |
|---|
| 448 | | - err = reg_raw_write(codec, reg, val); |
|---|
| 449 | | - snd_hdac_power_down_pm(codec); |
|---|
| 450 | | - } |
|---|
| 451 | | - return err; |
|---|
| 463 | + return CALL_RAW_FUNC(codec, reg_raw_write(codec, reg, val)); |
|---|
| 452 | 464 | } |
|---|
| 453 | 465 | EXPORT_SYMBOL_GPL(snd_hdac_regmap_write_raw); |
|---|
| 454 | 466 | |
|---|
| 455 | 467 | static int reg_raw_read(struct hdac_device *codec, unsigned int reg, |
|---|
| 456 | 468 | unsigned int *val, bool uncached) |
|---|
| 457 | 469 | { |
|---|
| 470 | + int err; |
|---|
| 471 | + |
|---|
| 472 | + mutex_lock(&codec->regmap_lock); |
|---|
| 458 | 473 | if (uncached || !codec->regmap) |
|---|
| 459 | | - return hda_reg_read(codec, reg, val); |
|---|
| 474 | + err = hda_reg_read(codec, reg, val); |
|---|
| 460 | 475 | else |
|---|
| 461 | | - return regmap_read(codec->regmap, reg, val); |
|---|
| 476 | + err = regmap_read(codec->regmap, reg, val); |
|---|
| 477 | + mutex_unlock(&codec->regmap_lock); |
|---|
| 478 | + return err; |
|---|
| 462 | 479 | } |
|---|
| 463 | 480 | |
|---|
| 464 | 481 | static int __snd_hdac_regmap_read_raw(struct hdac_device *codec, |
|---|
| 465 | 482 | unsigned int reg, unsigned int *val, |
|---|
| 466 | 483 | bool uncached) |
|---|
| 467 | 484 | { |
|---|
| 468 | | - int err; |
|---|
| 469 | | - |
|---|
| 470 | | - err = reg_raw_read(codec, reg, val, uncached); |
|---|
| 471 | | - if (err == -EAGAIN) { |
|---|
| 472 | | - err = snd_hdac_power_up_pm(codec); |
|---|
| 473 | | - if (err >= 0) |
|---|
| 474 | | - err = reg_raw_read(codec, reg, val, uncached); |
|---|
| 475 | | - snd_hdac_power_down_pm(codec); |
|---|
| 476 | | - } |
|---|
| 477 | | - return err; |
|---|
| 485 | + return CALL_RAW_FUNC(codec, reg_raw_read(codec, reg, val, uncached)); |
|---|
| 478 | 486 | } |
|---|
| 479 | 487 | |
|---|
| 480 | 488 | /** |
|---|
| .. | .. |
|---|
| 501 | 509 | return __snd_hdac_regmap_read_raw(codec, reg, val, true); |
|---|
| 502 | 510 | } |
|---|
| 503 | 511 | |
|---|
| 512 | +static int reg_raw_update(struct hdac_device *codec, unsigned int reg, |
|---|
| 513 | + unsigned int mask, unsigned int val) |
|---|
| 514 | +{ |
|---|
| 515 | + unsigned int orig; |
|---|
| 516 | + bool change; |
|---|
| 517 | + int err; |
|---|
| 518 | + |
|---|
| 519 | + mutex_lock(&codec->regmap_lock); |
|---|
| 520 | + if (codec->regmap) { |
|---|
| 521 | + err = regmap_update_bits_check(codec->regmap, reg, mask, val, |
|---|
| 522 | + &change); |
|---|
| 523 | + if (!err) |
|---|
| 524 | + err = change ? 1 : 0; |
|---|
| 525 | + } else { |
|---|
| 526 | + err = hda_reg_read(codec, reg, &orig); |
|---|
| 527 | + if (!err) { |
|---|
| 528 | + val &= mask; |
|---|
| 529 | + val |= orig & ~mask; |
|---|
| 530 | + if (val != orig) { |
|---|
| 531 | + err = hda_reg_write(codec, reg, val); |
|---|
| 532 | + if (!err) |
|---|
| 533 | + err = 1; |
|---|
| 534 | + } |
|---|
| 535 | + } |
|---|
| 536 | + } |
|---|
| 537 | + mutex_unlock(&codec->regmap_lock); |
|---|
| 538 | + return err; |
|---|
| 539 | +} |
|---|
| 540 | + |
|---|
| 504 | 541 | /** |
|---|
| 505 | 542 | * snd_hdac_regmap_update_raw - update a pseudo register with power mgmt |
|---|
| 506 | 543 | * @codec: the codec object |
|---|
| 507 | 544 | * @reg: pseudo register |
|---|
| 508 | | - * @mask: bit mask to udpate |
|---|
| 545 | + * @mask: bit mask to update |
|---|
| 509 | 546 | * @val: value to update |
|---|
| 510 | 547 | * |
|---|
| 511 | 548 | * Returns zero if successful or a negative error code. |
|---|
| .. | .. |
|---|
| 513 | 550 | int snd_hdac_regmap_update_raw(struct hdac_device *codec, unsigned int reg, |
|---|
| 514 | 551 | unsigned int mask, unsigned int val) |
|---|
| 515 | 552 | { |
|---|
| 553 | + return CALL_RAW_FUNC(codec, reg_raw_update(codec, reg, mask, val)); |
|---|
| 554 | +} |
|---|
| 555 | +EXPORT_SYMBOL_GPL(snd_hdac_regmap_update_raw); |
|---|
| 556 | + |
|---|
| 557 | +static int reg_raw_update_once(struct hdac_device *codec, unsigned int reg, |
|---|
| 558 | + unsigned int mask, unsigned int val) |
|---|
| 559 | +{ |
|---|
| 516 | 560 | unsigned int orig; |
|---|
| 517 | 561 | int err; |
|---|
| 518 | 562 | |
|---|
| 519 | | - val &= mask; |
|---|
| 520 | | - err = snd_hdac_regmap_read_raw(codec, reg, &orig); |
|---|
| 563 | + if (!codec->regmap) |
|---|
| 564 | + return reg_raw_update(codec, reg, mask, val); |
|---|
| 565 | + |
|---|
| 566 | + mutex_lock(&codec->regmap_lock); |
|---|
| 567 | + regcache_cache_only(codec->regmap, true); |
|---|
| 568 | + err = regmap_read(codec->regmap, reg, &orig); |
|---|
| 569 | + regcache_cache_only(codec->regmap, false); |
|---|
| 521 | 570 | if (err < 0) |
|---|
| 522 | | - return err; |
|---|
| 523 | | - val |= orig & ~mask; |
|---|
| 524 | | - if (val == orig) |
|---|
| 525 | | - return 0; |
|---|
| 526 | | - err = snd_hdac_regmap_write_raw(codec, reg, val); |
|---|
| 527 | | - if (err < 0) |
|---|
| 528 | | - return err; |
|---|
| 529 | | - return 1; |
|---|
| 571 | + err = regmap_update_bits(codec->regmap, reg, mask, val); |
|---|
| 572 | + mutex_unlock(&codec->regmap_lock); |
|---|
| 573 | + return err; |
|---|
| 530 | 574 | } |
|---|
| 531 | | -EXPORT_SYMBOL_GPL(snd_hdac_regmap_update_raw); |
|---|
| 575 | + |
|---|
| 576 | +/** |
|---|
| 577 | + * snd_hdac_regmap_update_raw_once - initialize the register value only once |
|---|
| 578 | + * @codec: the codec object |
|---|
| 579 | + * @reg: pseudo register |
|---|
| 580 | + * @mask: bit mask to update |
|---|
| 581 | + * @val: value to update |
|---|
| 582 | + * |
|---|
| 583 | + * Performs the update of the register bits only once when the register |
|---|
| 584 | + * hasn't been initialized yet. Used in HD-audio legacy driver. |
|---|
| 585 | + * Returns zero if successful or a negative error code |
|---|
| 586 | + */ |
|---|
| 587 | +int snd_hdac_regmap_update_raw_once(struct hdac_device *codec, unsigned int reg, |
|---|
| 588 | + unsigned int mask, unsigned int val) |
|---|
| 589 | +{ |
|---|
| 590 | + return CALL_RAW_FUNC(codec, reg_raw_update_once(codec, reg, mask, val)); |
|---|
| 591 | +} |
|---|
| 592 | +EXPORT_SYMBOL_GPL(snd_hdac_regmap_update_raw_once); |
|---|
| 593 | + |
|---|
| 594 | +/** |
|---|
| 595 | + * snd_hdac_regmap_sync - sync out the cached values for PM resume |
|---|
| 596 | + * @codec: the codec object |
|---|
| 597 | + */ |
|---|
| 598 | +void snd_hdac_regmap_sync(struct hdac_device *codec) |
|---|
| 599 | +{ |
|---|
| 600 | + if (codec->regmap) { |
|---|
| 601 | + mutex_lock(&codec->regmap_lock); |
|---|
| 602 | + regcache_sync(codec->regmap); |
|---|
| 603 | + mutex_unlock(&codec->regmap_lock); |
|---|
| 604 | + } |
|---|
| 605 | +} |
|---|
| 606 | +EXPORT_SYMBOL_GPL(snd_hdac_regmap_sync); |
|---|