| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * tas5720.c - ALSA SoC Texas Instruments TAS5720 Mono Audio Amplifier |
|---|
| 3 | 4 | * |
|---|
| 4 | | - * Copyright (C)2015-2016 Texas Instruments Incorporated - http://www.ti.com |
|---|
| 5 | + * Copyright (C)2015-2016 Texas Instruments Incorporated - https://www.ti.com |
|---|
| 5 | 6 | * |
|---|
| 6 | 7 | * Author: Andreas Dannenberg <dannenberg@ti.com> |
|---|
| 7 | | - * |
|---|
| 8 | | - * This program is free software; you can redistribute it and/or |
|---|
| 9 | | - * modify it under the terms of the GNU General Public License |
|---|
| 10 | | - * version 2 as published by the Free Software Foundation. |
|---|
| 11 | | - * |
|---|
| 12 | | - * This program is distributed in the hope that it will be useful, but |
|---|
| 13 | | - * WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 14 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|---|
| 15 | | - * General Public License for more details. |
|---|
| 16 | 8 | */ |
|---|
| 17 | 9 | |
|---|
| 18 | 10 | #include <linux/module.h> |
|---|
| .. | .. |
|---|
| 152 | 144 | int slots, int slot_width) |
|---|
| 153 | 145 | { |
|---|
| 154 | 146 | struct snd_soc_component *component = dai->component; |
|---|
| 147 | + struct tas5720_data *tas5720 = snd_soc_component_get_drvdata(component); |
|---|
| 155 | 148 | unsigned int first_slot; |
|---|
| 156 | 149 | int ret; |
|---|
| 157 | 150 | |
|---|
| .. | .. |
|---|
| 185 | 178 | if (ret < 0) |
|---|
| 186 | 179 | goto error_snd_soc_component_update_bits; |
|---|
| 187 | 180 | |
|---|
| 181 | + /* Configure TDM slot width. This is only applicable to TAS5722. */ |
|---|
| 182 | + switch (tas5720->devtype) { |
|---|
| 183 | + case TAS5722: |
|---|
| 184 | + ret = snd_soc_component_update_bits(component, TAS5722_DIGITAL_CTRL2_REG, |
|---|
| 185 | + TAS5722_TDM_SLOT_16B, |
|---|
| 186 | + slot_width == 16 ? |
|---|
| 187 | + TAS5722_TDM_SLOT_16B : 0); |
|---|
| 188 | + if (ret < 0) |
|---|
| 189 | + goto error_snd_soc_component_update_bits; |
|---|
| 190 | + break; |
|---|
| 191 | + default: |
|---|
| 192 | + break; |
|---|
| 193 | + } |
|---|
| 194 | + |
|---|
| 188 | 195 | return 0; |
|---|
| 189 | 196 | |
|---|
| 190 | 197 | error_snd_soc_component_update_bits: |
|---|
| .. | .. |
|---|
| 192 | 199 | return ret; |
|---|
| 193 | 200 | } |
|---|
| 194 | 201 | |
|---|
| 195 | | -static int tas5720_mute(struct snd_soc_dai *dai, int mute) |
|---|
| 202 | +static int tas5720_mute(struct snd_soc_dai *dai, int mute, int direction) |
|---|
| 196 | 203 | { |
|---|
| 197 | 204 | struct snd_soc_component *component = dai->component; |
|---|
| 198 | 205 | int ret; |
|---|
| .. | .. |
|---|
| 485 | 492 | ); |
|---|
| 486 | 493 | |
|---|
| 487 | 494 | /* |
|---|
| 488 | | - * DAC digital volumes. From -103.5 to 24 dB in 0.5 dB steps. Note that |
|---|
| 489 | | - * setting the gain below -100 dB (register value <0x7) is effectively a MUTE |
|---|
| 490 | | - * as per device datasheet. |
|---|
| 495 | + * DAC digital volumes. From -103.5 to 24 dB in 0.5 dB or 0.25 dB steps |
|---|
| 496 | + * depending on the device. Note that setting the gain below -100 dB |
|---|
| 497 | + * (register value <0x7) is effectively a MUTE as per device datasheet. |
|---|
| 498 | + * |
|---|
| 499 | + * Note that for the TAS5722 the digital volume controls are actually split |
|---|
| 500 | + * over two registers, so we need custom getters/setters for access. |
|---|
| 491 | 501 | */ |
|---|
| 492 | | -static DECLARE_TLV_DB_SCALE(dac_tlv, -10350, 50, 0); |
|---|
| 502 | +static DECLARE_TLV_DB_SCALE(tas5720_dac_tlv, -10350, 50, 0); |
|---|
| 503 | +static DECLARE_TLV_DB_SCALE(tas5722_dac_tlv, -10350, 25, 0); |
|---|
| 504 | + |
|---|
| 505 | +static int tas5722_volume_get(struct snd_kcontrol *kcontrol, |
|---|
| 506 | + struct snd_ctl_elem_value *ucontrol) |
|---|
| 507 | +{ |
|---|
| 508 | + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); |
|---|
| 509 | + unsigned int val; |
|---|
| 510 | + |
|---|
| 511 | + val = snd_soc_component_read(component, TAS5720_VOLUME_CTRL_REG); |
|---|
| 512 | + ucontrol->value.integer.value[0] = val << 1; |
|---|
| 513 | + |
|---|
| 514 | + val = snd_soc_component_read(component, TAS5722_DIGITAL_CTRL2_REG); |
|---|
| 515 | + ucontrol->value.integer.value[0] |= val & TAS5722_VOL_CONTROL_LSB; |
|---|
| 516 | + |
|---|
| 517 | + return 0; |
|---|
| 518 | +} |
|---|
| 519 | + |
|---|
| 520 | +static int tas5722_volume_set(struct snd_kcontrol *kcontrol, |
|---|
| 521 | + struct snd_ctl_elem_value *ucontrol) |
|---|
| 522 | +{ |
|---|
| 523 | + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); |
|---|
| 524 | + unsigned int sel = ucontrol->value.integer.value[0]; |
|---|
| 525 | + |
|---|
| 526 | + snd_soc_component_write(component, TAS5720_VOLUME_CTRL_REG, sel >> 1); |
|---|
| 527 | + snd_soc_component_update_bits(component, TAS5722_DIGITAL_CTRL2_REG, |
|---|
| 528 | + TAS5722_VOL_CONTROL_LSB, sel); |
|---|
| 529 | + |
|---|
| 530 | + return 0; |
|---|
| 531 | +} |
|---|
| 493 | 532 | |
|---|
| 494 | 533 | static const struct snd_kcontrol_new tas5720_snd_controls[] = { |
|---|
| 495 | 534 | SOC_SINGLE_TLV("Speaker Driver Playback Volume", |
|---|
| 496 | | - TAS5720_VOLUME_CTRL_REG, 0, 0xff, 0, dac_tlv), |
|---|
| 535 | + TAS5720_VOLUME_CTRL_REG, 0, 0xff, 0, tas5720_dac_tlv), |
|---|
| 536 | + SOC_SINGLE_TLV("Speaker Driver Analog Gain", TAS5720_ANALOG_CTRL_REG, |
|---|
| 537 | + TAS5720_ANALOG_GAIN_SHIFT, 3, 0, dac_analog_tlv), |
|---|
| 538 | +}; |
|---|
| 539 | + |
|---|
| 540 | +static const struct snd_kcontrol_new tas5722_snd_controls[] = { |
|---|
| 541 | + SOC_SINGLE_EXT_TLV("Speaker Driver Playback Volume", |
|---|
| 542 | + 0, 0, 511, 0, |
|---|
| 543 | + tas5722_volume_get, tas5722_volume_set, |
|---|
| 544 | + tas5722_dac_tlv), |
|---|
| 497 | 545 | SOC_SINGLE_TLV("Speaker Driver Analog Gain", TAS5720_ANALOG_CTRL_REG, |
|---|
| 498 | 546 | TAS5720_ANALOG_GAIN_SHIFT, 3, 0, dac_analog_tlv), |
|---|
| 499 | 547 | }; |
|---|
| .. | .. |
|---|
| 527 | 575 | .non_legacy_dai_naming = 1, |
|---|
| 528 | 576 | }; |
|---|
| 529 | 577 | |
|---|
| 578 | +static const struct snd_soc_component_driver soc_component_dev_tas5722 = { |
|---|
| 579 | + .probe = tas5720_codec_probe, |
|---|
| 580 | + .remove = tas5720_codec_remove, |
|---|
| 581 | + .suspend = tas5720_suspend, |
|---|
| 582 | + .resume = tas5720_resume, |
|---|
| 583 | + .controls = tas5722_snd_controls, |
|---|
| 584 | + .num_controls = ARRAY_SIZE(tas5722_snd_controls), |
|---|
| 585 | + .dapm_widgets = tas5720_dapm_widgets, |
|---|
| 586 | + .num_dapm_widgets = ARRAY_SIZE(tas5720_dapm_widgets), |
|---|
| 587 | + .dapm_routes = tas5720_audio_map, |
|---|
| 588 | + .num_dapm_routes = ARRAY_SIZE(tas5720_audio_map), |
|---|
| 589 | + .idle_bias_on = 1, |
|---|
| 590 | + .use_pmdown_time = 1, |
|---|
| 591 | + .endianness = 1, |
|---|
| 592 | + .non_legacy_dai_naming = 1, |
|---|
| 593 | +}; |
|---|
| 594 | + |
|---|
| 530 | 595 | /* PCM rates supported by the TAS5720 driver */ |
|---|
| 531 | 596 | #define TAS5720_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\ |
|---|
| 532 | 597 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000) |
|---|
| .. | .. |
|---|
| 539 | 604 | .hw_params = tas5720_hw_params, |
|---|
| 540 | 605 | .set_fmt = tas5720_set_dai_fmt, |
|---|
| 541 | 606 | .set_tdm_slot = tas5720_set_dai_tdm_slot, |
|---|
| 542 | | - .digital_mute = tas5720_mute, |
|---|
| 607 | + .mute_stream = tas5720_mute, |
|---|
| 608 | + .no_capture_mute = 1, |
|---|
| 543 | 609 | }; |
|---|
| 544 | 610 | |
|---|
| 545 | 611 | /* |
|---|
| .. | .. |
|---|
| 613 | 679 | |
|---|
| 614 | 680 | dev_set_drvdata(dev, data); |
|---|
| 615 | 681 | |
|---|
| 616 | | - ret = devm_snd_soc_register_component(&client->dev, |
|---|
| 617 | | - &soc_component_dev_tas5720, |
|---|
| 618 | | - tas5720_dai, ARRAY_SIZE(tas5720_dai)); |
|---|
| 682 | + switch (id->driver_data) { |
|---|
| 683 | + case TAS5720: |
|---|
| 684 | + ret = devm_snd_soc_register_component(&client->dev, |
|---|
| 685 | + &soc_component_dev_tas5720, |
|---|
| 686 | + tas5720_dai, |
|---|
| 687 | + ARRAY_SIZE(tas5720_dai)); |
|---|
| 688 | + break; |
|---|
| 689 | + case TAS5722: |
|---|
| 690 | + ret = devm_snd_soc_register_component(&client->dev, |
|---|
| 691 | + &soc_component_dev_tas5722, |
|---|
| 692 | + tas5720_dai, |
|---|
| 693 | + ARRAY_SIZE(tas5720_dai)); |
|---|
| 694 | + break; |
|---|
| 695 | + default: |
|---|
| 696 | + dev_err(dev, "unexpected private driver data\n"); |
|---|
| 697 | + return -EINVAL; |
|---|
| 698 | + } |
|---|
| 619 | 699 | if (ret < 0) { |
|---|
| 620 | 700 | dev_err(dev, "failed to register component: %d\n", ret); |
|---|
| 621 | 701 | return ret; |
|---|