13fd6e7d9SKevin Cernekee /* 23fd6e7d9SKevin Cernekee * TAS571x amplifier audio driver 33fd6e7d9SKevin Cernekee * 43fd6e7d9SKevin Cernekee * Copyright (C) 2015 Google, Inc. 53fd6e7d9SKevin Cernekee * Copyright (c) 2013 Daniel Mack <zonque@gmail.com> 63fd6e7d9SKevin Cernekee * 723a282c4SPetr Kulhavy * TAS5721 support: 823a282c4SPetr Kulhavy * Copyright (C) 2016 Petr Kulhavy, Barix AG <petr@barix.com> 923a282c4SPetr Kulhavy * 103fd6e7d9SKevin Cernekee * This program is free software; you can redistribute it and/or modify 113fd6e7d9SKevin Cernekee * it under the terms of the GNU General Public License as published by 123fd6e7d9SKevin Cernekee * the Free Software Foundation; either version 2 of the License, or 133fd6e7d9SKevin Cernekee * (at your option) any later version. 143fd6e7d9SKevin Cernekee */ 153fd6e7d9SKevin Cernekee 163fd6e7d9SKevin Cernekee #include <linux/clk.h> 173fd6e7d9SKevin Cernekee #include <linux/delay.h> 183fd6e7d9SKevin Cernekee #include <linux/device.h> 193fd6e7d9SKevin Cernekee #include <linux/gpio/consumer.h> 203fd6e7d9SKevin Cernekee #include <linux/i2c.h> 213fd6e7d9SKevin Cernekee #include <linux/init.h> 223fd6e7d9SKevin Cernekee #include <linux/kernel.h> 233fd6e7d9SKevin Cernekee #include <linux/module.h> 243fd6e7d9SKevin Cernekee #include <linux/of_device.h> 253fd6e7d9SKevin Cernekee #include <linux/regmap.h> 263fd6e7d9SKevin Cernekee #include <linux/regulator/consumer.h> 273fd6e7d9SKevin Cernekee #include <linux/stddef.h> 283fd6e7d9SKevin Cernekee #include <sound/pcm_params.h> 293fd6e7d9SKevin Cernekee #include <sound/soc.h> 303fd6e7d9SKevin Cernekee #include <sound/tlv.h> 314b9e385bSPetr Kulhavy #include <asm/unaligned.h> 323fd6e7d9SKevin Cernekee 333fd6e7d9SKevin Cernekee #include "tas571x.h" 343fd6e7d9SKevin Cernekee 353fd6e7d9SKevin Cernekee #define TAS571X_MAX_SUPPLIES 6 363fd6e7d9SKevin Cernekee 373fd6e7d9SKevin Cernekee struct tas571x_chip { 383fd6e7d9SKevin Cernekee const char *const *supply_names; 393fd6e7d9SKevin Cernekee int num_supply_names; 403fd6e7d9SKevin Cernekee const struct snd_kcontrol_new *controls; 413fd6e7d9SKevin Cernekee int num_controls; 423fd6e7d9SKevin Cernekee const struct regmap_config *regmap_config; 433fd6e7d9SKevin Cernekee int vol_reg_size; 443fd6e7d9SKevin Cernekee }; 453fd6e7d9SKevin Cernekee 463fd6e7d9SKevin Cernekee struct tas571x_private { 473fd6e7d9SKevin Cernekee const struct tas571x_chip *chip; 483fd6e7d9SKevin Cernekee struct regmap *regmap; 493fd6e7d9SKevin Cernekee struct regulator_bulk_data supplies[TAS571X_MAX_SUPPLIES]; 503fd6e7d9SKevin Cernekee struct clk *mclk; 513fd6e7d9SKevin Cernekee unsigned int format; 523fd6e7d9SKevin Cernekee struct gpio_desc *reset_gpio; 533fd6e7d9SKevin Cernekee struct gpio_desc *pdn_gpio; 543fd6e7d9SKevin Cernekee struct snd_soc_codec_driver codec_driver; 553fd6e7d9SKevin Cernekee }; 563fd6e7d9SKevin Cernekee 573fd6e7d9SKevin Cernekee static int tas571x_register_size(struct tas571x_private *priv, unsigned int reg) 583fd6e7d9SKevin Cernekee { 593fd6e7d9SKevin Cernekee switch (reg) { 603fd6e7d9SKevin Cernekee case TAS571X_MVOL_REG: 613fd6e7d9SKevin Cernekee case TAS571X_CH1_VOL_REG: 623fd6e7d9SKevin Cernekee case TAS571X_CH2_VOL_REG: 633fd6e7d9SKevin Cernekee return priv->chip->vol_reg_size; 64a593ed09SPetr Kulhavy case TAS571X_INPUT_MUX_REG: 65a593ed09SPetr Kulhavy case TAS571X_CH4_SRC_SELECT_REG: 66a593ed09SPetr Kulhavy case TAS571X_PWM_MUX_REG: 67a593ed09SPetr Kulhavy return 4; 683fd6e7d9SKevin Cernekee default: 693fd6e7d9SKevin Cernekee return 1; 703fd6e7d9SKevin Cernekee } 713fd6e7d9SKevin Cernekee } 723fd6e7d9SKevin Cernekee 733fd6e7d9SKevin Cernekee static int tas571x_reg_write(void *context, unsigned int reg, 743fd6e7d9SKevin Cernekee unsigned int value) 753fd6e7d9SKevin Cernekee { 763fd6e7d9SKevin Cernekee struct i2c_client *client = context; 773fd6e7d9SKevin Cernekee struct tas571x_private *priv = i2c_get_clientdata(client); 783fd6e7d9SKevin Cernekee unsigned int i, size; 793fd6e7d9SKevin Cernekee uint8_t buf[5]; 803fd6e7d9SKevin Cernekee int ret; 813fd6e7d9SKevin Cernekee 823fd6e7d9SKevin Cernekee size = tas571x_register_size(priv, reg); 833fd6e7d9SKevin Cernekee buf[0] = reg; 843fd6e7d9SKevin Cernekee 853fd6e7d9SKevin Cernekee for (i = size; i >= 1; --i) { 863fd6e7d9SKevin Cernekee buf[i] = value; 873fd6e7d9SKevin Cernekee value >>= 8; 883fd6e7d9SKevin Cernekee } 893fd6e7d9SKevin Cernekee 903fd6e7d9SKevin Cernekee ret = i2c_master_send(client, buf, size + 1); 913fd6e7d9SKevin Cernekee if (ret == size + 1) 923fd6e7d9SKevin Cernekee return 0; 933fd6e7d9SKevin Cernekee else if (ret < 0) 943fd6e7d9SKevin Cernekee return ret; 953fd6e7d9SKevin Cernekee else 963fd6e7d9SKevin Cernekee return -EIO; 973fd6e7d9SKevin Cernekee } 983fd6e7d9SKevin Cernekee 993fd6e7d9SKevin Cernekee static int tas571x_reg_read(void *context, unsigned int reg, 1003fd6e7d9SKevin Cernekee unsigned int *value) 1013fd6e7d9SKevin Cernekee { 1023fd6e7d9SKevin Cernekee struct i2c_client *client = context; 1033fd6e7d9SKevin Cernekee struct tas571x_private *priv = i2c_get_clientdata(client); 1043fd6e7d9SKevin Cernekee uint8_t send_buf, recv_buf[4]; 1053fd6e7d9SKevin Cernekee struct i2c_msg msgs[2]; 1063fd6e7d9SKevin Cernekee unsigned int size; 1073fd6e7d9SKevin Cernekee unsigned int i; 1083fd6e7d9SKevin Cernekee int ret; 1093fd6e7d9SKevin Cernekee 1103fd6e7d9SKevin Cernekee size = tas571x_register_size(priv, reg); 1113fd6e7d9SKevin Cernekee send_buf = reg; 1123fd6e7d9SKevin Cernekee 1133fd6e7d9SKevin Cernekee msgs[0].addr = client->addr; 1143fd6e7d9SKevin Cernekee msgs[0].len = sizeof(send_buf); 1153fd6e7d9SKevin Cernekee msgs[0].buf = &send_buf; 1163fd6e7d9SKevin Cernekee msgs[0].flags = 0; 1173fd6e7d9SKevin Cernekee 1183fd6e7d9SKevin Cernekee msgs[1].addr = client->addr; 1193fd6e7d9SKevin Cernekee msgs[1].len = size; 1203fd6e7d9SKevin Cernekee msgs[1].buf = recv_buf; 1213fd6e7d9SKevin Cernekee msgs[1].flags = I2C_M_RD; 1223fd6e7d9SKevin Cernekee 1233fd6e7d9SKevin Cernekee ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); 1243fd6e7d9SKevin Cernekee if (ret < 0) 1253fd6e7d9SKevin Cernekee return ret; 1263fd6e7d9SKevin Cernekee else if (ret != ARRAY_SIZE(msgs)) 1273fd6e7d9SKevin Cernekee return -EIO; 1283fd6e7d9SKevin Cernekee 1293fd6e7d9SKevin Cernekee *value = 0; 1303fd6e7d9SKevin Cernekee 1313fd6e7d9SKevin Cernekee for (i = 0; i < size; i++) { 1323fd6e7d9SKevin Cernekee *value <<= 8; 1333fd6e7d9SKevin Cernekee *value |= recv_buf[i]; 1343fd6e7d9SKevin Cernekee } 1353fd6e7d9SKevin Cernekee 1363fd6e7d9SKevin Cernekee return 0; 1373fd6e7d9SKevin Cernekee } 1383fd6e7d9SKevin Cernekee 1394b9e385bSPetr Kulhavy /* 1404b9e385bSPetr Kulhavy * register write for 8- and 20-byte registers 1414b9e385bSPetr Kulhavy */ 1424b9e385bSPetr Kulhavy static int tas571x_reg_write_multiword(struct i2c_client *client, 1434b9e385bSPetr Kulhavy unsigned int reg, const long values[], size_t len) 1444b9e385bSPetr Kulhavy { 1454b9e385bSPetr Kulhavy size_t i; 1464b9e385bSPetr Kulhavy uint8_t *buf, *p; 1474b9e385bSPetr Kulhavy int ret; 1484b9e385bSPetr Kulhavy size_t send_size = 1 + len * sizeof(uint32_t); 1494b9e385bSPetr Kulhavy 1504b9e385bSPetr Kulhavy buf = kzalloc(send_size, GFP_KERNEL | GFP_DMA); 1514b9e385bSPetr Kulhavy if (!buf) 1524b9e385bSPetr Kulhavy return -ENOMEM; 1534b9e385bSPetr Kulhavy buf[0] = reg; 1544b9e385bSPetr Kulhavy 1554b9e385bSPetr Kulhavy for (i = 0, p = buf + 1; i < len; i++, p += sizeof(uint32_t)) 1564b9e385bSPetr Kulhavy put_unaligned_be32(values[i], p); 1574b9e385bSPetr Kulhavy 1584b9e385bSPetr Kulhavy ret = i2c_master_send(client, buf, send_size); 1594b9e385bSPetr Kulhavy 1604b9e385bSPetr Kulhavy kfree(buf); 1614b9e385bSPetr Kulhavy 1624b9e385bSPetr Kulhavy if (ret == send_size) 1634b9e385bSPetr Kulhavy return 0; 1644b9e385bSPetr Kulhavy else if (ret < 0) 1654b9e385bSPetr Kulhavy return ret; 1664b9e385bSPetr Kulhavy else 1674b9e385bSPetr Kulhavy return -EIO; 1684b9e385bSPetr Kulhavy } 1694b9e385bSPetr Kulhavy 1704b9e385bSPetr Kulhavy /* 1714b9e385bSPetr Kulhavy * register read for 8- and 20-byte registers 1724b9e385bSPetr Kulhavy */ 1734b9e385bSPetr Kulhavy static int tas571x_reg_read_multiword(struct i2c_client *client, 1744b9e385bSPetr Kulhavy unsigned int reg, long values[], size_t len) 1754b9e385bSPetr Kulhavy { 1764b9e385bSPetr Kulhavy unsigned int i; 1774b9e385bSPetr Kulhavy uint8_t send_buf; 1784b9e385bSPetr Kulhavy uint8_t *recv_buf, *p; 1794b9e385bSPetr Kulhavy struct i2c_msg msgs[2]; 1804b9e385bSPetr Kulhavy unsigned int recv_size = len * sizeof(uint32_t); 1814b9e385bSPetr Kulhavy int ret; 1824b9e385bSPetr Kulhavy 1834b9e385bSPetr Kulhavy recv_buf = kzalloc(recv_size, GFP_KERNEL | GFP_DMA); 1844b9e385bSPetr Kulhavy if (!recv_buf) 1854b9e385bSPetr Kulhavy return -ENOMEM; 1864b9e385bSPetr Kulhavy 1874b9e385bSPetr Kulhavy send_buf = reg; 1884b9e385bSPetr Kulhavy 1894b9e385bSPetr Kulhavy msgs[0].addr = client->addr; 1904b9e385bSPetr Kulhavy msgs[0].len = sizeof(send_buf); 1914b9e385bSPetr Kulhavy msgs[0].buf = &send_buf; 1924b9e385bSPetr Kulhavy msgs[0].flags = 0; 1934b9e385bSPetr Kulhavy 1944b9e385bSPetr Kulhavy msgs[1].addr = client->addr; 1954b9e385bSPetr Kulhavy msgs[1].len = recv_size; 1964b9e385bSPetr Kulhavy msgs[1].buf = recv_buf; 1974b9e385bSPetr Kulhavy msgs[1].flags = I2C_M_RD; 1984b9e385bSPetr Kulhavy 1994b9e385bSPetr Kulhavy ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); 2004b9e385bSPetr Kulhavy if (ret < 0) 2014b9e385bSPetr Kulhavy goto err_ret; 2024b9e385bSPetr Kulhavy else if (ret != ARRAY_SIZE(msgs)) { 2034b9e385bSPetr Kulhavy ret = -EIO; 2044b9e385bSPetr Kulhavy goto err_ret; 2054b9e385bSPetr Kulhavy } 2064b9e385bSPetr Kulhavy 2074b9e385bSPetr Kulhavy for (i = 0, p = recv_buf; i < len; i++, p += sizeof(uint32_t)) 2084b9e385bSPetr Kulhavy values[i] = get_unaligned_be32(p); 2094b9e385bSPetr Kulhavy 2104b9e385bSPetr Kulhavy err_ret: 2114b9e385bSPetr Kulhavy kfree(recv_buf); 2124b9e385bSPetr Kulhavy return ret; 2134b9e385bSPetr Kulhavy } 2144b9e385bSPetr Kulhavy 2154b9e385bSPetr Kulhavy /* 2164b9e385bSPetr Kulhavy * Integer array controls for setting biquad, mixer, DRC coefficients. 2174b9e385bSPetr Kulhavy * According to the datasheet each coefficient is effectively 26bits, 2184b9e385bSPetr Kulhavy * i.e. stored as 32bits, where bits [31:26] are ignored. 2194b9e385bSPetr Kulhavy * TI's TAS57xx Graphical Development Environment tool however produces 2204b9e385bSPetr Kulhavy * coefficients with more than 26 bits. For this reason we allow values 2214b9e385bSPetr Kulhavy * in the full 32-bits reange. 2224b9e385bSPetr Kulhavy * The coefficients are ordered as given in the TAS571x data sheet: 2234b9e385bSPetr Kulhavy * b0, b1, b2, a1, a2 2244b9e385bSPetr Kulhavy */ 2254b9e385bSPetr Kulhavy 2264b9e385bSPetr Kulhavy static int tas571x_coefficient_info(struct snd_kcontrol *kcontrol, 2274b9e385bSPetr Kulhavy struct snd_ctl_elem_info *uinfo) 2284b9e385bSPetr Kulhavy { 2294b9e385bSPetr Kulhavy int numcoef = kcontrol->private_value >> 16; 2304b9e385bSPetr Kulhavy 2314b9e385bSPetr Kulhavy uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 2324b9e385bSPetr Kulhavy uinfo->count = numcoef; 2334b9e385bSPetr Kulhavy uinfo->value.integer.min = 0; 2344b9e385bSPetr Kulhavy uinfo->value.integer.max = 0xffffffff; 2354b9e385bSPetr Kulhavy return 0; 2364b9e385bSPetr Kulhavy } 2374b9e385bSPetr Kulhavy 2384b9e385bSPetr Kulhavy static int tas571x_coefficient_get(struct snd_kcontrol *kcontrol, 2394b9e385bSPetr Kulhavy struct snd_ctl_elem_value *ucontrol) 2404b9e385bSPetr Kulhavy { 2414b9e385bSPetr Kulhavy struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); 2424b9e385bSPetr Kulhavy struct i2c_client *i2c = to_i2c_client(codec->dev); 2434b9e385bSPetr Kulhavy int numcoef = kcontrol->private_value >> 16; 2444b9e385bSPetr Kulhavy int index = kcontrol->private_value & 0xffff; 2454b9e385bSPetr Kulhavy 2464b9e385bSPetr Kulhavy return tas571x_reg_read_multiword(i2c, index, 2474b9e385bSPetr Kulhavy ucontrol->value.integer.value, numcoef); 2484b9e385bSPetr Kulhavy } 2494b9e385bSPetr Kulhavy 2504b9e385bSPetr Kulhavy static int tas571x_coefficient_put(struct snd_kcontrol *kcontrol, 2514b9e385bSPetr Kulhavy struct snd_ctl_elem_value *ucontrol) 2524b9e385bSPetr Kulhavy { 2534b9e385bSPetr Kulhavy struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); 2544b9e385bSPetr Kulhavy struct i2c_client *i2c = to_i2c_client(codec->dev); 2554b9e385bSPetr Kulhavy int numcoef = kcontrol->private_value >> 16; 2564b9e385bSPetr Kulhavy int index = kcontrol->private_value & 0xffff; 2574b9e385bSPetr Kulhavy 2584b9e385bSPetr Kulhavy return tas571x_reg_write_multiword(i2c, index, 2594b9e385bSPetr Kulhavy ucontrol->value.integer.value, numcoef); 2604b9e385bSPetr Kulhavy } 2614b9e385bSPetr Kulhavy 2623fd6e7d9SKevin Cernekee static int tas571x_set_dai_fmt(struct snd_soc_dai *dai, unsigned int format) 2633fd6e7d9SKevin Cernekee { 2643fd6e7d9SKevin Cernekee struct tas571x_private *priv = snd_soc_codec_get_drvdata(dai->codec); 2653fd6e7d9SKevin Cernekee 2663fd6e7d9SKevin Cernekee priv->format = format; 2673fd6e7d9SKevin Cernekee 2683fd6e7d9SKevin Cernekee return 0; 2693fd6e7d9SKevin Cernekee } 2703fd6e7d9SKevin Cernekee 2713fd6e7d9SKevin Cernekee static int tas571x_hw_params(struct snd_pcm_substream *substream, 2723fd6e7d9SKevin Cernekee struct snd_pcm_hw_params *params, 2733fd6e7d9SKevin Cernekee struct snd_soc_dai *dai) 2743fd6e7d9SKevin Cernekee { 2753fd6e7d9SKevin Cernekee struct tas571x_private *priv = snd_soc_codec_get_drvdata(dai->codec); 2763fd6e7d9SKevin Cernekee u32 val; 2773fd6e7d9SKevin Cernekee 2783fd6e7d9SKevin Cernekee switch (priv->format & SND_SOC_DAIFMT_FORMAT_MASK) { 2793fd6e7d9SKevin Cernekee case SND_SOC_DAIFMT_RIGHT_J: 2803fd6e7d9SKevin Cernekee val = 0x00; 2813fd6e7d9SKevin Cernekee break; 2823fd6e7d9SKevin Cernekee case SND_SOC_DAIFMT_I2S: 2833fd6e7d9SKevin Cernekee val = 0x03; 2843fd6e7d9SKevin Cernekee break; 2853fd6e7d9SKevin Cernekee case SND_SOC_DAIFMT_LEFT_J: 2863fd6e7d9SKevin Cernekee val = 0x06; 2873fd6e7d9SKevin Cernekee break; 2883fd6e7d9SKevin Cernekee default: 2893fd6e7d9SKevin Cernekee return -EINVAL; 2903fd6e7d9SKevin Cernekee } 2913fd6e7d9SKevin Cernekee 2923fd6e7d9SKevin Cernekee if (params_width(params) >= 24) 2933fd6e7d9SKevin Cernekee val += 2; 2943fd6e7d9SKevin Cernekee else if (params_width(params) >= 20) 2953fd6e7d9SKevin Cernekee val += 1; 2963fd6e7d9SKevin Cernekee 2973fd6e7d9SKevin Cernekee return regmap_update_bits(priv->regmap, TAS571X_SDI_REG, 2983fd6e7d9SKevin Cernekee TAS571X_SDI_FMT_MASK, val); 2993fd6e7d9SKevin Cernekee } 3003fd6e7d9SKevin Cernekee 30104004850SPetr Kulhavy static int tas571x_mute(struct snd_soc_dai *dai, int mute) 30204004850SPetr Kulhavy { 30304004850SPetr Kulhavy struct snd_soc_codec *codec = dai->codec; 30404004850SPetr Kulhavy u8 sysctl2; 30504004850SPetr Kulhavy int ret; 30604004850SPetr Kulhavy 30704004850SPetr Kulhavy sysctl2 = mute ? TAS571X_SYS_CTRL_2_SDN_MASK : 0; 30804004850SPetr Kulhavy 30904004850SPetr Kulhavy ret = snd_soc_update_bits(codec, 31004004850SPetr Kulhavy TAS571X_SYS_CTRL_2_REG, 31104004850SPetr Kulhavy TAS571X_SYS_CTRL_2_SDN_MASK, 31204004850SPetr Kulhavy sysctl2); 31304004850SPetr Kulhavy usleep_range(1000, 2000); 31404004850SPetr Kulhavy 31504004850SPetr Kulhavy return ret; 31604004850SPetr Kulhavy } 31704004850SPetr Kulhavy 3183fd6e7d9SKevin Cernekee static int tas571x_set_bias_level(struct snd_soc_codec *codec, 3193fd6e7d9SKevin Cernekee enum snd_soc_bias_level level) 3203fd6e7d9SKevin Cernekee { 3213fd6e7d9SKevin Cernekee struct tas571x_private *priv = snd_soc_codec_get_drvdata(codec); 3223fd6e7d9SKevin Cernekee int ret; 3233fd6e7d9SKevin Cernekee 3243fd6e7d9SKevin Cernekee switch (level) { 3253fd6e7d9SKevin Cernekee case SND_SOC_BIAS_ON: 3263fd6e7d9SKevin Cernekee break; 3273fd6e7d9SKevin Cernekee case SND_SOC_BIAS_PREPARE: 3283fd6e7d9SKevin Cernekee break; 3293fd6e7d9SKevin Cernekee case SND_SOC_BIAS_STANDBY: 3308f218fa9SLars-Peter Clausen if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { 3313fd6e7d9SKevin Cernekee if (!IS_ERR(priv->mclk)) { 3323fd6e7d9SKevin Cernekee ret = clk_prepare_enable(priv->mclk); 3333fd6e7d9SKevin Cernekee if (ret) { 3343fd6e7d9SKevin Cernekee dev_err(codec->dev, 3353fd6e7d9SKevin Cernekee "Failed to enable master clock: %d\n", 3363fd6e7d9SKevin Cernekee ret); 3373fd6e7d9SKevin Cernekee return ret; 3383fd6e7d9SKevin Cernekee } 3393fd6e7d9SKevin Cernekee } 3403fd6e7d9SKevin Cernekee 3413fd6e7d9SKevin Cernekee gpiod_set_value(priv->pdn_gpio, 0); 3423fd6e7d9SKevin Cernekee usleep_range(5000, 6000); 3433fd6e7d9SKevin Cernekee 3443fd6e7d9SKevin Cernekee regcache_cache_only(priv->regmap, false); 3453fd6e7d9SKevin Cernekee ret = regcache_sync(priv->regmap); 3463fd6e7d9SKevin Cernekee if (ret) 3473fd6e7d9SKevin Cernekee return ret; 3483fd6e7d9SKevin Cernekee } 3493fd6e7d9SKevin Cernekee break; 3503fd6e7d9SKevin Cernekee case SND_SOC_BIAS_OFF: 3513fd6e7d9SKevin Cernekee regcache_cache_only(priv->regmap, true); 3523fd6e7d9SKevin Cernekee gpiod_set_value(priv->pdn_gpio, 1); 3533fd6e7d9SKevin Cernekee 3543fd6e7d9SKevin Cernekee if (!IS_ERR(priv->mclk)) 3553fd6e7d9SKevin Cernekee clk_disable_unprepare(priv->mclk); 3563fd6e7d9SKevin Cernekee break; 3573fd6e7d9SKevin Cernekee } 3583fd6e7d9SKevin Cernekee 3593fd6e7d9SKevin Cernekee return 0; 3603fd6e7d9SKevin Cernekee } 3613fd6e7d9SKevin Cernekee 3623fd6e7d9SKevin Cernekee static const struct snd_soc_dai_ops tas571x_dai_ops = { 3633fd6e7d9SKevin Cernekee .set_fmt = tas571x_set_dai_fmt, 3643fd6e7d9SKevin Cernekee .hw_params = tas571x_hw_params, 36504004850SPetr Kulhavy .digital_mute = tas571x_mute, 3663fd6e7d9SKevin Cernekee }; 3673fd6e7d9SKevin Cernekee 3684b9e385bSPetr Kulhavy 3694b9e385bSPetr Kulhavy #define BIQUAD_COEFS(xname, reg) \ 3704b9e385bSPetr Kulhavy { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ 3714b9e385bSPetr Kulhavy .info = tas571x_coefficient_info, \ 3724b9e385bSPetr Kulhavy .get = tas571x_coefficient_get,\ 3734b9e385bSPetr Kulhavy .put = tas571x_coefficient_put, \ 3744b9e385bSPetr Kulhavy .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \ 3754b9e385bSPetr Kulhavy .private_value = reg | (5 << 16) } 3764b9e385bSPetr Kulhavy 3773fd6e7d9SKevin Cernekee static const char *const tas5711_supply_names[] = { 3783fd6e7d9SKevin Cernekee "AVDD", 3793fd6e7d9SKevin Cernekee "DVDD", 3803fd6e7d9SKevin Cernekee "PVDD_A", 3813fd6e7d9SKevin Cernekee "PVDD_B", 3823fd6e7d9SKevin Cernekee "PVDD_C", 3833fd6e7d9SKevin Cernekee "PVDD_D", 3843fd6e7d9SKevin Cernekee }; 3853fd6e7d9SKevin Cernekee 3863fd6e7d9SKevin Cernekee static const DECLARE_TLV_DB_SCALE(tas5711_volume_tlv, -10350, 50, 1); 3873fd6e7d9SKevin Cernekee 3883fd6e7d9SKevin Cernekee static const struct snd_kcontrol_new tas5711_controls[] = { 3893fd6e7d9SKevin Cernekee SOC_SINGLE_TLV("Master Volume", 3903fd6e7d9SKevin Cernekee TAS571X_MVOL_REG, 3913fd6e7d9SKevin Cernekee 0, 0xff, 1, tas5711_volume_tlv), 3923fd6e7d9SKevin Cernekee SOC_DOUBLE_R_TLV("Speaker Volume", 3933fd6e7d9SKevin Cernekee TAS571X_CH1_VOL_REG, 3943fd6e7d9SKevin Cernekee TAS571X_CH2_VOL_REG, 3953fd6e7d9SKevin Cernekee 0, 0xff, 1, tas5711_volume_tlv), 3963fd6e7d9SKevin Cernekee SOC_DOUBLE("Speaker Switch", 3973fd6e7d9SKevin Cernekee TAS571X_SOFT_MUTE_REG, 3983fd6e7d9SKevin Cernekee TAS571X_SOFT_MUTE_CH1_SHIFT, TAS571X_SOFT_MUTE_CH2_SHIFT, 3993fd6e7d9SKevin Cernekee 1, 1), 4003fd6e7d9SKevin Cernekee }; 4013fd6e7d9SKevin Cernekee 402a593ed09SPetr Kulhavy static const struct regmap_range tas571x_readonly_regs_range[] = { 403a593ed09SPetr Kulhavy regmap_reg_range(TAS571X_CLK_CTRL_REG, TAS571X_DEV_ID_REG), 404a593ed09SPetr Kulhavy }; 405a593ed09SPetr Kulhavy 406a593ed09SPetr Kulhavy static const struct regmap_range tas571x_volatile_regs_range[] = { 407a593ed09SPetr Kulhavy regmap_reg_range(TAS571X_CLK_CTRL_REG, TAS571X_ERR_STATUS_REG), 408a593ed09SPetr Kulhavy regmap_reg_range(TAS571X_OSC_TRIM_REG, TAS571X_OSC_TRIM_REG), 409a593ed09SPetr Kulhavy }; 410a593ed09SPetr Kulhavy 411a593ed09SPetr Kulhavy static const struct regmap_access_table tas571x_write_regs = { 412a593ed09SPetr Kulhavy .no_ranges = tas571x_readonly_regs_range, 413a593ed09SPetr Kulhavy .n_no_ranges = ARRAY_SIZE(tas571x_readonly_regs_range), 414a593ed09SPetr Kulhavy }; 415a593ed09SPetr Kulhavy 416a593ed09SPetr Kulhavy static const struct regmap_access_table tas571x_volatile_regs = { 417a593ed09SPetr Kulhavy .yes_ranges = tas571x_volatile_regs_range, 418a593ed09SPetr Kulhavy .n_yes_ranges = ARRAY_SIZE(tas571x_volatile_regs_range), 419a593ed09SPetr Kulhavy 420a593ed09SPetr Kulhavy }; 421a593ed09SPetr Kulhavy 4223fd6e7d9SKevin Cernekee static const struct reg_default tas5711_reg_defaults[] = { 4233fd6e7d9SKevin Cernekee { 0x04, 0x05 }, 4243fd6e7d9SKevin Cernekee { 0x05, 0x40 }, 4253fd6e7d9SKevin Cernekee { 0x06, 0x00 }, 4263fd6e7d9SKevin Cernekee { 0x07, 0xff }, 4273fd6e7d9SKevin Cernekee { 0x08, 0x30 }, 4283fd6e7d9SKevin Cernekee { 0x09, 0x30 }, 4293fd6e7d9SKevin Cernekee { 0x1b, 0x82 }, 4303fd6e7d9SKevin Cernekee }; 4313fd6e7d9SKevin Cernekee 4323fd6e7d9SKevin Cernekee static const struct regmap_config tas5711_regmap_config = { 4333fd6e7d9SKevin Cernekee .reg_bits = 8, 4343fd6e7d9SKevin Cernekee .val_bits = 32, 4353fd6e7d9SKevin Cernekee .max_register = 0xff, 4363fd6e7d9SKevin Cernekee .reg_read = tas571x_reg_read, 4373fd6e7d9SKevin Cernekee .reg_write = tas571x_reg_write, 4383fd6e7d9SKevin Cernekee .reg_defaults = tas5711_reg_defaults, 4393fd6e7d9SKevin Cernekee .num_reg_defaults = ARRAY_SIZE(tas5711_reg_defaults), 4403fd6e7d9SKevin Cernekee .cache_type = REGCACHE_RBTREE, 441a593ed09SPetr Kulhavy .wr_table = &tas571x_write_regs, 442a593ed09SPetr Kulhavy .volatile_table = &tas571x_volatile_regs, 4433fd6e7d9SKevin Cernekee }; 4443fd6e7d9SKevin Cernekee 4453fd6e7d9SKevin Cernekee static const struct tas571x_chip tas5711_chip = { 4463fd6e7d9SKevin Cernekee .supply_names = tas5711_supply_names, 4473fd6e7d9SKevin Cernekee .num_supply_names = ARRAY_SIZE(tas5711_supply_names), 4483fd6e7d9SKevin Cernekee .controls = tas5711_controls, 4493fd6e7d9SKevin Cernekee .num_controls = ARRAY_SIZE(tas5711_controls), 4503fd6e7d9SKevin Cernekee .regmap_config = &tas5711_regmap_config, 4513fd6e7d9SKevin Cernekee .vol_reg_size = 1, 4523fd6e7d9SKevin Cernekee }; 4533fd6e7d9SKevin Cernekee 4543fd6e7d9SKevin Cernekee static const char *const tas5717_supply_names[] = { 4553fd6e7d9SKevin Cernekee "AVDD", 4563fd6e7d9SKevin Cernekee "DVDD", 4573fd6e7d9SKevin Cernekee "HPVDD", 4583fd6e7d9SKevin Cernekee "PVDD_AB", 4593fd6e7d9SKevin Cernekee "PVDD_CD", 4603fd6e7d9SKevin Cernekee }; 4613fd6e7d9SKevin Cernekee 4623fd6e7d9SKevin Cernekee static const DECLARE_TLV_DB_SCALE(tas5717_volume_tlv, -10375, 25, 0); 4633fd6e7d9SKevin Cernekee 4643fd6e7d9SKevin Cernekee static const struct snd_kcontrol_new tas5717_controls[] = { 4653fd6e7d9SKevin Cernekee /* MVOL LSB is ignored - see comments in tas571x_i2c_probe() */ 4663fd6e7d9SKevin Cernekee SOC_SINGLE_TLV("Master Volume", 4673fd6e7d9SKevin Cernekee TAS571X_MVOL_REG, 1, 0x1ff, 1, 4683fd6e7d9SKevin Cernekee tas5717_volume_tlv), 4693fd6e7d9SKevin Cernekee SOC_DOUBLE_R_TLV("Speaker Volume", 4703fd6e7d9SKevin Cernekee TAS571X_CH1_VOL_REG, TAS571X_CH2_VOL_REG, 4713fd6e7d9SKevin Cernekee 1, 0x1ff, 1, tas5717_volume_tlv), 4723fd6e7d9SKevin Cernekee SOC_DOUBLE("Speaker Switch", 4733fd6e7d9SKevin Cernekee TAS571X_SOFT_MUTE_REG, 4743fd6e7d9SKevin Cernekee TAS571X_SOFT_MUTE_CH1_SHIFT, TAS571X_SOFT_MUTE_CH2_SHIFT, 4753fd6e7d9SKevin Cernekee 1, 1), 4764b9e385bSPetr Kulhavy 4774b9e385bSPetr Kulhavy /* 4784b9e385bSPetr Kulhavy * The biquads are named according to the register names. 4794b9e385bSPetr Kulhavy * Please note that TI's TAS57xx Graphical Development Environment 4804b9e385bSPetr Kulhavy * tool names them different. 4814b9e385bSPetr Kulhavy */ 4824b9e385bSPetr Kulhavy BIQUAD_COEFS("CH1 - Biquad 0", TAS5717_CH1_BQ0_REG), 4834b9e385bSPetr Kulhavy BIQUAD_COEFS("CH1 - Biquad 1", TAS5717_CH1_BQ1_REG), 4844b9e385bSPetr Kulhavy BIQUAD_COEFS("CH1 - Biquad 2", TAS5717_CH1_BQ2_REG), 4854b9e385bSPetr Kulhavy BIQUAD_COEFS("CH1 - Biquad 3", TAS5717_CH1_BQ3_REG), 4864b9e385bSPetr Kulhavy BIQUAD_COEFS("CH1 - Biquad 4", TAS5717_CH1_BQ4_REG), 4874b9e385bSPetr Kulhavy BIQUAD_COEFS("CH1 - Biquad 5", TAS5717_CH1_BQ5_REG), 4884b9e385bSPetr Kulhavy BIQUAD_COEFS("CH1 - Biquad 6", TAS5717_CH1_BQ6_REG), 4894b9e385bSPetr Kulhavy BIQUAD_COEFS("CH1 - Biquad 7", TAS5717_CH1_BQ7_REG), 4904b9e385bSPetr Kulhavy BIQUAD_COEFS("CH1 - Biquad 8", TAS5717_CH1_BQ8_REG), 4914b9e385bSPetr Kulhavy BIQUAD_COEFS("CH1 - Biquad 9", TAS5717_CH1_BQ9_REG), 4924b9e385bSPetr Kulhavy BIQUAD_COEFS("CH1 - Biquad 10", TAS5717_CH1_BQ10_REG), 4934b9e385bSPetr Kulhavy BIQUAD_COEFS("CH1 - Biquad 11", TAS5717_CH1_BQ11_REG), 4944b9e385bSPetr Kulhavy 4954b9e385bSPetr Kulhavy BIQUAD_COEFS("CH2 - Biquad 0", TAS5717_CH2_BQ0_REG), 4964b9e385bSPetr Kulhavy BIQUAD_COEFS("CH2 - Biquad 1", TAS5717_CH2_BQ1_REG), 4974b9e385bSPetr Kulhavy BIQUAD_COEFS("CH2 - Biquad 2", TAS5717_CH2_BQ2_REG), 4984b9e385bSPetr Kulhavy BIQUAD_COEFS("CH2 - Biquad 3", TAS5717_CH2_BQ3_REG), 4994b9e385bSPetr Kulhavy BIQUAD_COEFS("CH2 - Biquad 4", TAS5717_CH2_BQ4_REG), 5004b9e385bSPetr Kulhavy BIQUAD_COEFS("CH2 - Biquad 5", TAS5717_CH2_BQ5_REG), 5014b9e385bSPetr Kulhavy BIQUAD_COEFS("CH2 - Biquad 6", TAS5717_CH2_BQ6_REG), 5024b9e385bSPetr Kulhavy BIQUAD_COEFS("CH2 - Biquad 7", TAS5717_CH2_BQ7_REG), 5034b9e385bSPetr Kulhavy BIQUAD_COEFS("CH2 - Biquad 8", TAS5717_CH2_BQ8_REG), 5044b9e385bSPetr Kulhavy BIQUAD_COEFS("CH2 - Biquad 9", TAS5717_CH2_BQ9_REG), 5054b9e385bSPetr Kulhavy BIQUAD_COEFS("CH2 - Biquad 10", TAS5717_CH2_BQ10_REG), 5064b9e385bSPetr Kulhavy BIQUAD_COEFS("CH2 - Biquad 11", TAS5717_CH2_BQ11_REG), 5074b9e385bSPetr Kulhavy 5084b9e385bSPetr Kulhavy BIQUAD_COEFS("CH3 - Biquad 0", TAS5717_CH3_BQ0_REG), 5094b9e385bSPetr Kulhavy BIQUAD_COEFS("CH3 - Biquad 1", TAS5717_CH3_BQ1_REG), 5104b9e385bSPetr Kulhavy 5114b9e385bSPetr Kulhavy BIQUAD_COEFS("CH4 - Biquad 0", TAS5717_CH4_BQ0_REG), 5124b9e385bSPetr Kulhavy BIQUAD_COEFS("CH4 - Biquad 1", TAS5717_CH4_BQ1_REG), 5133fd6e7d9SKevin Cernekee }; 5143fd6e7d9SKevin Cernekee 5153fd6e7d9SKevin Cernekee static const struct reg_default tas5717_reg_defaults[] = { 5163fd6e7d9SKevin Cernekee { 0x04, 0x05 }, 5173fd6e7d9SKevin Cernekee { 0x05, 0x40 }, 5183fd6e7d9SKevin Cernekee { 0x06, 0x00 }, 5193fd6e7d9SKevin Cernekee { 0x07, 0x03ff }, 5203fd6e7d9SKevin Cernekee { 0x08, 0x00c0 }, 5213fd6e7d9SKevin Cernekee { 0x09, 0x00c0 }, 5223fd6e7d9SKevin Cernekee { 0x1b, 0x82 }, 5233fd6e7d9SKevin Cernekee }; 5243fd6e7d9SKevin Cernekee 5253fd6e7d9SKevin Cernekee static const struct regmap_config tas5717_regmap_config = { 5263fd6e7d9SKevin Cernekee .reg_bits = 8, 5273fd6e7d9SKevin Cernekee .val_bits = 32, 5283fd6e7d9SKevin Cernekee .max_register = 0xff, 5293fd6e7d9SKevin Cernekee .reg_read = tas571x_reg_read, 5303fd6e7d9SKevin Cernekee .reg_write = tas571x_reg_write, 5313fd6e7d9SKevin Cernekee .reg_defaults = tas5717_reg_defaults, 5323fd6e7d9SKevin Cernekee .num_reg_defaults = ARRAY_SIZE(tas5717_reg_defaults), 5333fd6e7d9SKevin Cernekee .cache_type = REGCACHE_RBTREE, 534a593ed09SPetr Kulhavy .wr_table = &tas571x_write_regs, 535a593ed09SPetr Kulhavy .volatile_table = &tas571x_volatile_regs, 5363fd6e7d9SKevin Cernekee }; 5373fd6e7d9SKevin Cernekee 5383fd6e7d9SKevin Cernekee /* This entry is reused for tas5719 as the software interface is identical. */ 5393fd6e7d9SKevin Cernekee static const struct tas571x_chip tas5717_chip = { 5403fd6e7d9SKevin Cernekee .supply_names = tas5717_supply_names, 5413fd6e7d9SKevin Cernekee .num_supply_names = ARRAY_SIZE(tas5717_supply_names), 5423fd6e7d9SKevin Cernekee .controls = tas5717_controls, 5433fd6e7d9SKevin Cernekee .num_controls = ARRAY_SIZE(tas5717_controls), 5443fd6e7d9SKevin Cernekee .regmap_config = &tas5717_regmap_config, 5453fd6e7d9SKevin Cernekee .vol_reg_size = 2, 5463fd6e7d9SKevin Cernekee }; 5473fd6e7d9SKevin Cernekee 54823a282c4SPetr Kulhavy static const char *const tas5721_supply_names[] = { 54923a282c4SPetr Kulhavy "AVDD", 55023a282c4SPetr Kulhavy "DVDD", 55123a282c4SPetr Kulhavy "DRVDD", 55223a282c4SPetr Kulhavy "PVDD", 55323a282c4SPetr Kulhavy }; 55423a282c4SPetr Kulhavy 55523a282c4SPetr Kulhavy static const struct snd_kcontrol_new tas5721_controls[] = { 55623a282c4SPetr Kulhavy SOC_SINGLE_TLV("Master Volume", 55723a282c4SPetr Kulhavy TAS571X_MVOL_REG, 55823a282c4SPetr Kulhavy 0, 0xff, 1, tas5711_volume_tlv), 55923a282c4SPetr Kulhavy SOC_DOUBLE_R_TLV("Speaker Volume", 56023a282c4SPetr Kulhavy TAS571X_CH1_VOL_REG, 56123a282c4SPetr Kulhavy TAS571X_CH2_VOL_REG, 56223a282c4SPetr Kulhavy 0, 0xff, 1, tas5711_volume_tlv), 56323a282c4SPetr Kulhavy SOC_DOUBLE("Speaker Switch", 56423a282c4SPetr Kulhavy TAS571X_SOFT_MUTE_REG, 56523a282c4SPetr Kulhavy TAS571X_SOFT_MUTE_CH1_SHIFT, TAS571X_SOFT_MUTE_CH2_SHIFT, 56623a282c4SPetr Kulhavy 1, 1), 56723a282c4SPetr Kulhavy }; 56823a282c4SPetr Kulhavy 56923a282c4SPetr Kulhavy static const struct reg_default tas5721_reg_defaults[] = { 57023a282c4SPetr Kulhavy {TAS571X_CLK_CTRL_REG, 0x6c}, 57123a282c4SPetr Kulhavy {TAS571X_DEV_ID_REG, 0x00}, 57223a282c4SPetr Kulhavy {TAS571X_ERR_STATUS_REG, 0x00}, 57323a282c4SPetr Kulhavy {TAS571X_SYS_CTRL_1_REG, 0xa0}, 57423a282c4SPetr Kulhavy {TAS571X_SDI_REG, 0x05}, 57523a282c4SPetr Kulhavy {TAS571X_SYS_CTRL_2_REG, 0x40}, 57623a282c4SPetr Kulhavy {TAS571X_SOFT_MUTE_REG, 0x00}, 57723a282c4SPetr Kulhavy {TAS571X_MVOL_REG, 0xff}, 57823a282c4SPetr Kulhavy {TAS571X_CH1_VOL_REG, 0x30}, 57923a282c4SPetr Kulhavy {TAS571X_CH2_VOL_REG, 0x30}, 58023a282c4SPetr Kulhavy {TAS571X_CH3_VOL_REG, 0x30}, 58123a282c4SPetr Kulhavy {TAS571X_VOL_CFG_REG, 0x91}, 58223a282c4SPetr Kulhavy {TAS571X_MODULATION_LIMIT_REG, 0x02}, 58323a282c4SPetr Kulhavy {TAS571X_IC_DELAY_CH1_REG, 0xac}, 58423a282c4SPetr Kulhavy {TAS571X_IC_DELAY_CH2_REG, 0x54}, 58523a282c4SPetr Kulhavy {TAS571X_IC_DELAY_CH3_REG, 0xac}, 58623a282c4SPetr Kulhavy {TAS571X_IC_DELAY_CH4_REG, 0x54}, 58723a282c4SPetr Kulhavy {TAS571X_PWM_CH_SDN_GROUP_REG, 0x30}, 58823a282c4SPetr Kulhavy {TAS571X_START_STOP_PERIOD_REG, 0x0f}, 58923a282c4SPetr Kulhavy {TAS571X_OSC_TRIM_REG, 0x82}, 59023a282c4SPetr Kulhavy {TAS571X_BKND_ERR_REG, 0x02}, 59123a282c4SPetr Kulhavy {TAS571X_INPUT_MUX_REG, 0x17772}, 59223a282c4SPetr Kulhavy {TAS571X_CH4_SRC_SELECT_REG, 0x4303}, 59323a282c4SPetr Kulhavy {TAS571X_PWM_MUX_REG, 0x1021345}, 59423a282c4SPetr Kulhavy }; 59523a282c4SPetr Kulhavy 59623a282c4SPetr Kulhavy static const struct regmap_config tas5721_regmap_config = { 59723a282c4SPetr Kulhavy .reg_bits = 8, 59823a282c4SPetr Kulhavy .val_bits = 32, 59923a282c4SPetr Kulhavy .max_register = 0xff, 60023a282c4SPetr Kulhavy .reg_read = tas571x_reg_read, 60123a282c4SPetr Kulhavy .reg_write = tas571x_reg_write, 60223a282c4SPetr Kulhavy .reg_defaults = tas5721_reg_defaults, 60323a282c4SPetr Kulhavy .num_reg_defaults = ARRAY_SIZE(tas5721_reg_defaults), 60423a282c4SPetr Kulhavy .cache_type = REGCACHE_RBTREE, 60523a282c4SPetr Kulhavy .wr_table = &tas571x_write_regs, 60623a282c4SPetr Kulhavy .volatile_table = &tas571x_volatile_regs, 60723a282c4SPetr Kulhavy }; 60823a282c4SPetr Kulhavy 60923a282c4SPetr Kulhavy 61023a282c4SPetr Kulhavy static const struct tas571x_chip tas5721_chip = { 61123a282c4SPetr Kulhavy .supply_names = tas5721_supply_names, 61223a282c4SPetr Kulhavy .num_supply_names = ARRAY_SIZE(tas5721_supply_names), 61323a282c4SPetr Kulhavy .controls = tas5711_controls, 61423a282c4SPetr Kulhavy .num_controls = ARRAY_SIZE(tas5711_controls), 61523a282c4SPetr Kulhavy .regmap_config = &tas5721_regmap_config, 61623a282c4SPetr Kulhavy .vol_reg_size = 1, 61723a282c4SPetr Kulhavy }; 61823a282c4SPetr Kulhavy 6193fd6e7d9SKevin Cernekee static const struct snd_soc_dapm_widget tas571x_dapm_widgets[] = { 6203fd6e7d9SKevin Cernekee SND_SOC_DAPM_DAC("DACL", NULL, SND_SOC_NOPM, 0, 0), 6213fd6e7d9SKevin Cernekee SND_SOC_DAPM_DAC("DACR", NULL, SND_SOC_NOPM, 0, 0), 6223fd6e7d9SKevin Cernekee 6233fd6e7d9SKevin Cernekee SND_SOC_DAPM_OUTPUT("OUT_A"), 6243fd6e7d9SKevin Cernekee SND_SOC_DAPM_OUTPUT("OUT_B"), 6253fd6e7d9SKevin Cernekee SND_SOC_DAPM_OUTPUT("OUT_C"), 6263fd6e7d9SKevin Cernekee SND_SOC_DAPM_OUTPUT("OUT_D"), 6273fd6e7d9SKevin Cernekee }; 6283fd6e7d9SKevin Cernekee 6293fd6e7d9SKevin Cernekee static const struct snd_soc_dapm_route tas571x_dapm_routes[] = { 6303fd6e7d9SKevin Cernekee { "DACL", NULL, "Playback" }, 6313fd6e7d9SKevin Cernekee { "DACR", NULL, "Playback" }, 6323fd6e7d9SKevin Cernekee 6333fd6e7d9SKevin Cernekee { "OUT_A", NULL, "DACL" }, 6343fd6e7d9SKevin Cernekee { "OUT_B", NULL, "DACL" }, 6353fd6e7d9SKevin Cernekee { "OUT_C", NULL, "DACR" }, 6363fd6e7d9SKevin Cernekee { "OUT_D", NULL, "DACR" }, 6373fd6e7d9SKevin Cernekee }; 6383fd6e7d9SKevin Cernekee 6393fd6e7d9SKevin Cernekee static const struct snd_soc_codec_driver tas571x_codec = { 6403fd6e7d9SKevin Cernekee .set_bias_level = tas571x_set_bias_level, 6413fd6e7d9SKevin Cernekee .idle_bias_off = true, 6423fd6e7d9SKevin Cernekee 6433fd6e7d9SKevin Cernekee .dapm_widgets = tas571x_dapm_widgets, 6443fd6e7d9SKevin Cernekee .num_dapm_widgets = ARRAY_SIZE(tas571x_dapm_widgets), 6453fd6e7d9SKevin Cernekee .dapm_routes = tas571x_dapm_routes, 6463fd6e7d9SKevin Cernekee .num_dapm_routes = ARRAY_SIZE(tas571x_dapm_routes), 6473fd6e7d9SKevin Cernekee }; 6483fd6e7d9SKevin Cernekee 6493fd6e7d9SKevin Cernekee static struct snd_soc_dai_driver tas571x_dai = { 6503fd6e7d9SKevin Cernekee .name = "tas571x-hifi", 6513fd6e7d9SKevin Cernekee .playback = { 6523fd6e7d9SKevin Cernekee .stream_name = "Playback", 6533fd6e7d9SKevin Cernekee .channels_min = 2, 6543fd6e7d9SKevin Cernekee .channels_max = 2, 6553fd6e7d9SKevin Cernekee .rates = SNDRV_PCM_RATE_8000_48000, 6563fd6e7d9SKevin Cernekee .formats = SNDRV_PCM_FMTBIT_S32_LE | 6573fd6e7d9SKevin Cernekee SNDRV_PCM_FMTBIT_S24_LE | 6583fd6e7d9SKevin Cernekee SNDRV_PCM_FMTBIT_S16_LE, 6593fd6e7d9SKevin Cernekee }, 6603fd6e7d9SKevin Cernekee .ops = &tas571x_dai_ops, 6613fd6e7d9SKevin Cernekee }; 6623fd6e7d9SKevin Cernekee 6633fd6e7d9SKevin Cernekee static const struct of_device_id tas571x_of_match[]; 6643fd6e7d9SKevin Cernekee 6653fd6e7d9SKevin Cernekee static int tas571x_i2c_probe(struct i2c_client *client, 6663fd6e7d9SKevin Cernekee const struct i2c_device_id *id) 6673fd6e7d9SKevin Cernekee { 6683fd6e7d9SKevin Cernekee struct tas571x_private *priv; 6693fd6e7d9SKevin Cernekee struct device *dev = &client->dev; 67097fceb4dSKevin Cernekee const struct of_device_id *of_id; 6713fd6e7d9SKevin Cernekee int i, ret; 6723fd6e7d9SKevin Cernekee 6733fd6e7d9SKevin Cernekee priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 6743fd6e7d9SKevin Cernekee if (!priv) 6753fd6e7d9SKevin Cernekee return -ENOMEM; 6763fd6e7d9SKevin Cernekee i2c_set_clientdata(client, priv); 6773fd6e7d9SKevin Cernekee 6783fd6e7d9SKevin Cernekee of_id = of_match_device(tas571x_of_match, dev); 679630e413dSPetr Kulhavy if (of_id) 68097fceb4dSKevin Cernekee priv->chip = of_id->data; 681630e413dSPetr Kulhavy else 682630e413dSPetr Kulhavy priv->chip = (void *) id->driver_data; 6833fd6e7d9SKevin Cernekee 6843fd6e7d9SKevin Cernekee priv->mclk = devm_clk_get(dev, "mclk"); 6853fd6e7d9SKevin Cernekee if (IS_ERR(priv->mclk) && PTR_ERR(priv->mclk) != -ENOENT) { 6863fd6e7d9SKevin Cernekee dev_err(dev, "Failed to request mclk: %ld\n", 6873fd6e7d9SKevin Cernekee PTR_ERR(priv->mclk)); 6883fd6e7d9SKevin Cernekee return PTR_ERR(priv->mclk); 6893fd6e7d9SKevin Cernekee } 6903fd6e7d9SKevin Cernekee 6913fd6e7d9SKevin Cernekee BUG_ON(priv->chip->num_supply_names > TAS571X_MAX_SUPPLIES); 6923fd6e7d9SKevin Cernekee for (i = 0; i < priv->chip->num_supply_names; i++) 6933fd6e7d9SKevin Cernekee priv->supplies[i].supply = priv->chip->supply_names[i]; 6943fd6e7d9SKevin Cernekee 6953fd6e7d9SKevin Cernekee ret = devm_regulator_bulk_get(dev, priv->chip->num_supply_names, 6963fd6e7d9SKevin Cernekee priv->supplies); 6973fd6e7d9SKevin Cernekee if (ret) { 6983fd6e7d9SKevin Cernekee dev_err(dev, "Failed to get supplies: %d\n", ret); 6993fd6e7d9SKevin Cernekee return ret; 7003fd6e7d9SKevin Cernekee } 7013fd6e7d9SKevin Cernekee ret = regulator_bulk_enable(priv->chip->num_supply_names, 7023fd6e7d9SKevin Cernekee priv->supplies); 7033fd6e7d9SKevin Cernekee if (ret) { 7043fd6e7d9SKevin Cernekee dev_err(dev, "Failed to enable supplies: %d\n", ret); 7053fd6e7d9SKevin Cernekee return ret; 7063fd6e7d9SKevin Cernekee } 7073fd6e7d9SKevin Cernekee 7083fd6e7d9SKevin Cernekee priv->regmap = devm_regmap_init(dev, NULL, client, 7093fd6e7d9SKevin Cernekee priv->chip->regmap_config); 7103fd6e7d9SKevin Cernekee if (IS_ERR(priv->regmap)) 7113fd6e7d9SKevin Cernekee return PTR_ERR(priv->regmap); 7123fd6e7d9SKevin Cernekee 7133fd6e7d9SKevin Cernekee priv->pdn_gpio = devm_gpiod_get_optional(dev, "pdn", GPIOD_OUT_LOW); 7143fd6e7d9SKevin Cernekee if (IS_ERR(priv->pdn_gpio)) { 7153fd6e7d9SKevin Cernekee dev_err(dev, "error requesting pdn_gpio: %ld\n", 7163fd6e7d9SKevin Cernekee PTR_ERR(priv->pdn_gpio)); 7173fd6e7d9SKevin Cernekee return PTR_ERR(priv->pdn_gpio); 7183fd6e7d9SKevin Cernekee } 7193fd6e7d9SKevin Cernekee 7203fd6e7d9SKevin Cernekee priv->reset_gpio = devm_gpiod_get_optional(dev, "reset", 7213fd6e7d9SKevin Cernekee GPIOD_OUT_HIGH); 7223fd6e7d9SKevin Cernekee if (IS_ERR(priv->reset_gpio)) { 7233fd6e7d9SKevin Cernekee dev_err(dev, "error requesting reset_gpio: %ld\n", 7243fd6e7d9SKevin Cernekee PTR_ERR(priv->reset_gpio)); 7253fd6e7d9SKevin Cernekee return PTR_ERR(priv->reset_gpio); 7263fd6e7d9SKevin Cernekee } else if (priv->reset_gpio) { 7273fd6e7d9SKevin Cernekee /* pulse the active low reset line for ~100us */ 7283fd6e7d9SKevin Cernekee usleep_range(100, 200); 7293fd6e7d9SKevin Cernekee gpiod_set_value(priv->reset_gpio, 0); 7303fd6e7d9SKevin Cernekee usleep_range(12000, 20000); 7313fd6e7d9SKevin Cernekee } 7323fd6e7d9SKevin Cernekee 7333fd6e7d9SKevin Cernekee ret = regmap_write(priv->regmap, TAS571X_OSC_TRIM_REG, 0); 7343fd6e7d9SKevin Cernekee if (ret) 7353fd6e7d9SKevin Cernekee return ret; 7363fd6e7d9SKevin Cernekee 7373fd6e7d9SKevin Cernekee 7383fd6e7d9SKevin Cernekee memcpy(&priv->codec_driver, &tas571x_codec, sizeof(priv->codec_driver)); 7393fd6e7d9SKevin Cernekee priv->codec_driver.controls = priv->chip->controls; 7403fd6e7d9SKevin Cernekee priv->codec_driver.num_controls = priv->chip->num_controls; 7413fd6e7d9SKevin Cernekee 7423fd6e7d9SKevin Cernekee if (priv->chip->vol_reg_size == 2) { 7433fd6e7d9SKevin Cernekee /* 7443fd6e7d9SKevin Cernekee * The master volume defaults to 0x3ff (mute), but we ignore 7453fd6e7d9SKevin Cernekee * (zero) the LSB because the hardware step size is 0.125 dB 7463fd6e7d9SKevin Cernekee * and TLV_DB_SCALE_ITEM has a resolution of 0.01 dB. 7473fd6e7d9SKevin Cernekee */ 7483fd6e7d9SKevin Cernekee ret = regmap_update_bits(priv->regmap, TAS571X_MVOL_REG, 1, 0); 7493fd6e7d9SKevin Cernekee if (ret) 7503fd6e7d9SKevin Cernekee return ret; 7513fd6e7d9SKevin Cernekee } 7523fd6e7d9SKevin Cernekee 7533fd6e7d9SKevin Cernekee regcache_cache_only(priv->regmap, true); 7543fd6e7d9SKevin Cernekee gpiod_set_value(priv->pdn_gpio, 1); 7553fd6e7d9SKevin Cernekee 7563fd6e7d9SKevin Cernekee return snd_soc_register_codec(&client->dev, &priv->codec_driver, 7573fd6e7d9SKevin Cernekee &tas571x_dai, 1); 7583fd6e7d9SKevin Cernekee } 7593fd6e7d9SKevin Cernekee 7603fd6e7d9SKevin Cernekee static int tas571x_i2c_remove(struct i2c_client *client) 7613fd6e7d9SKevin Cernekee { 7623fd6e7d9SKevin Cernekee struct tas571x_private *priv = i2c_get_clientdata(client); 7633fd6e7d9SKevin Cernekee 7643fd6e7d9SKevin Cernekee snd_soc_unregister_codec(&client->dev); 7653fd6e7d9SKevin Cernekee regulator_bulk_disable(priv->chip->num_supply_names, priv->supplies); 7663fd6e7d9SKevin Cernekee 7673fd6e7d9SKevin Cernekee return 0; 7683fd6e7d9SKevin Cernekee } 7693fd6e7d9SKevin Cernekee 7703fd6e7d9SKevin Cernekee static const struct of_device_id tas571x_of_match[] = { 7713fd6e7d9SKevin Cernekee { .compatible = "ti,tas5711", .data = &tas5711_chip, }, 7723fd6e7d9SKevin Cernekee { .compatible = "ti,tas5717", .data = &tas5717_chip, }, 7733fd6e7d9SKevin Cernekee { .compatible = "ti,tas5719", .data = &tas5717_chip, }, 77423a282c4SPetr Kulhavy { .compatible = "ti,tas5721", .data = &tas5721_chip, }, 7753fd6e7d9SKevin Cernekee { } 7763fd6e7d9SKevin Cernekee }; 7773fd6e7d9SKevin Cernekee MODULE_DEVICE_TABLE(of, tas571x_of_match); 7783fd6e7d9SKevin Cernekee 7793fd6e7d9SKevin Cernekee static const struct i2c_device_id tas571x_i2c_id[] = { 780630e413dSPetr Kulhavy { "tas5711", (kernel_ulong_t) &tas5711_chip }, 781630e413dSPetr Kulhavy { "tas5717", (kernel_ulong_t) &tas5717_chip }, 782630e413dSPetr Kulhavy { "tas5719", (kernel_ulong_t) &tas5717_chip }, 78323a282c4SPetr Kulhavy { "tas5721", (kernel_ulong_t) &tas5721_chip }, 7843fd6e7d9SKevin Cernekee { } 7853fd6e7d9SKevin Cernekee }; 7863fd6e7d9SKevin Cernekee MODULE_DEVICE_TABLE(i2c, tas571x_i2c_id); 7873fd6e7d9SKevin Cernekee 7883fd6e7d9SKevin Cernekee static struct i2c_driver tas571x_i2c_driver = { 7893fd6e7d9SKevin Cernekee .driver = { 7903fd6e7d9SKevin Cernekee .name = "tas571x", 7913fd6e7d9SKevin Cernekee .of_match_table = of_match_ptr(tas571x_of_match), 7923fd6e7d9SKevin Cernekee }, 7933fd6e7d9SKevin Cernekee .probe = tas571x_i2c_probe, 7943fd6e7d9SKevin Cernekee .remove = tas571x_i2c_remove, 7953fd6e7d9SKevin Cernekee .id_table = tas571x_i2c_id, 7963fd6e7d9SKevin Cernekee }; 7973fd6e7d9SKevin Cernekee module_i2c_driver(tas571x_i2c_driver); 7983fd6e7d9SKevin Cernekee 7993fd6e7d9SKevin Cernekee MODULE_DESCRIPTION("ASoC TAS571x driver"); 8003fd6e7d9SKevin Cernekee MODULE_AUTHOR("Kevin Cernekee <cernekee@chromium.org>"); 8013fd6e7d9SKevin Cernekee MODULE_LICENSE("GPL"); 802