18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * tas5720.c - ALSA SoC Texas Instruments TAS5720 Mono Audio Amplifier 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C)2015-2016 Texas Instruments Incorporated - https://www.ti.com 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Author: Andreas Dannenberg <dannenberg@ti.com> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/errno.h> 128c2ecf20Sopenharmony_ci#include <linux/device.h> 138c2ecf20Sopenharmony_ci#include <linux/i2c.h> 148c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 158c2ecf20Sopenharmony_ci#include <linux/regmap.h> 168c2ecf20Sopenharmony_ci#include <linux/slab.h> 178c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h> 188c2ecf20Sopenharmony_ci#include <linux/delay.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include <sound/pcm.h> 218c2ecf20Sopenharmony_ci#include <sound/pcm_params.h> 228c2ecf20Sopenharmony_ci#include <sound/soc.h> 238c2ecf20Sopenharmony_ci#include <sound/soc-dapm.h> 248c2ecf20Sopenharmony_ci#include <sound/tlv.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include "tas5720.h" 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci/* Define how often to check (and clear) the fault status register (in ms) */ 298c2ecf20Sopenharmony_ci#define TAS5720_FAULT_CHECK_INTERVAL 200 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cienum tas572x_type { 328c2ecf20Sopenharmony_ci TAS5720, 338c2ecf20Sopenharmony_ci TAS5722, 348c2ecf20Sopenharmony_ci}; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic const char * const tas5720_supply_names[] = { 378c2ecf20Sopenharmony_ci "dvdd", /* Digital power supply. Connect to 3.3-V supply. */ 388c2ecf20Sopenharmony_ci "pvdd", /* Class-D amp and analog power supply (connected). */ 398c2ecf20Sopenharmony_ci}; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci#define TAS5720_NUM_SUPPLIES ARRAY_SIZE(tas5720_supply_names) 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistruct tas5720_data { 448c2ecf20Sopenharmony_ci struct snd_soc_component *component; 458c2ecf20Sopenharmony_ci struct regmap *regmap; 468c2ecf20Sopenharmony_ci struct i2c_client *tas5720_client; 478c2ecf20Sopenharmony_ci enum tas572x_type devtype; 488c2ecf20Sopenharmony_ci struct regulator_bulk_data supplies[TAS5720_NUM_SUPPLIES]; 498c2ecf20Sopenharmony_ci struct delayed_work fault_check_work; 508c2ecf20Sopenharmony_ci unsigned int last_fault; 518c2ecf20Sopenharmony_ci}; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic int tas5720_hw_params(struct snd_pcm_substream *substream, 548c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *params, 558c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci struct snd_soc_component *component = dai->component; 588c2ecf20Sopenharmony_ci unsigned int rate = params_rate(params); 598c2ecf20Sopenharmony_ci bool ssz_ds; 608c2ecf20Sopenharmony_ci int ret; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci switch (rate) { 638c2ecf20Sopenharmony_ci case 44100: 648c2ecf20Sopenharmony_ci case 48000: 658c2ecf20Sopenharmony_ci ssz_ds = false; 668c2ecf20Sopenharmony_ci break; 678c2ecf20Sopenharmony_ci case 88200: 688c2ecf20Sopenharmony_ci case 96000: 698c2ecf20Sopenharmony_ci ssz_ds = true; 708c2ecf20Sopenharmony_ci break; 718c2ecf20Sopenharmony_ci default: 728c2ecf20Sopenharmony_ci dev_err(component->dev, "unsupported sample rate: %u\n", rate); 738c2ecf20Sopenharmony_ci return -EINVAL; 748c2ecf20Sopenharmony_ci } 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci ret = snd_soc_component_update_bits(component, TAS5720_DIGITAL_CTRL1_REG, 778c2ecf20Sopenharmony_ci TAS5720_SSZ_DS, ssz_ds); 788c2ecf20Sopenharmony_ci if (ret < 0) { 798c2ecf20Sopenharmony_ci dev_err(component->dev, "error setting sample rate: %d\n", ret); 808c2ecf20Sopenharmony_ci return ret; 818c2ecf20Sopenharmony_ci } 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci return 0; 848c2ecf20Sopenharmony_ci} 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistatic int tas5720_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci struct snd_soc_component *component = dai->component; 898c2ecf20Sopenharmony_ci u8 serial_format; 908c2ecf20Sopenharmony_ci int ret; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) { 938c2ecf20Sopenharmony_ci dev_vdbg(component->dev, "DAI Format master is not found\n"); 948c2ecf20Sopenharmony_ci return -EINVAL; 958c2ecf20Sopenharmony_ci } 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci switch (fmt & (SND_SOC_DAIFMT_FORMAT_MASK | 988c2ecf20Sopenharmony_ci SND_SOC_DAIFMT_INV_MASK)) { 998c2ecf20Sopenharmony_ci case (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF): 1008c2ecf20Sopenharmony_ci /* 1st data bit occur one BCLK cycle after the frame sync */ 1018c2ecf20Sopenharmony_ci serial_format = TAS5720_SAIF_I2S; 1028c2ecf20Sopenharmony_ci break; 1038c2ecf20Sopenharmony_ci case (SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_NB_NF): 1048c2ecf20Sopenharmony_ci /* 1058c2ecf20Sopenharmony_ci * Note that although the TAS5720 does not have a dedicated DSP 1068c2ecf20Sopenharmony_ci * mode it doesn't care about the LRCLK duty cycle during TDM 1078c2ecf20Sopenharmony_ci * operation. Therefore we can use the device's I2S mode with 1088c2ecf20Sopenharmony_ci * its delaying of the 1st data bit to receive DSP_A formatted 1098c2ecf20Sopenharmony_ci * data. See device datasheet for additional details. 1108c2ecf20Sopenharmony_ci */ 1118c2ecf20Sopenharmony_ci serial_format = TAS5720_SAIF_I2S; 1128c2ecf20Sopenharmony_ci break; 1138c2ecf20Sopenharmony_ci case (SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_NB_NF): 1148c2ecf20Sopenharmony_ci /* 1158c2ecf20Sopenharmony_ci * Similar to DSP_A, we can use the fact that the TAS5720 does 1168c2ecf20Sopenharmony_ci * not care about the LRCLK duty cycle during TDM to receive 1178c2ecf20Sopenharmony_ci * DSP_B formatted data in LEFTJ mode (no delaying of the 1st 1188c2ecf20Sopenharmony_ci * data bit). 1198c2ecf20Sopenharmony_ci */ 1208c2ecf20Sopenharmony_ci serial_format = TAS5720_SAIF_LEFTJ; 1218c2ecf20Sopenharmony_ci break; 1228c2ecf20Sopenharmony_ci case (SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF): 1238c2ecf20Sopenharmony_ci /* No delay after the frame sync */ 1248c2ecf20Sopenharmony_ci serial_format = TAS5720_SAIF_LEFTJ; 1258c2ecf20Sopenharmony_ci break; 1268c2ecf20Sopenharmony_ci default: 1278c2ecf20Sopenharmony_ci dev_vdbg(component->dev, "DAI Format is not found\n"); 1288c2ecf20Sopenharmony_ci return -EINVAL; 1298c2ecf20Sopenharmony_ci } 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci ret = snd_soc_component_update_bits(component, TAS5720_DIGITAL_CTRL1_REG, 1328c2ecf20Sopenharmony_ci TAS5720_SAIF_FORMAT_MASK, 1338c2ecf20Sopenharmony_ci serial_format); 1348c2ecf20Sopenharmony_ci if (ret < 0) { 1358c2ecf20Sopenharmony_ci dev_err(component->dev, "error setting SAIF format: %d\n", ret); 1368c2ecf20Sopenharmony_ci return ret; 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci return 0; 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic int tas5720_set_dai_tdm_slot(struct snd_soc_dai *dai, 1438c2ecf20Sopenharmony_ci unsigned int tx_mask, unsigned int rx_mask, 1448c2ecf20Sopenharmony_ci int slots, int slot_width) 1458c2ecf20Sopenharmony_ci{ 1468c2ecf20Sopenharmony_ci struct snd_soc_component *component = dai->component; 1478c2ecf20Sopenharmony_ci struct tas5720_data *tas5720 = snd_soc_component_get_drvdata(component); 1488c2ecf20Sopenharmony_ci unsigned int first_slot; 1498c2ecf20Sopenharmony_ci int ret; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci if (!tx_mask) { 1528c2ecf20Sopenharmony_ci dev_err(component->dev, "tx masks must not be 0\n"); 1538c2ecf20Sopenharmony_ci return -EINVAL; 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci /* 1578c2ecf20Sopenharmony_ci * Determine the first slot that is being requested. We will only 1588c2ecf20Sopenharmony_ci * use the first slot that is found since the TAS5720 is a mono 1598c2ecf20Sopenharmony_ci * amplifier. 1608c2ecf20Sopenharmony_ci */ 1618c2ecf20Sopenharmony_ci first_slot = __ffs(tx_mask); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci if (first_slot > 7) { 1648c2ecf20Sopenharmony_ci dev_err(component->dev, "slot selection out of bounds (%u)\n", 1658c2ecf20Sopenharmony_ci first_slot); 1668c2ecf20Sopenharmony_ci return -EINVAL; 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci /* Enable manual TDM slot selection (instead of I2C ID based) */ 1708c2ecf20Sopenharmony_ci ret = snd_soc_component_update_bits(component, TAS5720_DIGITAL_CTRL1_REG, 1718c2ecf20Sopenharmony_ci TAS5720_TDM_CFG_SRC, TAS5720_TDM_CFG_SRC); 1728c2ecf20Sopenharmony_ci if (ret < 0) 1738c2ecf20Sopenharmony_ci goto error_snd_soc_component_update_bits; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci /* Configure the TDM slot to process audio from */ 1768c2ecf20Sopenharmony_ci ret = snd_soc_component_update_bits(component, TAS5720_DIGITAL_CTRL2_REG, 1778c2ecf20Sopenharmony_ci TAS5720_TDM_SLOT_SEL_MASK, first_slot); 1788c2ecf20Sopenharmony_ci if (ret < 0) 1798c2ecf20Sopenharmony_ci goto error_snd_soc_component_update_bits; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci /* Configure TDM slot width. This is only applicable to TAS5722. */ 1828c2ecf20Sopenharmony_ci switch (tas5720->devtype) { 1838c2ecf20Sopenharmony_ci case TAS5722: 1848c2ecf20Sopenharmony_ci ret = snd_soc_component_update_bits(component, TAS5722_DIGITAL_CTRL2_REG, 1858c2ecf20Sopenharmony_ci TAS5722_TDM_SLOT_16B, 1868c2ecf20Sopenharmony_ci slot_width == 16 ? 1878c2ecf20Sopenharmony_ci TAS5722_TDM_SLOT_16B : 0); 1888c2ecf20Sopenharmony_ci if (ret < 0) 1898c2ecf20Sopenharmony_ci goto error_snd_soc_component_update_bits; 1908c2ecf20Sopenharmony_ci break; 1918c2ecf20Sopenharmony_ci default: 1928c2ecf20Sopenharmony_ci break; 1938c2ecf20Sopenharmony_ci } 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci return 0; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cierror_snd_soc_component_update_bits: 1988c2ecf20Sopenharmony_ci dev_err(component->dev, "error configuring TDM mode: %d\n", ret); 1998c2ecf20Sopenharmony_ci return ret; 2008c2ecf20Sopenharmony_ci} 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_cistatic int tas5720_mute(struct snd_soc_dai *dai, int mute, int direction) 2038c2ecf20Sopenharmony_ci{ 2048c2ecf20Sopenharmony_ci struct snd_soc_component *component = dai->component; 2058c2ecf20Sopenharmony_ci int ret; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci ret = snd_soc_component_update_bits(component, TAS5720_DIGITAL_CTRL2_REG, 2088c2ecf20Sopenharmony_ci TAS5720_MUTE, mute ? TAS5720_MUTE : 0); 2098c2ecf20Sopenharmony_ci if (ret < 0) { 2108c2ecf20Sopenharmony_ci dev_err(component->dev, "error (un-)muting device: %d\n", ret); 2118c2ecf20Sopenharmony_ci return ret; 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci return 0; 2158c2ecf20Sopenharmony_ci} 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_cistatic void tas5720_fault_check_work(struct work_struct *work) 2188c2ecf20Sopenharmony_ci{ 2198c2ecf20Sopenharmony_ci struct tas5720_data *tas5720 = container_of(work, struct tas5720_data, 2208c2ecf20Sopenharmony_ci fault_check_work.work); 2218c2ecf20Sopenharmony_ci struct device *dev = tas5720->component->dev; 2228c2ecf20Sopenharmony_ci unsigned int curr_fault; 2238c2ecf20Sopenharmony_ci int ret; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci ret = regmap_read(tas5720->regmap, TAS5720_FAULT_REG, &curr_fault); 2268c2ecf20Sopenharmony_ci if (ret < 0) { 2278c2ecf20Sopenharmony_ci dev_err(dev, "failed to read FAULT register: %d\n", ret); 2288c2ecf20Sopenharmony_ci goto out; 2298c2ecf20Sopenharmony_ci } 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci /* Check/handle all errors except SAIF clock errors */ 2328c2ecf20Sopenharmony_ci curr_fault &= TAS5720_OCE | TAS5720_DCE | TAS5720_OTE; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci /* 2358c2ecf20Sopenharmony_ci * Only flag errors once for a given occurrence. This is needed as 2368c2ecf20Sopenharmony_ci * the TAS5720 will take time clearing the fault condition internally 2378c2ecf20Sopenharmony_ci * during which we don't want to bombard the system with the same 2388c2ecf20Sopenharmony_ci * error message over and over. 2398c2ecf20Sopenharmony_ci */ 2408c2ecf20Sopenharmony_ci if ((curr_fault & TAS5720_OCE) && !(tas5720->last_fault & TAS5720_OCE)) 2418c2ecf20Sopenharmony_ci dev_crit(dev, "experienced an over current hardware fault\n"); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci if ((curr_fault & TAS5720_DCE) && !(tas5720->last_fault & TAS5720_DCE)) 2448c2ecf20Sopenharmony_ci dev_crit(dev, "experienced a DC detection fault\n"); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci if ((curr_fault & TAS5720_OTE) && !(tas5720->last_fault & TAS5720_OTE)) 2478c2ecf20Sopenharmony_ci dev_crit(dev, "experienced an over temperature fault\n"); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci /* Store current fault value so we can detect any changes next time */ 2508c2ecf20Sopenharmony_ci tas5720->last_fault = curr_fault; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci if (!curr_fault) 2538c2ecf20Sopenharmony_ci goto out; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci /* 2568c2ecf20Sopenharmony_ci * Periodically toggle SDZ (shutdown bit) H->L->H to clear any latching 2578c2ecf20Sopenharmony_ci * faults as long as a fault condition persists. Always going through 2588c2ecf20Sopenharmony_ci * the full sequence no matter the first return value to minimizes 2598c2ecf20Sopenharmony_ci * chances for the device to end up in shutdown mode. 2608c2ecf20Sopenharmony_ci */ 2618c2ecf20Sopenharmony_ci ret = regmap_write_bits(tas5720->regmap, TAS5720_POWER_CTRL_REG, 2628c2ecf20Sopenharmony_ci TAS5720_SDZ, 0); 2638c2ecf20Sopenharmony_ci if (ret < 0) 2648c2ecf20Sopenharmony_ci dev_err(dev, "failed to write POWER_CTRL register: %d\n", ret); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci ret = regmap_write_bits(tas5720->regmap, TAS5720_POWER_CTRL_REG, 2678c2ecf20Sopenharmony_ci TAS5720_SDZ, TAS5720_SDZ); 2688c2ecf20Sopenharmony_ci if (ret < 0) 2698c2ecf20Sopenharmony_ci dev_err(dev, "failed to write POWER_CTRL register: %d\n", ret); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ciout: 2728c2ecf20Sopenharmony_ci /* Schedule the next fault check at the specified interval */ 2738c2ecf20Sopenharmony_ci schedule_delayed_work(&tas5720->fault_check_work, 2748c2ecf20Sopenharmony_ci msecs_to_jiffies(TAS5720_FAULT_CHECK_INTERVAL)); 2758c2ecf20Sopenharmony_ci} 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_cistatic int tas5720_codec_probe(struct snd_soc_component *component) 2788c2ecf20Sopenharmony_ci{ 2798c2ecf20Sopenharmony_ci struct tas5720_data *tas5720 = snd_soc_component_get_drvdata(component); 2808c2ecf20Sopenharmony_ci unsigned int device_id, expected_device_id; 2818c2ecf20Sopenharmony_ci int ret; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci tas5720->component = component; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci ret = regulator_bulk_enable(ARRAY_SIZE(tas5720->supplies), 2868c2ecf20Sopenharmony_ci tas5720->supplies); 2878c2ecf20Sopenharmony_ci if (ret != 0) { 2888c2ecf20Sopenharmony_ci dev_err(component->dev, "failed to enable supplies: %d\n", ret); 2898c2ecf20Sopenharmony_ci return ret; 2908c2ecf20Sopenharmony_ci } 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci /* 2938c2ecf20Sopenharmony_ci * Take a liberal approach to checking the device ID to allow the 2948c2ecf20Sopenharmony_ci * driver to be used even if the device ID does not match, however 2958c2ecf20Sopenharmony_ci * issue a warning if there is a mismatch. 2968c2ecf20Sopenharmony_ci */ 2978c2ecf20Sopenharmony_ci ret = regmap_read(tas5720->regmap, TAS5720_DEVICE_ID_REG, &device_id); 2988c2ecf20Sopenharmony_ci if (ret < 0) { 2998c2ecf20Sopenharmony_ci dev_err(component->dev, "failed to read device ID register: %d\n", 3008c2ecf20Sopenharmony_ci ret); 3018c2ecf20Sopenharmony_ci goto probe_fail; 3028c2ecf20Sopenharmony_ci } 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci switch (tas5720->devtype) { 3058c2ecf20Sopenharmony_ci case TAS5720: 3068c2ecf20Sopenharmony_ci expected_device_id = TAS5720_DEVICE_ID; 3078c2ecf20Sopenharmony_ci break; 3088c2ecf20Sopenharmony_ci case TAS5722: 3098c2ecf20Sopenharmony_ci expected_device_id = TAS5722_DEVICE_ID; 3108c2ecf20Sopenharmony_ci break; 3118c2ecf20Sopenharmony_ci default: 3128c2ecf20Sopenharmony_ci dev_err(component->dev, "unexpected private driver data\n"); 3138c2ecf20Sopenharmony_ci return -EINVAL; 3148c2ecf20Sopenharmony_ci } 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci if (device_id != expected_device_id) 3178c2ecf20Sopenharmony_ci dev_warn(component->dev, "wrong device ID. expected: %u read: %u\n", 3188c2ecf20Sopenharmony_ci expected_device_id, device_id); 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci /* Set device to mute */ 3218c2ecf20Sopenharmony_ci ret = snd_soc_component_update_bits(component, TAS5720_DIGITAL_CTRL2_REG, 3228c2ecf20Sopenharmony_ci TAS5720_MUTE, TAS5720_MUTE); 3238c2ecf20Sopenharmony_ci if (ret < 0) 3248c2ecf20Sopenharmony_ci goto error_snd_soc_component_update_bits; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci /* 3278c2ecf20Sopenharmony_ci * Enter shutdown mode - our default when not playing audio - to 3288c2ecf20Sopenharmony_ci * minimize current consumption. On the TAS5720 there is no real down 3298c2ecf20Sopenharmony_ci * side doing so as all device registers are preserved and the wakeup 3308c2ecf20Sopenharmony_ci * of the codec is rather quick which we do using a dapm widget. 3318c2ecf20Sopenharmony_ci */ 3328c2ecf20Sopenharmony_ci ret = snd_soc_component_update_bits(component, TAS5720_POWER_CTRL_REG, 3338c2ecf20Sopenharmony_ci TAS5720_SDZ, 0); 3348c2ecf20Sopenharmony_ci if (ret < 0) 3358c2ecf20Sopenharmony_ci goto error_snd_soc_component_update_bits; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&tas5720->fault_check_work, tas5720_fault_check_work); 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci return 0; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_cierror_snd_soc_component_update_bits: 3428c2ecf20Sopenharmony_ci dev_err(component->dev, "error configuring device registers: %d\n", ret); 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ciprobe_fail: 3458c2ecf20Sopenharmony_ci regulator_bulk_disable(ARRAY_SIZE(tas5720->supplies), 3468c2ecf20Sopenharmony_ci tas5720->supplies); 3478c2ecf20Sopenharmony_ci return ret; 3488c2ecf20Sopenharmony_ci} 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_cistatic void tas5720_codec_remove(struct snd_soc_component *component) 3518c2ecf20Sopenharmony_ci{ 3528c2ecf20Sopenharmony_ci struct tas5720_data *tas5720 = snd_soc_component_get_drvdata(component); 3538c2ecf20Sopenharmony_ci int ret; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&tas5720->fault_check_work); 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci ret = regulator_bulk_disable(ARRAY_SIZE(tas5720->supplies), 3588c2ecf20Sopenharmony_ci tas5720->supplies); 3598c2ecf20Sopenharmony_ci if (ret < 0) 3608c2ecf20Sopenharmony_ci dev_err(component->dev, "failed to disable supplies: %d\n", ret); 3618c2ecf20Sopenharmony_ci}; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_cistatic int tas5720_dac_event(struct snd_soc_dapm_widget *w, 3648c2ecf20Sopenharmony_ci struct snd_kcontrol *kcontrol, int event) 3658c2ecf20Sopenharmony_ci{ 3668c2ecf20Sopenharmony_ci struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); 3678c2ecf20Sopenharmony_ci struct tas5720_data *tas5720 = snd_soc_component_get_drvdata(component); 3688c2ecf20Sopenharmony_ci int ret; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci if (event & SND_SOC_DAPM_POST_PMU) { 3718c2ecf20Sopenharmony_ci /* Take TAS5720 out of shutdown mode */ 3728c2ecf20Sopenharmony_ci ret = snd_soc_component_update_bits(component, TAS5720_POWER_CTRL_REG, 3738c2ecf20Sopenharmony_ci TAS5720_SDZ, TAS5720_SDZ); 3748c2ecf20Sopenharmony_ci if (ret < 0) { 3758c2ecf20Sopenharmony_ci dev_err(component->dev, "error waking component: %d\n", ret); 3768c2ecf20Sopenharmony_ci return ret; 3778c2ecf20Sopenharmony_ci } 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci /* 3808c2ecf20Sopenharmony_ci * Observe codec shutdown-to-active time. The datasheet only 3818c2ecf20Sopenharmony_ci * lists a nominal value however just use-it as-is without 3828c2ecf20Sopenharmony_ci * additional padding to minimize the delay introduced in 3838c2ecf20Sopenharmony_ci * starting to play audio (actually there is other setup done 3848c2ecf20Sopenharmony_ci * by the ASoC framework that will provide additional delays, 3858c2ecf20Sopenharmony_ci * so we should always be safe). 3868c2ecf20Sopenharmony_ci */ 3878c2ecf20Sopenharmony_ci msleep(25); 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci /* Turn on TAS5720 periodic fault checking/handling */ 3908c2ecf20Sopenharmony_ci tas5720->last_fault = 0; 3918c2ecf20Sopenharmony_ci schedule_delayed_work(&tas5720->fault_check_work, 3928c2ecf20Sopenharmony_ci msecs_to_jiffies(TAS5720_FAULT_CHECK_INTERVAL)); 3938c2ecf20Sopenharmony_ci } else if (event & SND_SOC_DAPM_PRE_PMD) { 3948c2ecf20Sopenharmony_ci /* Disable TAS5720 periodic fault checking/handling */ 3958c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&tas5720->fault_check_work); 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci /* Place TAS5720 in shutdown mode to minimize current draw */ 3988c2ecf20Sopenharmony_ci ret = snd_soc_component_update_bits(component, TAS5720_POWER_CTRL_REG, 3998c2ecf20Sopenharmony_ci TAS5720_SDZ, 0); 4008c2ecf20Sopenharmony_ci if (ret < 0) { 4018c2ecf20Sopenharmony_ci dev_err(component->dev, "error shutting down component: %d\n", 4028c2ecf20Sopenharmony_ci ret); 4038c2ecf20Sopenharmony_ci return ret; 4048c2ecf20Sopenharmony_ci } 4058c2ecf20Sopenharmony_ci } 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci return 0; 4088c2ecf20Sopenharmony_ci} 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 4118c2ecf20Sopenharmony_cistatic int tas5720_suspend(struct snd_soc_component *component) 4128c2ecf20Sopenharmony_ci{ 4138c2ecf20Sopenharmony_ci struct tas5720_data *tas5720 = snd_soc_component_get_drvdata(component); 4148c2ecf20Sopenharmony_ci int ret; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci regcache_cache_only(tas5720->regmap, true); 4178c2ecf20Sopenharmony_ci regcache_mark_dirty(tas5720->regmap); 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci ret = regulator_bulk_disable(ARRAY_SIZE(tas5720->supplies), 4208c2ecf20Sopenharmony_ci tas5720->supplies); 4218c2ecf20Sopenharmony_ci if (ret < 0) 4228c2ecf20Sopenharmony_ci dev_err(component->dev, "failed to disable supplies: %d\n", ret); 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci return ret; 4258c2ecf20Sopenharmony_ci} 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_cistatic int tas5720_resume(struct snd_soc_component *component) 4288c2ecf20Sopenharmony_ci{ 4298c2ecf20Sopenharmony_ci struct tas5720_data *tas5720 = snd_soc_component_get_drvdata(component); 4308c2ecf20Sopenharmony_ci int ret; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci ret = regulator_bulk_enable(ARRAY_SIZE(tas5720->supplies), 4338c2ecf20Sopenharmony_ci tas5720->supplies); 4348c2ecf20Sopenharmony_ci if (ret < 0) { 4358c2ecf20Sopenharmony_ci dev_err(component->dev, "failed to enable supplies: %d\n", ret); 4368c2ecf20Sopenharmony_ci return ret; 4378c2ecf20Sopenharmony_ci } 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci regcache_cache_only(tas5720->regmap, false); 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci ret = regcache_sync(tas5720->regmap); 4428c2ecf20Sopenharmony_ci if (ret < 0) { 4438c2ecf20Sopenharmony_ci dev_err(component->dev, "failed to sync regcache: %d\n", ret); 4448c2ecf20Sopenharmony_ci return ret; 4458c2ecf20Sopenharmony_ci } 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci return 0; 4488c2ecf20Sopenharmony_ci} 4498c2ecf20Sopenharmony_ci#else 4508c2ecf20Sopenharmony_ci#define tas5720_suspend NULL 4518c2ecf20Sopenharmony_ci#define tas5720_resume NULL 4528c2ecf20Sopenharmony_ci#endif 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_cistatic bool tas5720_is_volatile_reg(struct device *dev, unsigned int reg) 4558c2ecf20Sopenharmony_ci{ 4568c2ecf20Sopenharmony_ci switch (reg) { 4578c2ecf20Sopenharmony_ci case TAS5720_DEVICE_ID_REG: 4588c2ecf20Sopenharmony_ci case TAS5720_FAULT_REG: 4598c2ecf20Sopenharmony_ci return true; 4608c2ecf20Sopenharmony_ci default: 4618c2ecf20Sopenharmony_ci return false; 4628c2ecf20Sopenharmony_ci } 4638c2ecf20Sopenharmony_ci} 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_cistatic const struct regmap_config tas5720_regmap_config = { 4668c2ecf20Sopenharmony_ci .reg_bits = 8, 4678c2ecf20Sopenharmony_ci .val_bits = 8, 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci .max_register = TAS5720_MAX_REG, 4708c2ecf20Sopenharmony_ci .cache_type = REGCACHE_RBTREE, 4718c2ecf20Sopenharmony_ci .volatile_reg = tas5720_is_volatile_reg, 4728c2ecf20Sopenharmony_ci}; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_cistatic const struct regmap_config tas5722_regmap_config = { 4758c2ecf20Sopenharmony_ci .reg_bits = 8, 4768c2ecf20Sopenharmony_ci .val_bits = 8, 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci .max_register = TAS5722_MAX_REG, 4798c2ecf20Sopenharmony_ci .cache_type = REGCACHE_RBTREE, 4808c2ecf20Sopenharmony_ci .volatile_reg = tas5720_is_volatile_reg, 4818c2ecf20Sopenharmony_ci}; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci/* 4848c2ecf20Sopenharmony_ci * DAC analog gain. There are four discrete values to select from, ranging 4858c2ecf20Sopenharmony_ci * from 19.2 dB to 26.3dB. 4868c2ecf20Sopenharmony_ci */ 4878c2ecf20Sopenharmony_cistatic const DECLARE_TLV_DB_RANGE(dac_analog_tlv, 4888c2ecf20Sopenharmony_ci 0x0, 0x0, TLV_DB_SCALE_ITEM(1920, 0, 0), 4898c2ecf20Sopenharmony_ci 0x1, 0x1, TLV_DB_SCALE_ITEM(2070, 0, 0), 4908c2ecf20Sopenharmony_ci 0x2, 0x2, TLV_DB_SCALE_ITEM(2350, 0, 0), 4918c2ecf20Sopenharmony_ci 0x3, 0x3, TLV_DB_SCALE_ITEM(2630, 0, 0), 4928c2ecf20Sopenharmony_ci); 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci/* 4958c2ecf20Sopenharmony_ci * DAC digital volumes. From -103.5 to 24 dB in 0.5 dB or 0.25 dB steps 4968c2ecf20Sopenharmony_ci * depending on the device. Note that setting the gain below -100 dB 4978c2ecf20Sopenharmony_ci * (register value <0x7) is effectively a MUTE as per device datasheet. 4988c2ecf20Sopenharmony_ci * 4998c2ecf20Sopenharmony_ci * Note that for the TAS5722 the digital volume controls are actually split 5008c2ecf20Sopenharmony_ci * over two registers, so we need custom getters/setters for access. 5018c2ecf20Sopenharmony_ci */ 5028c2ecf20Sopenharmony_cistatic DECLARE_TLV_DB_SCALE(tas5720_dac_tlv, -10350, 50, 0); 5038c2ecf20Sopenharmony_cistatic DECLARE_TLV_DB_SCALE(tas5722_dac_tlv, -10350, 25, 0); 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_cistatic int tas5722_volume_get(struct snd_kcontrol *kcontrol, 5068c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 5078c2ecf20Sopenharmony_ci{ 5088c2ecf20Sopenharmony_ci struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); 5098c2ecf20Sopenharmony_ci unsigned int val; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci val = snd_soc_component_read(component, TAS5720_VOLUME_CTRL_REG); 5128c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = val << 1; 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci val = snd_soc_component_read(component, TAS5722_DIGITAL_CTRL2_REG); 5158c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] |= val & TAS5722_VOL_CONTROL_LSB; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci return 0; 5188c2ecf20Sopenharmony_ci} 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_cistatic int tas5722_volume_set(struct snd_kcontrol *kcontrol, 5218c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 5228c2ecf20Sopenharmony_ci{ 5238c2ecf20Sopenharmony_ci struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); 5248c2ecf20Sopenharmony_ci unsigned int sel = ucontrol->value.integer.value[0]; 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci snd_soc_component_write(component, TAS5720_VOLUME_CTRL_REG, sel >> 1); 5278c2ecf20Sopenharmony_ci snd_soc_component_update_bits(component, TAS5722_DIGITAL_CTRL2_REG, 5288c2ecf20Sopenharmony_ci TAS5722_VOL_CONTROL_LSB, sel); 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci return 0; 5318c2ecf20Sopenharmony_ci} 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new tas5720_snd_controls[] = { 5348c2ecf20Sopenharmony_ci SOC_SINGLE_TLV("Speaker Driver Playback Volume", 5358c2ecf20Sopenharmony_ci TAS5720_VOLUME_CTRL_REG, 0, 0xff, 0, tas5720_dac_tlv), 5368c2ecf20Sopenharmony_ci SOC_SINGLE_TLV("Speaker Driver Analog Gain", TAS5720_ANALOG_CTRL_REG, 5378c2ecf20Sopenharmony_ci TAS5720_ANALOG_GAIN_SHIFT, 3, 0, dac_analog_tlv), 5388c2ecf20Sopenharmony_ci}; 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new tas5722_snd_controls[] = { 5418c2ecf20Sopenharmony_ci SOC_SINGLE_EXT_TLV("Speaker Driver Playback Volume", 5428c2ecf20Sopenharmony_ci 0, 0, 511, 0, 5438c2ecf20Sopenharmony_ci tas5722_volume_get, tas5722_volume_set, 5448c2ecf20Sopenharmony_ci tas5722_dac_tlv), 5458c2ecf20Sopenharmony_ci SOC_SINGLE_TLV("Speaker Driver Analog Gain", TAS5720_ANALOG_CTRL_REG, 5468c2ecf20Sopenharmony_ci TAS5720_ANALOG_GAIN_SHIFT, 3, 0, dac_analog_tlv), 5478c2ecf20Sopenharmony_ci}; 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_cistatic const struct snd_soc_dapm_widget tas5720_dapm_widgets[] = { 5508c2ecf20Sopenharmony_ci SND_SOC_DAPM_AIF_IN("DAC IN", "Playback", 0, SND_SOC_NOPM, 0, 0), 5518c2ecf20Sopenharmony_ci SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0, tas5720_dac_event, 5528c2ecf20Sopenharmony_ci SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), 5538c2ecf20Sopenharmony_ci SND_SOC_DAPM_OUTPUT("OUT") 5548c2ecf20Sopenharmony_ci}; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_cistatic const struct snd_soc_dapm_route tas5720_audio_map[] = { 5578c2ecf20Sopenharmony_ci { "DAC", NULL, "DAC IN" }, 5588c2ecf20Sopenharmony_ci { "OUT", NULL, "DAC" }, 5598c2ecf20Sopenharmony_ci}; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_cistatic const struct snd_soc_component_driver soc_component_dev_tas5720 = { 5628c2ecf20Sopenharmony_ci .probe = tas5720_codec_probe, 5638c2ecf20Sopenharmony_ci .remove = tas5720_codec_remove, 5648c2ecf20Sopenharmony_ci .suspend = tas5720_suspend, 5658c2ecf20Sopenharmony_ci .resume = tas5720_resume, 5668c2ecf20Sopenharmony_ci .controls = tas5720_snd_controls, 5678c2ecf20Sopenharmony_ci .num_controls = ARRAY_SIZE(tas5720_snd_controls), 5688c2ecf20Sopenharmony_ci .dapm_widgets = tas5720_dapm_widgets, 5698c2ecf20Sopenharmony_ci .num_dapm_widgets = ARRAY_SIZE(tas5720_dapm_widgets), 5708c2ecf20Sopenharmony_ci .dapm_routes = tas5720_audio_map, 5718c2ecf20Sopenharmony_ci .num_dapm_routes = ARRAY_SIZE(tas5720_audio_map), 5728c2ecf20Sopenharmony_ci .idle_bias_on = 1, 5738c2ecf20Sopenharmony_ci .use_pmdown_time = 1, 5748c2ecf20Sopenharmony_ci .endianness = 1, 5758c2ecf20Sopenharmony_ci .non_legacy_dai_naming = 1, 5768c2ecf20Sopenharmony_ci}; 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_cistatic const struct snd_soc_component_driver soc_component_dev_tas5722 = { 5798c2ecf20Sopenharmony_ci .probe = tas5720_codec_probe, 5808c2ecf20Sopenharmony_ci .remove = tas5720_codec_remove, 5818c2ecf20Sopenharmony_ci .suspend = tas5720_suspend, 5828c2ecf20Sopenharmony_ci .resume = tas5720_resume, 5838c2ecf20Sopenharmony_ci .controls = tas5722_snd_controls, 5848c2ecf20Sopenharmony_ci .num_controls = ARRAY_SIZE(tas5722_snd_controls), 5858c2ecf20Sopenharmony_ci .dapm_widgets = tas5720_dapm_widgets, 5868c2ecf20Sopenharmony_ci .num_dapm_widgets = ARRAY_SIZE(tas5720_dapm_widgets), 5878c2ecf20Sopenharmony_ci .dapm_routes = tas5720_audio_map, 5888c2ecf20Sopenharmony_ci .num_dapm_routes = ARRAY_SIZE(tas5720_audio_map), 5898c2ecf20Sopenharmony_ci .idle_bias_on = 1, 5908c2ecf20Sopenharmony_ci .use_pmdown_time = 1, 5918c2ecf20Sopenharmony_ci .endianness = 1, 5928c2ecf20Sopenharmony_ci .non_legacy_dai_naming = 1, 5938c2ecf20Sopenharmony_ci}; 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci/* PCM rates supported by the TAS5720 driver */ 5968c2ecf20Sopenharmony_ci#define TAS5720_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\ 5978c2ecf20Sopenharmony_ci SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000) 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci/* Formats supported by TAS5720 driver */ 6008c2ecf20Sopenharmony_ci#define TAS5720_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE |\ 6018c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE) 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_cistatic const struct snd_soc_dai_ops tas5720_speaker_dai_ops = { 6048c2ecf20Sopenharmony_ci .hw_params = tas5720_hw_params, 6058c2ecf20Sopenharmony_ci .set_fmt = tas5720_set_dai_fmt, 6068c2ecf20Sopenharmony_ci .set_tdm_slot = tas5720_set_dai_tdm_slot, 6078c2ecf20Sopenharmony_ci .mute_stream = tas5720_mute, 6088c2ecf20Sopenharmony_ci .no_capture_mute = 1, 6098c2ecf20Sopenharmony_ci}; 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci/* 6128c2ecf20Sopenharmony_ci * TAS5720 DAI structure 6138c2ecf20Sopenharmony_ci * 6148c2ecf20Sopenharmony_ci * Note that were are advertising .playback.channels_max = 2 despite this being 6158c2ecf20Sopenharmony_ci * a mono amplifier. The reason for that is that some serial ports such as TI's 6168c2ecf20Sopenharmony_ci * McASP module have a minimum number of channels (2) that they can output. 6178c2ecf20Sopenharmony_ci * Advertising more channels than we have will allow us to interface with such 6188c2ecf20Sopenharmony_ci * a serial port without really any negative side effects as the TAS5720 will 6198c2ecf20Sopenharmony_ci * simply ignore any extra channel(s) asides from the one channel that is 6208c2ecf20Sopenharmony_ci * configured to be played back. 6218c2ecf20Sopenharmony_ci */ 6228c2ecf20Sopenharmony_cistatic struct snd_soc_dai_driver tas5720_dai[] = { 6238c2ecf20Sopenharmony_ci { 6248c2ecf20Sopenharmony_ci .name = "tas5720-amplifier", 6258c2ecf20Sopenharmony_ci .playback = { 6268c2ecf20Sopenharmony_ci .stream_name = "Playback", 6278c2ecf20Sopenharmony_ci .channels_min = 1, 6288c2ecf20Sopenharmony_ci .channels_max = 2, 6298c2ecf20Sopenharmony_ci .rates = TAS5720_RATES, 6308c2ecf20Sopenharmony_ci .formats = TAS5720_FORMATS, 6318c2ecf20Sopenharmony_ci }, 6328c2ecf20Sopenharmony_ci .ops = &tas5720_speaker_dai_ops, 6338c2ecf20Sopenharmony_ci }, 6348c2ecf20Sopenharmony_ci}; 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_cistatic int tas5720_probe(struct i2c_client *client, 6378c2ecf20Sopenharmony_ci const struct i2c_device_id *id) 6388c2ecf20Sopenharmony_ci{ 6398c2ecf20Sopenharmony_ci struct device *dev = &client->dev; 6408c2ecf20Sopenharmony_ci struct tas5720_data *data; 6418c2ecf20Sopenharmony_ci const struct regmap_config *regmap_config; 6428c2ecf20Sopenharmony_ci int ret; 6438c2ecf20Sopenharmony_ci int i; 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); 6468c2ecf20Sopenharmony_ci if (!data) 6478c2ecf20Sopenharmony_ci return -ENOMEM; 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci data->tas5720_client = client; 6508c2ecf20Sopenharmony_ci data->devtype = id->driver_data; 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci switch (id->driver_data) { 6538c2ecf20Sopenharmony_ci case TAS5720: 6548c2ecf20Sopenharmony_ci regmap_config = &tas5720_regmap_config; 6558c2ecf20Sopenharmony_ci break; 6568c2ecf20Sopenharmony_ci case TAS5722: 6578c2ecf20Sopenharmony_ci regmap_config = &tas5722_regmap_config; 6588c2ecf20Sopenharmony_ci break; 6598c2ecf20Sopenharmony_ci default: 6608c2ecf20Sopenharmony_ci dev_err(dev, "unexpected private driver data\n"); 6618c2ecf20Sopenharmony_ci return -EINVAL; 6628c2ecf20Sopenharmony_ci } 6638c2ecf20Sopenharmony_ci data->regmap = devm_regmap_init_i2c(client, regmap_config); 6648c2ecf20Sopenharmony_ci if (IS_ERR(data->regmap)) { 6658c2ecf20Sopenharmony_ci ret = PTR_ERR(data->regmap); 6668c2ecf20Sopenharmony_ci dev_err(dev, "failed to allocate register map: %d\n", ret); 6678c2ecf20Sopenharmony_ci return ret; 6688c2ecf20Sopenharmony_ci } 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(data->supplies); i++) 6718c2ecf20Sopenharmony_ci data->supplies[i].supply = tas5720_supply_names[i]; 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(data->supplies), 6748c2ecf20Sopenharmony_ci data->supplies); 6758c2ecf20Sopenharmony_ci if (ret != 0) { 6768c2ecf20Sopenharmony_ci dev_err(dev, "failed to request supplies: %d\n", ret); 6778c2ecf20Sopenharmony_ci return ret; 6788c2ecf20Sopenharmony_ci } 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci dev_set_drvdata(dev, data); 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci switch (id->driver_data) { 6838c2ecf20Sopenharmony_ci case TAS5720: 6848c2ecf20Sopenharmony_ci ret = devm_snd_soc_register_component(&client->dev, 6858c2ecf20Sopenharmony_ci &soc_component_dev_tas5720, 6868c2ecf20Sopenharmony_ci tas5720_dai, 6878c2ecf20Sopenharmony_ci ARRAY_SIZE(tas5720_dai)); 6888c2ecf20Sopenharmony_ci break; 6898c2ecf20Sopenharmony_ci case TAS5722: 6908c2ecf20Sopenharmony_ci ret = devm_snd_soc_register_component(&client->dev, 6918c2ecf20Sopenharmony_ci &soc_component_dev_tas5722, 6928c2ecf20Sopenharmony_ci tas5720_dai, 6938c2ecf20Sopenharmony_ci ARRAY_SIZE(tas5720_dai)); 6948c2ecf20Sopenharmony_ci break; 6958c2ecf20Sopenharmony_ci default: 6968c2ecf20Sopenharmony_ci dev_err(dev, "unexpected private driver data\n"); 6978c2ecf20Sopenharmony_ci return -EINVAL; 6988c2ecf20Sopenharmony_ci } 6998c2ecf20Sopenharmony_ci if (ret < 0) { 7008c2ecf20Sopenharmony_ci dev_err(dev, "failed to register component: %d\n", ret); 7018c2ecf20Sopenharmony_ci return ret; 7028c2ecf20Sopenharmony_ci } 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci return 0; 7058c2ecf20Sopenharmony_ci} 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_cistatic const struct i2c_device_id tas5720_id[] = { 7088c2ecf20Sopenharmony_ci { "tas5720", TAS5720 }, 7098c2ecf20Sopenharmony_ci { "tas5722", TAS5722 }, 7108c2ecf20Sopenharmony_ci { } 7118c2ecf20Sopenharmony_ci}; 7128c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, tas5720_id); 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_OF) 7158c2ecf20Sopenharmony_cistatic const struct of_device_id tas5720_of_match[] = { 7168c2ecf20Sopenharmony_ci { .compatible = "ti,tas5720", }, 7178c2ecf20Sopenharmony_ci { .compatible = "ti,tas5722", }, 7188c2ecf20Sopenharmony_ci { }, 7198c2ecf20Sopenharmony_ci}; 7208c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, tas5720_of_match); 7218c2ecf20Sopenharmony_ci#endif 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_cistatic struct i2c_driver tas5720_i2c_driver = { 7248c2ecf20Sopenharmony_ci .driver = { 7258c2ecf20Sopenharmony_ci .name = "tas5720", 7268c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(tas5720_of_match), 7278c2ecf20Sopenharmony_ci }, 7288c2ecf20Sopenharmony_ci .probe = tas5720_probe, 7298c2ecf20Sopenharmony_ci .id_table = tas5720_id, 7308c2ecf20Sopenharmony_ci}; 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_cimodule_i2c_driver(tas5720_i2c_driver); 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ciMODULE_AUTHOR("Andreas Dannenberg <dannenberg@ti.com>"); 7358c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("TAS5720 Audio amplifier driver"); 7368c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 737