.. | .. |
---|
| 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 | + mutex_lock(&codec->regmap_lock); |
---|
| 601 | + if (codec->regmap) |
---|
| 602 | + regcache_sync(codec->regmap); |
---|
| 603 | + mutex_unlock(&codec->regmap_lock); |
---|
| 604 | +} |
---|
| 605 | +EXPORT_SYMBOL_GPL(snd_hdac_regmap_sync); |
---|