832 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * tas5720.c - ALSA SoC Texas Instruments TAS5720 Mono Audio Amplifier4 *5 * Copyright (C)2015-2016 Texas Instruments Incorporated - https://www.ti.com6 *7 * Author: Andreas Dannenberg <dannenberg@ti.com>8 */9 10#include <linux/module.h>11#include <linux/errno.h>12#include <linux/device.h>13#include <linux/i2c.h>14#include <linux/regmap.h>15#include <linux/slab.h>16#include <linux/regulator/consumer.h>17#include <linux/delay.h>18 19#include <sound/pcm.h>20#include <sound/pcm_params.h>21#include <sound/soc.h>22#include <sound/soc-dapm.h>23#include <sound/tlv.h>24 25#include "tas5720.h"26 27/* Define how often to check (and clear) the fault status register (in ms) */28#define TAS5720_FAULT_CHECK_INTERVAL 20029 30enum tas572x_type {31 TAS5720,32 TAS5720A_Q1,33 TAS5722,34};35 36static const char * const tas5720_supply_names[] = {37 "dvdd", /* Digital power supply. Connect to 3.3-V supply. */38 "pvdd", /* Class-D amp and analog power supply (connected). */39};40 41#define TAS5720_NUM_SUPPLIES ARRAY_SIZE(tas5720_supply_names)42 43struct tas5720_data {44 struct snd_soc_component *component;45 struct regmap *regmap;46 struct i2c_client *tas5720_client;47 enum tas572x_type devtype;48 struct regulator_bulk_data supplies[TAS5720_NUM_SUPPLIES];49 struct delayed_work fault_check_work;50 unsigned int last_fault;51};52 53static int tas5720_hw_params(struct snd_pcm_substream *substream,54 struct snd_pcm_hw_params *params,55 struct snd_soc_dai *dai)56{57 struct snd_soc_component *component = dai->component;58 unsigned int rate = params_rate(params);59 bool ssz_ds;60 int ret;61 62 switch (rate) {63 case 44100:64 case 48000:65 ssz_ds = false;66 break;67 case 88200:68 case 96000:69 ssz_ds = true;70 break;71 default:72 dev_err(component->dev, "unsupported sample rate: %u\n", rate);73 return -EINVAL;74 }75 76 ret = snd_soc_component_update_bits(component, TAS5720_DIGITAL_CTRL1_REG,77 TAS5720_SSZ_DS, ssz_ds);78 if (ret < 0) {79 dev_err(component->dev, "error setting sample rate: %d\n", ret);80 return ret;81 }82 83 return 0;84}85 86static int tas5720_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)87{88 struct snd_soc_component *component = dai->component;89 u8 serial_format;90 int ret;91 92 if ((fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) != SND_SOC_DAIFMT_CBC_CFC) {93 dev_vdbg(component->dev, "DAI clocking invalid\n");94 return -EINVAL;95 }96 97 switch (fmt & (SND_SOC_DAIFMT_FORMAT_MASK |98 SND_SOC_DAIFMT_INV_MASK)) {99 case (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF):100 /* 1st data bit occur one BCLK cycle after the frame sync */101 serial_format = TAS5720_SAIF_I2S;102 break;103 case (SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_NB_NF):104 /*105 * Note that although the TAS5720 does not have a dedicated DSP106 * mode it doesn't care about the LRCLK duty cycle during TDM107 * operation. Therefore we can use the device's I2S mode with108 * its delaying of the 1st data bit to receive DSP_A formatted109 * data. See device datasheet for additional details.110 */111 serial_format = TAS5720_SAIF_I2S;112 break;113 case (SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_NB_NF):114 /*115 * Similar to DSP_A, we can use the fact that the TAS5720 does116 * not care about the LRCLK duty cycle during TDM to receive117 * DSP_B formatted data in LEFTJ mode (no delaying of the 1st118 * data bit).119 */120 serial_format = TAS5720_SAIF_LEFTJ;121 break;122 case (SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF):123 /* No delay after the frame sync */124 serial_format = TAS5720_SAIF_LEFTJ;125 break;126 default:127 dev_vdbg(component->dev, "DAI Format is not found\n");128 return -EINVAL;129 }130 131 ret = snd_soc_component_update_bits(component, TAS5720_DIGITAL_CTRL1_REG,132 TAS5720_SAIF_FORMAT_MASK,133 serial_format);134 if (ret < 0) {135 dev_err(component->dev, "error setting SAIF format: %d\n", ret);136 return ret;137 }138 139 return 0;140}141 142static int tas5720_set_dai_tdm_slot(struct snd_soc_dai *dai,143 unsigned int tx_mask, unsigned int rx_mask,144 int slots, int slot_width)145{146 struct snd_soc_component *component = dai->component;147 struct tas5720_data *tas5720 = snd_soc_component_get_drvdata(component);148 unsigned int first_slot;149 int ret;150 151 if (!tx_mask) {152 dev_err(component->dev, "tx masks must not be 0\n");153 return -EINVAL;154 }155 156 /*157 * Determine the first slot that is being requested. We will only158 * use the first slot that is found since the TAS5720 is a mono159 * amplifier.160 */161 first_slot = __ffs(tx_mask);162 163 if (first_slot > 7) {164 dev_err(component->dev, "slot selection out of bounds (%u)\n",165 first_slot);166 return -EINVAL;167 }168 169 /*170 * Enable manual TDM slot selection (instead of I2C ID based).171 * This is not applicable to TAS5720A-Q1.172 */173 switch (tas5720->devtype) {174 case TAS5720A_Q1:175 break;176 default:177 ret = snd_soc_component_update_bits(component, TAS5720_DIGITAL_CTRL1_REG,178 TAS5720_TDM_CFG_SRC, TAS5720_TDM_CFG_SRC);179 if (ret < 0)180 goto error_snd_soc_component_update_bits;181 182 /* Configure the TDM slot to process audio from */183 ret = snd_soc_component_update_bits(component, TAS5720_DIGITAL_CTRL2_REG,184 TAS5720_TDM_SLOT_SEL_MASK, first_slot);185 if (ret < 0)186 goto error_snd_soc_component_update_bits;187 break;188 }189 190 /* Configure TDM slot width. This is only applicable to TAS5722. */191 switch (tas5720->devtype) {192 case TAS5722:193 ret = snd_soc_component_update_bits(component, TAS5722_DIGITAL_CTRL2_REG,194 TAS5722_TDM_SLOT_16B,195 slot_width == 16 ?196 TAS5722_TDM_SLOT_16B : 0);197 if (ret < 0)198 goto error_snd_soc_component_update_bits;199 break;200 default:201 break;202 }203 204 return 0;205 206error_snd_soc_component_update_bits:207 dev_err(component->dev, "error configuring TDM mode: %d\n", ret);208 return ret;209}210 211static int tas5720_mute_soc_component(struct snd_soc_component *component, int mute)212{213 struct tas5720_data *tas5720 = snd_soc_component_get_drvdata(component);214 unsigned int reg, mask;215 int ret;216 217 switch (tas5720->devtype) {218 case TAS5720A_Q1:219 reg = TAS5720_Q1_VOLUME_CTRL_CFG_REG;220 mask = TAS5720_Q1_MUTE;221 break;222 default:223 reg = TAS5720_DIGITAL_CTRL2_REG;224 mask = TAS5720_MUTE;225 break;226 }227 228 ret = snd_soc_component_update_bits(component, reg, mask, mute ? mask : 0);229 if (ret < 0) {230 dev_err(component->dev, "error (un-)muting device: %d\n", ret);231 return ret;232 }233 234 return 0;235}236 237static int tas5720_mute(struct snd_soc_dai *dai, int mute, int direction)238{239 return tas5720_mute_soc_component(dai->component, mute);240}241 242static void tas5720_fault_check_work(struct work_struct *work)243{244 struct tas5720_data *tas5720 = container_of(work, struct tas5720_data,245 fault_check_work.work);246 struct device *dev = tas5720->component->dev;247 unsigned int curr_fault;248 int ret;249 250 ret = regmap_read(tas5720->regmap, TAS5720_FAULT_REG, &curr_fault);251 if (ret < 0) {252 dev_err(dev, "failed to read FAULT register: %d\n", ret);253 goto out;254 }255 256 /* Check/handle all errors except SAIF clock errors */257 curr_fault &= TAS5720_OCE | TAS5720_DCE | TAS5720_OTE;258 259 /*260 * Only flag errors once for a given occurrence. This is needed as261 * the TAS5720 will take time clearing the fault condition internally262 * during which we don't want to bombard the system with the same263 * error message over and over.264 */265 if ((curr_fault & TAS5720_OCE) && !(tas5720->last_fault & TAS5720_OCE))266 dev_crit(dev, "experienced an over current hardware fault\n");267 268 if ((curr_fault & TAS5720_DCE) && !(tas5720->last_fault & TAS5720_DCE))269 dev_crit(dev, "experienced a DC detection fault\n");270 271 if ((curr_fault & TAS5720_OTE) && !(tas5720->last_fault & TAS5720_OTE))272 dev_crit(dev, "experienced an over temperature fault\n");273 274 /* Store current fault value so we can detect any changes next time */275 tas5720->last_fault = curr_fault;276 277 if (!curr_fault)278 goto out;279 280 /*281 * Periodically toggle SDZ (shutdown bit) H->L->H to clear any latching282 * faults as long as a fault condition persists. Always going through283 * the full sequence no matter the first return value to minimizes284 * chances for the device to end up in shutdown mode.285 */286 ret = regmap_write_bits(tas5720->regmap, TAS5720_POWER_CTRL_REG,287 TAS5720_SDZ, 0);288 if (ret < 0)289 dev_err(dev, "failed to write POWER_CTRL register: %d\n", ret);290 291 ret = regmap_write_bits(tas5720->regmap, TAS5720_POWER_CTRL_REG,292 TAS5720_SDZ, TAS5720_SDZ);293 if (ret < 0)294 dev_err(dev, "failed to write POWER_CTRL register: %d\n", ret);295 296out:297 /* Schedule the next fault check at the specified interval */298 schedule_delayed_work(&tas5720->fault_check_work,299 msecs_to_jiffies(TAS5720_FAULT_CHECK_INTERVAL));300}301 302static int tas5720_codec_probe(struct snd_soc_component *component)303{304 struct tas5720_data *tas5720 = snd_soc_component_get_drvdata(component);305 unsigned int device_id, expected_device_id;306 int ret;307 308 tas5720->component = component;309 310 ret = regulator_bulk_enable(ARRAY_SIZE(tas5720->supplies),311 tas5720->supplies);312 if (ret != 0) {313 dev_err(component->dev, "failed to enable supplies: %d\n", ret);314 return ret;315 }316 317 /*318 * Take a liberal approach to checking the device ID to allow the319 * driver to be used even if the device ID does not match, however320 * issue a warning if there is a mismatch.321 */322 ret = regmap_read(tas5720->regmap, TAS5720_DEVICE_ID_REG, &device_id);323 if (ret < 0) {324 dev_err(component->dev, "failed to read device ID register: %d\n",325 ret);326 goto probe_fail;327 }328 329 switch (tas5720->devtype) {330 case TAS5720:331 expected_device_id = TAS5720_DEVICE_ID;332 break;333 case TAS5720A_Q1:334 expected_device_id = TAS5720A_Q1_DEVICE_ID;335 break;336 case TAS5722:337 expected_device_id = TAS5722_DEVICE_ID;338 break;339 default:340 dev_err(component->dev, "unexpected private driver data\n");341 ret = -EINVAL;342 goto probe_fail;343 }344 345 if (device_id != expected_device_id)346 dev_warn(component->dev, "wrong device ID. expected: %u read: %u\n",347 expected_device_id, device_id);348 349 /* Set device to mute */350 ret = tas5720_mute_soc_component(component, 1);351 if (ret < 0)352 goto error_snd_soc_component_update_bits;353 354 /* Set Bit 7 in TAS5720_ANALOG_CTRL_REG to 1 for TAS5720A_Q1 */355 switch (tas5720->devtype) {356 case TAS5720A_Q1:357 ret = snd_soc_component_update_bits(component, TAS5720_ANALOG_CTRL_REG,358 TAS5720_Q1_RESERVED7_BIT,359 TAS5720_Q1_RESERVED7_BIT);360 break;361 default:362 break;363 }364 if (ret < 0)365 goto error_snd_soc_component_update_bits;366 367 /*368 * Enter shutdown mode - our default when not playing audio - to369 * minimize current consumption. On the TAS5720 there is no real down370 * side doing so as all device registers are preserved and the wakeup371 * of the codec is rather quick which we do using a dapm widget.372 */373 ret = snd_soc_component_update_bits(component, TAS5720_POWER_CTRL_REG,374 TAS5720_SDZ, 0);375 if (ret < 0)376 goto error_snd_soc_component_update_bits;377 378 INIT_DELAYED_WORK(&tas5720->fault_check_work, tas5720_fault_check_work);379 380 return 0;381 382error_snd_soc_component_update_bits:383 dev_err(component->dev, "error configuring device registers: %d\n", ret);384 385probe_fail:386 regulator_bulk_disable(ARRAY_SIZE(tas5720->supplies),387 tas5720->supplies);388 return ret;389}390 391static void tas5720_codec_remove(struct snd_soc_component *component)392{393 struct tas5720_data *tas5720 = snd_soc_component_get_drvdata(component);394 int ret;395 396 cancel_delayed_work_sync(&tas5720->fault_check_work);397 398 ret = regulator_bulk_disable(ARRAY_SIZE(tas5720->supplies),399 tas5720->supplies);400 if (ret < 0)401 dev_err(component->dev, "failed to disable supplies: %d\n", ret);402};403 404static int tas5720_dac_event(struct snd_soc_dapm_widget *w,405 struct snd_kcontrol *kcontrol, int event)406{407 struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);408 struct tas5720_data *tas5720 = snd_soc_component_get_drvdata(component);409 int ret;410 411 if (event & SND_SOC_DAPM_POST_PMU) {412 /* Take TAS5720 out of shutdown mode */413 ret = snd_soc_component_update_bits(component, TAS5720_POWER_CTRL_REG,414 TAS5720_SDZ, TAS5720_SDZ);415 if (ret < 0) {416 dev_err(component->dev, "error waking component: %d\n", ret);417 return ret;418 }419 420 /*421 * Observe codec shutdown-to-active time. The datasheet only422 * lists a nominal value however just use-it as-is without423 * additional padding to minimize the delay introduced in424 * starting to play audio (actually there is other setup done425 * by the ASoC framework that will provide additional delays,426 * so we should always be safe).427 */428 msleep(25);429 430 /* Turn on TAS5720 periodic fault checking/handling */431 tas5720->last_fault = 0;432 schedule_delayed_work(&tas5720->fault_check_work,433 msecs_to_jiffies(TAS5720_FAULT_CHECK_INTERVAL));434 } else if (event & SND_SOC_DAPM_PRE_PMD) {435 /* Disable TAS5720 periodic fault checking/handling */436 cancel_delayed_work_sync(&tas5720->fault_check_work);437 438 /* Place TAS5720 in shutdown mode to minimize current draw */439 ret = snd_soc_component_update_bits(component, TAS5720_POWER_CTRL_REG,440 TAS5720_SDZ, 0);441 if (ret < 0) {442 dev_err(component->dev, "error shutting down component: %d\n",443 ret);444 return ret;445 }446 }447 448 return 0;449}450 451#ifdef CONFIG_PM452static int tas5720_suspend(struct snd_soc_component *component)453{454 struct tas5720_data *tas5720 = snd_soc_component_get_drvdata(component);455 int ret;456 457 regcache_cache_only(tas5720->regmap, true);458 regcache_mark_dirty(tas5720->regmap);459 460 ret = regulator_bulk_disable(ARRAY_SIZE(tas5720->supplies),461 tas5720->supplies);462 if (ret < 0)463 dev_err(component->dev, "failed to disable supplies: %d\n", ret);464 465 return ret;466}467 468static int tas5720_resume(struct snd_soc_component *component)469{470 struct tas5720_data *tas5720 = snd_soc_component_get_drvdata(component);471 int ret;472 473 ret = regulator_bulk_enable(ARRAY_SIZE(tas5720->supplies),474 tas5720->supplies);475 if (ret < 0) {476 dev_err(component->dev, "failed to enable supplies: %d\n", ret);477 return ret;478 }479 480 regcache_cache_only(tas5720->regmap, false);481 482 ret = regcache_sync(tas5720->regmap);483 if (ret < 0) {484 dev_err(component->dev, "failed to sync regcache: %d\n", ret);485 return ret;486 }487 488 return 0;489}490#else491#define tas5720_suspend NULL492#define tas5720_resume NULL493#endif494 495static bool tas5720_is_volatile_reg(struct device *dev, unsigned int reg)496{497 switch (reg) {498 case TAS5720_DEVICE_ID_REG:499 case TAS5720_FAULT_REG:500 return true;501 default:502 return false;503 }504}505 506static const struct regmap_config tas5720_regmap_config = {507 .reg_bits = 8,508 .val_bits = 8,509 510 .max_register = TAS5720_MAX_REG,511 .cache_type = REGCACHE_RBTREE,512 .volatile_reg = tas5720_is_volatile_reg,513};514 515static const struct regmap_config tas5720a_q1_regmap_config = {516 .reg_bits = 8,517 .val_bits = 8,518 519 .max_register = TAS5720_MAX_REG,520 .cache_type = REGCACHE_RBTREE,521 .volatile_reg = tas5720_is_volatile_reg,522};523 524static const struct regmap_config tas5722_regmap_config = {525 .reg_bits = 8,526 .val_bits = 8,527 528 .max_register = TAS5722_MAX_REG,529 .cache_type = REGCACHE_RBTREE,530 .volatile_reg = tas5720_is_volatile_reg,531};532 533/*534 * DAC analog gain. There are four discrete values to select from, ranging535 * from 19.2 dB to 26.3dB.536 */537static const DECLARE_TLV_DB_RANGE(dac_analog_tlv,538 0x0, 0x0, TLV_DB_SCALE_ITEM(1920, 0, 0),539 0x1, 0x1, TLV_DB_SCALE_ITEM(2070, 0, 0),540 0x2, 0x2, TLV_DB_SCALE_ITEM(2350, 0, 0),541 0x3, 0x3, TLV_DB_SCALE_ITEM(2630, 0, 0),542);543 544/*545 * DAC analog gain for TAS5720A-Q1. There are three discrete values to select from, ranging546 * from 19.2 dB to 25.0dB.547 */548static const DECLARE_TLV_DB_RANGE(dac_analog_tlv_a_q1,549 0x0, 0x0, TLV_DB_SCALE_ITEM(1920, 0, 0),550 0x1, 0x1, TLV_DB_SCALE_ITEM(2260, 0, 0),551 0x2, 0x2, TLV_DB_SCALE_ITEM(2500, 0, 0),552);553 554/*555 * DAC digital volumes. From -103.5 to 24 dB in 0.5 dB or 0.25 dB steps556 * depending on the device. Note that setting the gain below -100 dB557 * (register value <0x7) is effectively a MUTE as per device datasheet.558 *559 * Note that for the TAS5722 the digital volume controls are actually split560 * over two registers, so we need custom getters/setters for access.561 */562static DECLARE_TLV_DB_SCALE(tas5720_dac_tlv, -10350, 50, 0);563static DECLARE_TLV_DB_SCALE(tas5722_dac_tlv, -10350, 25, 0);564 565static int tas5722_volume_get(struct snd_kcontrol *kcontrol,566 struct snd_ctl_elem_value *ucontrol)567{568 struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);569 unsigned int val;570 571 val = snd_soc_component_read(component, TAS5720_VOLUME_CTRL_REG);572 ucontrol->value.integer.value[0] = val << 1;573 574 val = snd_soc_component_read(component, TAS5722_DIGITAL_CTRL2_REG);575 ucontrol->value.integer.value[0] |= val & TAS5722_VOL_CONTROL_LSB;576 577 return 0;578}579 580static int tas5722_volume_set(struct snd_kcontrol *kcontrol,581 struct snd_ctl_elem_value *ucontrol)582{583 struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);584 unsigned int sel = ucontrol->value.integer.value[0];585 586 snd_soc_component_write(component, TAS5720_VOLUME_CTRL_REG, sel >> 1);587 snd_soc_component_update_bits(component, TAS5722_DIGITAL_CTRL2_REG,588 TAS5722_VOL_CONTROL_LSB, sel);589 590 return 0;591}592 593static const struct snd_kcontrol_new tas5720_snd_controls[] = {594 SOC_SINGLE_TLV("Speaker Driver Playback Volume",595 TAS5720_VOLUME_CTRL_REG, 0, 0xff, 0, tas5720_dac_tlv),596 SOC_SINGLE_TLV("Speaker Driver Analog Gain", TAS5720_ANALOG_CTRL_REG,597 TAS5720_ANALOG_GAIN_SHIFT, 3, 0, dac_analog_tlv),598};599 600static const struct snd_kcontrol_new tas5720a_q1_snd_controls[] = {601 SOC_DOUBLE_R_TLV("Speaker Driver Playback Volume",602 TAS5720_Q1_VOLUME_CTRL_LEFT_REG,603 TAS5720_Q1_VOLUME_CTRL_RIGHT_REG,604 0, 0xff, 0, tas5720_dac_tlv),605 SOC_SINGLE_TLV("Speaker Driver Analog Gain", TAS5720_ANALOG_CTRL_REG,606 TAS5720_ANALOG_GAIN_SHIFT, 3, 0, dac_analog_tlv_a_q1),607};608 609static const struct snd_kcontrol_new tas5722_snd_controls[] = {610 SOC_SINGLE_EXT_TLV("Speaker Driver Playback Volume",611 0, 0, 511, 0,612 tas5722_volume_get, tas5722_volume_set,613 tas5722_dac_tlv),614 SOC_SINGLE_TLV("Speaker Driver Analog Gain", TAS5720_ANALOG_CTRL_REG,615 TAS5720_ANALOG_GAIN_SHIFT, 3, 0, dac_analog_tlv),616};617 618static const struct snd_soc_dapm_widget tas5720_dapm_widgets[] = {619 SND_SOC_DAPM_AIF_IN("DAC IN", "Playback", 0, SND_SOC_NOPM, 0, 0),620 SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0, tas5720_dac_event,621 SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),622 SND_SOC_DAPM_OUTPUT("OUT")623};624 625static const struct snd_soc_dapm_route tas5720_audio_map[] = {626 { "DAC", NULL, "DAC IN" },627 { "OUT", NULL, "DAC" },628};629 630static const struct snd_soc_component_driver soc_component_dev_tas5720 = {631 .probe = tas5720_codec_probe,632 .remove = tas5720_codec_remove,633 .suspend = tas5720_suspend,634 .resume = tas5720_resume,635 .controls = tas5720_snd_controls,636 .num_controls = ARRAY_SIZE(tas5720_snd_controls),637 .dapm_widgets = tas5720_dapm_widgets,638 .num_dapm_widgets = ARRAY_SIZE(tas5720_dapm_widgets),639 .dapm_routes = tas5720_audio_map,640 .num_dapm_routes = ARRAY_SIZE(tas5720_audio_map),641 .idle_bias_on = 1,642 .use_pmdown_time = 1,643 .endianness = 1,644};645 646static const struct snd_soc_component_driver soc_component_dev_tas5720_a_q1 = {647 .probe = tas5720_codec_probe,648 .remove = tas5720_codec_remove,649 .suspend = tas5720_suspend,650 .resume = tas5720_resume,651 .controls = tas5720a_q1_snd_controls,652 .num_controls = ARRAY_SIZE(tas5720a_q1_snd_controls),653 .dapm_widgets = tas5720_dapm_widgets,654 .num_dapm_widgets = ARRAY_SIZE(tas5720_dapm_widgets),655 .dapm_routes = tas5720_audio_map,656 .num_dapm_routes = ARRAY_SIZE(tas5720_audio_map),657 .idle_bias_on = 1,658 .use_pmdown_time = 1,659 .endianness = 1,660};661 662static const struct snd_soc_component_driver soc_component_dev_tas5722 = {663 .probe = tas5720_codec_probe,664 .remove = tas5720_codec_remove,665 .suspend = tas5720_suspend,666 .resume = tas5720_resume,667 .controls = tas5722_snd_controls,668 .num_controls = ARRAY_SIZE(tas5722_snd_controls),669 .dapm_widgets = tas5720_dapm_widgets,670 .num_dapm_widgets = ARRAY_SIZE(tas5720_dapm_widgets),671 .dapm_routes = tas5720_audio_map,672 .num_dapm_routes = ARRAY_SIZE(tas5720_audio_map),673 .idle_bias_on = 1,674 .use_pmdown_time = 1,675 .endianness = 1,676};677 678/* PCM rates supported by the TAS5720 driver */679#define TAS5720_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\680 SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)681 682/* Formats supported by TAS5720 driver */683#define TAS5720_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE |\684 SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE)685 686static const struct snd_soc_dai_ops tas5720_speaker_dai_ops = {687 .hw_params = tas5720_hw_params,688 .set_fmt = tas5720_set_dai_fmt,689 .set_tdm_slot = tas5720_set_dai_tdm_slot,690 .mute_stream = tas5720_mute,691 .no_capture_mute = 1,692};693 694/*695 * TAS5720 DAI structure696 *697 * Note that were are advertising .playback.channels_max = 2 despite this being698 * a mono amplifier. The reason for that is that some serial ports such as TI's699 * McASP module have a minimum number of channels (2) that they can output.700 * Advertising more channels than we have will allow us to interface with such701 * a serial port without really any negative side effects as the TAS5720 will702 * simply ignore any extra channel(s) asides from the one channel that is703 * configured to be played back.704 */705static struct snd_soc_dai_driver tas5720_dai[] = {706 {707 .name = "tas5720-amplifier",708 .playback = {709 .stream_name = "Playback",710 .channels_min = 1,711 .channels_max = 2,712 .rates = TAS5720_RATES,713 .formats = TAS5720_FORMATS,714 },715 .ops = &tas5720_speaker_dai_ops,716 },717};718 719static const struct i2c_device_id tas5720_id[] = {720 { "tas5720", TAS5720 },721 { "tas5720a-q1", TAS5720A_Q1 },722 { "tas5722", TAS5722 },723 { }724};725MODULE_DEVICE_TABLE(i2c, tas5720_id);726 727static int tas5720_probe(struct i2c_client *client)728{729 struct device *dev = &client->dev;730 struct tas5720_data *data;731 const struct regmap_config *regmap_config;732 const struct i2c_device_id *id;733 int ret;734 int i;735 736 data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);737 if (!data)738 return -ENOMEM;739 740 id = i2c_match_id(tas5720_id, client);741 data->tas5720_client = client;742 data->devtype = id->driver_data;743 744 switch (id->driver_data) {745 case TAS5720:746 regmap_config = &tas5720_regmap_config;747 break;748 case TAS5720A_Q1:749 regmap_config = &tas5720a_q1_regmap_config;750 break;751 case TAS5722:752 regmap_config = &tas5722_regmap_config;753 break;754 default:755 dev_err(dev, "unexpected private driver data\n");756 return -EINVAL;757 }758 data->regmap = devm_regmap_init_i2c(client, regmap_config);759 if (IS_ERR(data->regmap)) {760 ret = PTR_ERR(data->regmap);761 dev_err(dev, "failed to allocate register map: %d\n", ret);762 return ret;763 }764 765 for (i = 0; i < ARRAY_SIZE(data->supplies); i++)766 data->supplies[i].supply = tas5720_supply_names[i];767 768 ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(data->supplies),769 data->supplies);770 if (ret != 0) {771 dev_err(dev, "failed to request supplies: %d\n", ret);772 return ret;773 }774 775 dev_set_drvdata(dev, data);776 777 switch (id->driver_data) {778 case TAS5720:779 ret = devm_snd_soc_register_component(&client->dev,780 &soc_component_dev_tas5720,781 tas5720_dai,782 ARRAY_SIZE(tas5720_dai));783 break;784 case TAS5720A_Q1:785 ret = devm_snd_soc_register_component(&client->dev,786 &soc_component_dev_tas5720_a_q1,787 tas5720_dai,788 ARRAY_SIZE(tas5720_dai));789 break;790 case TAS5722:791 ret = devm_snd_soc_register_component(&client->dev,792 &soc_component_dev_tas5722,793 tas5720_dai,794 ARRAY_SIZE(tas5720_dai));795 break;796 default:797 dev_err(dev, "unexpected private driver data\n");798 return -EINVAL;799 }800 if (ret < 0) {801 dev_err(dev, "failed to register component: %d\n", ret);802 return ret;803 }804 805 return 0;806}807 808#if IS_ENABLED(CONFIG_OF)809static const struct of_device_id tas5720_of_match[] = {810 { .compatible = "ti,tas5720", },811 { .compatible = "ti,tas5720a-q1", },812 { .compatible = "ti,tas5722", },813 { },814};815MODULE_DEVICE_TABLE(of, tas5720_of_match);816#endif817 818static struct i2c_driver tas5720_i2c_driver = {819 .driver = {820 .name = "tas5720",821 .of_match_table = of_match_ptr(tas5720_of_match),822 },823 .probe = tas5720_probe,824 .id_table = tas5720_id,825};826 827module_i2c_driver(tas5720_i2c_driver);828 829MODULE_AUTHOR("Andreas Dannenberg <dannenberg@ti.com>");830MODULE_DESCRIPTION("TAS5720 Audio amplifier driver");831MODULE_LICENSE("GPL");832