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 * 73fd6e7d9SKevin Cernekee * This program is free software; you can redistribute it and/or modify 83fd6e7d9SKevin Cernekee * it under the terms of the GNU General Public License as published by 93fd6e7d9SKevin Cernekee * the Free Software Foundation; either version 2 of the License, or 103fd6e7d9SKevin Cernekee * (at your option) any later version. 113fd6e7d9SKevin Cernekee */ 123fd6e7d9SKevin Cernekee 133fd6e7d9SKevin Cernekee #include <linux/clk.h> 143fd6e7d9SKevin Cernekee #include <linux/delay.h> 153fd6e7d9SKevin Cernekee #include <linux/device.h> 163fd6e7d9SKevin Cernekee #include <linux/gpio/consumer.h> 173fd6e7d9SKevin Cernekee #include <linux/i2c.h> 183fd6e7d9SKevin Cernekee #include <linux/init.h> 193fd6e7d9SKevin Cernekee #include <linux/kernel.h> 203fd6e7d9SKevin Cernekee #include <linux/module.h> 213fd6e7d9SKevin Cernekee #include <linux/of_device.h> 223fd6e7d9SKevin Cernekee #include <linux/regmap.h> 233fd6e7d9SKevin Cernekee #include <linux/regulator/consumer.h> 243fd6e7d9SKevin Cernekee #include <linux/stddef.h> 253fd6e7d9SKevin Cernekee #include <sound/pcm_params.h> 263fd6e7d9SKevin Cernekee #include <sound/soc.h> 273fd6e7d9SKevin Cernekee #include <sound/tlv.h> 283fd6e7d9SKevin Cernekee 293fd6e7d9SKevin Cernekee #include "tas571x.h" 303fd6e7d9SKevin Cernekee 313fd6e7d9SKevin Cernekee #define TAS571X_MAX_SUPPLIES 6 323fd6e7d9SKevin Cernekee 333fd6e7d9SKevin Cernekee struct tas571x_chip { 343fd6e7d9SKevin Cernekee const char *const *supply_names; 353fd6e7d9SKevin Cernekee int num_supply_names; 363fd6e7d9SKevin Cernekee const struct snd_kcontrol_new *controls; 373fd6e7d9SKevin Cernekee int num_controls; 383fd6e7d9SKevin Cernekee const struct regmap_config *regmap_config; 393fd6e7d9SKevin Cernekee int vol_reg_size; 403fd6e7d9SKevin Cernekee }; 413fd6e7d9SKevin Cernekee 423fd6e7d9SKevin Cernekee struct tas571x_private { 433fd6e7d9SKevin Cernekee const struct tas571x_chip *chip; 443fd6e7d9SKevin Cernekee struct regmap *regmap; 453fd6e7d9SKevin Cernekee struct regulator_bulk_data supplies[TAS571X_MAX_SUPPLIES]; 463fd6e7d9SKevin Cernekee struct clk *mclk; 473fd6e7d9SKevin Cernekee unsigned int format; 483fd6e7d9SKevin Cernekee struct gpio_desc *reset_gpio; 493fd6e7d9SKevin Cernekee struct gpio_desc *pdn_gpio; 503fd6e7d9SKevin Cernekee struct snd_soc_codec_driver codec_driver; 513fd6e7d9SKevin Cernekee }; 523fd6e7d9SKevin Cernekee 533fd6e7d9SKevin Cernekee static int tas571x_register_size(struct tas571x_private *priv, unsigned int reg) 543fd6e7d9SKevin Cernekee { 553fd6e7d9SKevin Cernekee switch (reg) { 563fd6e7d9SKevin Cernekee case TAS571X_MVOL_REG: 573fd6e7d9SKevin Cernekee case TAS571X_CH1_VOL_REG: 583fd6e7d9SKevin Cernekee case TAS571X_CH2_VOL_REG: 593fd6e7d9SKevin Cernekee return priv->chip->vol_reg_size; 603fd6e7d9SKevin Cernekee default: 613fd6e7d9SKevin Cernekee return 1; 623fd6e7d9SKevin Cernekee } 633fd6e7d9SKevin Cernekee } 643fd6e7d9SKevin Cernekee 653fd6e7d9SKevin Cernekee static int tas571x_reg_write(void *context, unsigned int reg, 663fd6e7d9SKevin Cernekee unsigned int value) 673fd6e7d9SKevin Cernekee { 683fd6e7d9SKevin Cernekee struct i2c_client *client = context; 693fd6e7d9SKevin Cernekee struct tas571x_private *priv = i2c_get_clientdata(client); 703fd6e7d9SKevin Cernekee unsigned int i, size; 713fd6e7d9SKevin Cernekee uint8_t buf[5]; 723fd6e7d9SKevin Cernekee int ret; 733fd6e7d9SKevin Cernekee 743fd6e7d9SKevin Cernekee size = tas571x_register_size(priv, reg); 753fd6e7d9SKevin Cernekee buf[0] = reg; 763fd6e7d9SKevin Cernekee 773fd6e7d9SKevin Cernekee for (i = size; i >= 1; --i) { 783fd6e7d9SKevin Cernekee buf[i] = value; 793fd6e7d9SKevin Cernekee value >>= 8; 803fd6e7d9SKevin Cernekee } 813fd6e7d9SKevin Cernekee 823fd6e7d9SKevin Cernekee ret = i2c_master_send(client, buf, size + 1); 833fd6e7d9SKevin Cernekee if (ret == size + 1) 843fd6e7d9SKevin Cernekee return 0; 853fd6e7d9SKevin Cernekee else if (ret < 0) 863fd6e7d9SKevin Cernekee return ret; 873fd6e7d9SKevin Cernekee else 883fd6e7d9SKevin Cernekee return -EIO; 893fd6e7d9SKevin Cernekee } 903fd6e7d9SKevin Cernekee 913fd6e7d9SKevin Cernekee static int tas571x_reg_read(void *context, unsigned int reg, 923fd6e7d9SKevin Cernekee unsigned int *value) 933fd6e7d9SKevin Cernekee { 943fd6e7d9SKevin Cernekee struct i2c_client *client = context; 953fd6e7d9SKevin Cernekee struct tas571x_private *priv = i2c_get_clientdata(client); 963fd6e7d9SKevin Cernekee uint8_t send_buf, recv_buf[4]; 973fd6e7d9SKevin Cernekee struct i2c_msg msgs[2]; 983fd6e7d9SKevin Cernekee unsigned int size; 993fd6e7d9SKevin Cernekee unsigned int i; 1003fd6e7d9SKevin Cernekee int ret; 1013fd6e7d9SKevin Cernekee 1023fd6e7d9SKevin Cernekee size = tas571x_register_size(priv, reg); 1033fd6e7d9SKevin Cernekee send_buf = reg; 1043fd6e7d9SKevin Cernekee 1053fd6e7d9SKevin Cernekee msgs[0].addr = client->addr; 1063fd6e7d9SKevin Cernekee msgs[0].len = sizeof(send_buf); 1073fd6e7d9SKevin Cernekee msgs[0].buf = &send_buf; 1083fd6e7d9SKevin Cernekee msgs[0].flags = 0; 1093fd6e7d9SKevin Cernekee 1103fd6e7d9SKevin Cernekee msgs[1].addr = client->addr; 1113fd6e7d9SKevin Cernekee msgs[1].len = size; 1123fd6e7d9SKevin Cernekee msgs[1].buf = recv_buf; 1133fd6e7d9SKevin Cernekee msgs[1].flags = I2C_M_RD; 1143fd6e7d9SKevin Cernekee 1153fd6e7d9SKevin Cernekee ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); 1163fd6e7d9SKevin Cernekee if (ret < 0) 1173fd6e7d9SKevin Cernekee return ret; 1183fd6e7d9SKevin Cernekee else if (ret != ARRAY_SIZE(msgs)) 1193fd6e7d9SKevin Cernekee return -EIO; 1203fd6e7d9SKevin Cernekee 1213fd6e7d9SKevin Cernekee *value = 0; 1223fd6e7d9SKevin Cernekee 1233fd6e7d9SKevin Cernekee for (i = 0; i < size; i++) { 1243fd6e7d9SKevin Cernekee *value <<= 8; 1253fd6e7d9SKevin Cernekee *value |= recv_buf[i]; 1263fd6e7d9SKevin Cernekee } 1273fd6e7d9SKevin Cernekee 1283fd6e7d9SKevin Cernekee return 0; 1293fd6e7d9SKevin Cernekee } 1303fd6e7d9SKevin Cernekee 1313fd6e7d9SKevin Cernekee static int tas571x_set_dai_fmt(struct snd_soc_dai *dai, unsigned int format) 1323fd6e7d9SKevin Cernekee { 1333fd6e7d9SKevin Cernekee struct tas571x_private *priv = snd_soc_codec_get_drvdata(dai->codec); 1343fd6e7d9SKevin Cernekee 1353fd6e7d9SKevin Cernekee priv->format = format; 1363fd6e7d9SKevin Cernekee 1373fd6e7d9SKevin Cernekee return 0; 1383fd6e7d9SKevin Cernekee } 1393fd6e7d9SKevin Cernekee 1403fd6e7d9SKevin Cernekee static int tas571x_hw_params(struct snd_pcm_substream *substream, 1413fd6e7d9SKevin Cernekee struct snd_pcm_hw_params *params, 1423fd6e7d9SKevin Cernekee struct snd_soc_dai *dai) 1433fd6e7d9SKevin Cernekee { 1443fd6e7d9SKevin Cernekee struct tas571x_private *priv = snd_soc_codec_get_drvdata(dai->codec); 1453fd6e7d9SKevin Cernekee u32 val; 1463fd6e7d9SKevin Cernekee 1473fd6e7d9SKevin Cernekee switch (priv->format & SND_SOC_DAIFMT_FORMAT_MASK) { 1483fd6e7d9SKevin Cernekee case SND_SOC_DAIFMT_RIGHT_J: 1493fd6e7d9SKevin Cernekee val = 0x00; 1503fd6e7d9SKevin Cernekee break; 1513fd6e7d9SKevin Cernekee case SND_SOC_DAIFMT_I2S: 1523fd6e7d9SKevin Cernekee val = 0x03; 1533fd6e7d9SKevin Cernekee break; 1543fd6e7d9SKevin Cernekee case SND_SOC_DAIFMT_LEFT_J: 1553fd6e7d9SKevin Cernekee val = 0x06; 1563fd6e7d9SKevin Cernekee break; 1573fd6e7d9SKevin Cernekee default: 1583fd6e7d9SKevin Cernekee return -EINVAL; 1593fd6e7d9SKevin Cernekee } 1603fd6e7d9SKevin Cernekee 1613fd6e7d9SKevin Cernekee if (params_width(params) >= 24) 1623fd6e7d9SKevin Cernekee val += 2; 1633fd6e7d9SKevin Cernekee else if (params_width(params) >= 20) 1643fd6e7d9SKevin Cernekee val += 1; 1653fd6e7d9SKevin Cernekee 1663fd6e7d9SKevin Cernekee return regmap_update_bits(priv->regmap, TAS571X_SDI_REG, 1673fd6e7d9SKevin Cernekee TAS571X_SDI_FMT_MASK, val); 1683fd6e7d9SKevin Cernekee } 1693fd6e7d9SKevin Cernekee 1703fd6e7d9SKevin Cernekee static int tas571x_set_bias_level(struct snd_soc_codec *codec, 1713fd6e7d9SKevin Cernekee enum snd_soc_bias_level level) 1723fd6e7d9SKevin Cernekee { 1733fd6e7d9SKevin Cernekee struct tas571x_private *priv = snd_soc_codec_get_drvdata(codec); 1743fd6e7d9SKevin Cernekee int ret; 1753fd6e7d9SKevin Cernekee 1763fd6e7d9SKevin Cernekee switch (level) { 1773fd6e7d9SKevin Cernekee case SND_SOC_BIAS_ON: 1783fd6e7d9SKevin Cernekee break; 1793fd6e7d9SKevin Cernekee case SND_SOC_BIAS_PREPARE: 1803fd6e7d9SKevin Cernekee break; 1813fd6e7d9SKevin Cernekee case SND_SOC_BIAS_STANDBY: 1823fd6e7d9SKevin Cernekee if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { 1833fd6e7d9SKevin Cernekee if (!IS_ERR(priv->mclk)) { 1843fd6e7d9SKevin Cernekee ret = clk_prepare_enable(priv->mclk); 1853fd6e7d9SKevin Cernekee if (ret) { 1863fd6e7d9SKevin Cernekee dev_err(codec->dev, 1873fd6e7d9SKevin Cernekee "Failed to enable master clock: %d\n", 1883fd6e7d9SKevin Cernekee ret); 1893fd6e7d9SKevin Cernekee return ret; 1903fd6e7d9SKevin Cernekee } 1913fd6e7d9SKevin Cernekee } 1923fd6e7d9SKevin Cernekee 1933fd6e7d9SKevin Cernekee gpiod_set_value(priv->pdn_gpio, 0); 1943fd6e7d9SKevin Cernekee usleep_range(5000, 6000); 1953fd6e7d9SKevin Cernekee 1963fd6e7d9SKevin Cernekee regcache_cache_only(priv->regmap, false); 1973fd6e7d9SKevin Cernekee ret = regcache_sync(priv->regmap); 1983fd6e7d9SKevin Cernekee if (ret) 1993fd6e7d9SKevin Cernekee return ret; 2003fd6e7d9SKevin Cernekee } 2013fd6e7d9SKevin Cernekee break; 2023fd6e7d9SKevin Cernekee case SND_SOC_BIAS_OFF: 2033fd6e7d9SKevin Cernekee regcache_cache_only(priv->regmap, true); 2043fd6e7d9SKevin Cernekee gpiod_set_value(priv->pdn_gpio, 1); 2053fd6e7d9SKevin Cernekee 2063fd6e7d9SKevin Cernekee if (!IS_ERR(priv->mclk)) 2073fd6e7d9SKevin Cernekee clk_disable_unprepare(priv->mclk); 2083fd6e7d9SKevin Cernekee break; 2093fd6e7d9SKevin Cernekee } 2103fd6e7d9SKevin Cernekee 2113fd6e7d9SKevin Cernekee codec->dapm.bias_level = level; 2123fd6e7d9SKevin Cernekee return 0; 2133fd6e7d9SKevin Cernekee } 2143fd6e7d9SKevin Cernekee 2153fd6e7d9SKevin Cernekee static const struct snd_soc_dai_ops tas571x_dai_ops = { 2163fd6e7d9SKevin Cernekee .set_fmt = tas571x_set_dai_fmt, 2173fd6e7d9SKevin Cernekee .hw_params = tas571x_hw_params, 2183fd6e7d9SKevin Cernekee }; 2193fd6e7d9SKevin Cernekee 2203fd6e7d9SKevin Cernekee static const char *const tas5711_supply_names[] = { 2213fd6e7d9SKevin Cernekee "AVDD", 2223fd6e7d9SKevin Cernekee "DVDD", 2233fd6e7d9SKevin Cernekee "PVDD_A", 2243fd6e7d9SKevin Cernekee "PVDD_B", 2253fd6e7d9SKevin Cernekee "PVDD_C", 2263fd6e7d9SKevin Cernekee "PVDD_D", 2273fd6e7d9SKevin Cernekee }; 2283fd6e7d9SKevin Cernekee 2293fd6e7d9SKevin Cernekee static const DECLARE_TLV_DB_SCALE(tas5711_volume_tlv, -10350, 50, 1); 2303fd6e7d9SKevin Cernekee 2313fd6e7d9SKevin Cernekee static const struct snd_kcontrol_new tas5711_controls[] = { 2323fd6e7d9SKevin Cernekee SOC_SINGLE_TLV("Master Volume", 2333fd6e7d9SKevin Cernekee TAS571X_MVOL_REG, 2343fd6e7d9SKevin Cernekee 0, 0xff, 1, tas5711_volume_tlv), 2353fd6e7d9SKevin Cernekee SOC_DOUBLE_R_TLV("Speaker Volume", 2363fd6e7d9SKevin Cernekee TAS571X_CH1_VOL_REG, 2373fd6e7d9SKevin Cernekee TAS571X_CH2_VOL_REG, 2383fd6e7d9SKevin Cernekee 0, 0xff, 1, tas5711_volume_tlv), 2393fd6e7d9SKevin Cernekee SOC_DOUBLE("Speaker Switch", 2403fd6e7d9SKevin Cernekee TAS571X_SOFT_MUTE_REG, 2413fd6e7d9SKevin Cernekee TAS571X_SOFT_MUTE_CH1_SHIFT, TAS571X_SOFT_MUTE_CH2_SHIFT, 2423fd6e7d9SKevin Cernekee 1, 1), 2433fd6e7d9SKevin Cernekee }; 2443fd6e7d9SKevin Cernekee 2453fd6e7d9SKevin Cernekee static const struct reg_default tas5711_reg_defaults[] = { 2463fd6e7d9SKevin Cernekee { 0x04, 0x05 }, 2473fd6e7d9SKevin Cernekee { 0x05, 0x40 }, 2483fd6e7d9SKevin Cernekee { 0x06, 0x00 }, 2493fd6e7d9SKevin Cernekee { 0x07, 0xff }, 2503fd6e7d9SKevin Cernekee { 0x08, 0x30 }, 2513fd6e7d9SKevin Cernekee { 0x09, 0x30 }, 2523fd6e7d9SKevin Cernekee { 0x1b, 0x82 }, 2533fd6e7d9SKevin Cernekee }; 2543fd6e7d9SKevin Cernekee 2553fd6e7d9SKevin Cernekee static const struct regmap_config tas5711_regmap_config = { 2563fd6e7d9SKevin Cernekee .reg_bits = 8, 2573fd6e7d9SKevin Cernekee .val_bits = 32, 2583fd6e7d9SKevin Cernekee .max_register = 0xff, 2593fd6e7d9SKevin Cernekee .reg_read = tas571x_reg_read, 2603fd6e7d9SKevin Cernekee .reg_write = tas571x_reg_write, 2613fd6e7d9SKevin Cernekee .reg_defaults = tas5711_reg_defaults, 2623fd6e7d9SKevin Cernekee .num_reg_defaults = ARRAY_SIZE(tas5711_reg_defaults), 2633fd6e7d9SKevin Cernekee .cache_type = REGCACHE_RBTREE, 2643fd6e7d9SKevin Cernekee }; 2653fd6e7d9SKevin Cernekee 2663fd6e7d9SKevin Cernekee static const struct tas571x_chip tas5711_chip = { 2673fd6e7d9SKevin Cernekee .supply_names = tas5711_supply_names, 2683fd6e7d9SKevin Cernekee .num_supply_names = ARRAY_SIZE(tas5711_supply_names), 2693fd6e7d9SKevin Cernekee .controls = tas5711_controls, 2703fd6e7d9SKevin Cernekee .num_controls = ARRAY_SIZE(tas5711_controls), 2713fd6e7d9SKevin Cernekee .regmap_config = &tas5711_regmap_config, 2723fd6e7d9SKevin Cernekee .vol_reg_size = 1, 2733fd6e7d9SKevin Cernekee }; 2743fd6e7d9SKevin Cernekee 2753fd6e7d9SKevin Cernekee static const char *const tas5717_supply_names[] = { 2763fd6e7d9SKevin Cernekee "AVDD", 2773fd6e7d9SKevin Cernekee "DVDD", 2783fd6e7d9SKevin Cernekee "HPVDD", 2793fd6e7d9SKevin Cernekee "PVDD_AB", 2803fd6e7d9SKevin Cernekee "PVDD_CD", 2813fd6e7d9SKevin Cernekee }; 2823fd6e7d9SKevin Cernekee 2833fd6e7d9SKevin Cernekee static const DECLARE_TLV_DB_SCALE(tas5717_volume_tlv, -10375, 25, 0); 2843fd6e7d9SKevin Cernekee 2853fd6e7d9SKevin Cernekee static const struct snd_kcontrol_new tas5717_controls[] = { 2863fd6e7d9SKevin Cernekee /* MVOL LSB is ignored - see comments in tas571x_i2c_probe() */ 2873fd6e7d9SKevin Cernekee SOC_SINGLE_TLV("Master Volume", 2883fd6e7d9SKevin Cernekee TAS571X_MVOL_REG, 1, 0x1ff, 1, 2893fd6e7d9SKevin Cernekee tas5717_volume_tlv), 2903fd6e7d9SKevin Cernekee SOC_DOUBLE_R_TLV("Speaker Volume", 2913fd6e7d9SKevin Cernekee TAS571X_CH1_VOL_REG, TAS571X_CH2_VOL_REG, 2923fd6e7d9SKevin Cernekee 1, 0x1ff, 1, tas5717_volume_tlv), 2933fd6e7d9SKevin Cernekee SOC_DOUBLE("Speaker Switch", 2943fd6e7d9SKevin Cernekee TAS571X_SOFT_MUTE_REG, 2953fd6e7d9SKevin Cernekee TAS571X_SOFT_MUTE_CH1_SHIFT, TAS571X_SOFT_MUTE_CH2_SHIFT, 2963fd6e7d9SKevin Cernekee 1, 1), 2973fd6e7d9SKevin Cernekee }; 2983fd6e7d9SKevin Cernekee 2993fd6e7d9SKevin Cernekee static const struct reg_default tas5717_reg_defaults[] = { 3003fd6e7d9SKevin Cernekee { 0x04, 0x05 }, 3013fd6e7d9SKevin Cernekee { 0x05, 0x40 }, 3023fd6e7d9SKevin Cernekee { 0x06, 0x00 }, 3033fd6e7d9SKevin Cernekee { 0x07, 0x03ff }, 3043fd6e7d9SKevin Cernekee { 0x08, 0x00c0 }, 3053fd6e7d9SKevin Cernekee { 0x09, 0x00c0 }, 3063fd6e7d9SKevin Cernekee { 0x1b, 0x82 }, 3073fd6e7d9SKevin Cernekee }; 3083fd6e7d9SKevin Cernekee 3093fd6e7d9SKevin Cernekee static const struct regmap_config tas5717_regmap_config = { 3103fd6e7d9SKevin Cernekee .reg_bits = 8, 3113fd6e7d9SKevin Cernekee .val_bits = 32, 3123fd6e7d9SKevin Cernekee .max_register = 0xff, 3133fd6e7d9SKevin Cernekee .reg_read = tas571x_reg_read, 3143fd6e7d9SKevin Cernekee .reg_write = tas571x_reg_write, 3153fd6e7d9SKevin Cernekee .reg_defaults = tas5717_reg_defaults, 3163fd6e7d9SKevin Cernekee .num_reg_defaults = ARRAY_SIZE(tas5717_reg_defaults), 3173fd6e7d9SKevin Cernekee .cache_type = REGCACHE_RBTREE, 3183fd6e7d9SKevin Cernekee }; 3193fd6e7d9SKevin Cernekee 3203fd6e7d9SKevin Cernekee /* This entry is reused for tas5719 as the software interface is identical. */ 3213fd6e7d9SKevin Cernekee static const struct tas571x_chip tas5717_chip = { 3223fd6e7d9SKevin Cernekee .supply_names = tas5717_supply_names, 3233fd6e7d9SKevin Cernekee .num_supply_names = ARRAY_SIZE(tas5717_supply_names), 3243fd6e7d9SKevin Cernekee .controls = tas5717_controls, 3253fd6e7d9SKevin Cernekee .num_controls = ARRAY_SIZE(tas5717_controls), 3263fd6e7d9SKevin Cernekee .regmap_config = &tas5717_regmap_config, 3273fd6e7d9SKevin Cernekee .vol_reg_size = 2, 3283fd6e7d9SKevin Cernekee }; 3293fd6e7d9SKevin Cernekee 3303fd6e7d9SKevin Cernekee static const struct snd_soc_dapm_widget tas571x_dapm_widgets[] = { 3313fd6e7d9SKevin Cernekee SND_SOC_DAPM_DAC("DACL", NULL, SND_SOC_NOPM, 0, 0), 3323fd6e7d9SKevin Cernekee SND_SOC_DAPM_DAC("DACR", NULL, SND_SOC_NOPM, 0, 0), 3333fd6e7d9SKevin Cernekee 3343fd6e7d9SKevin Cernekee SND_SOC_DAPM_OUTPUT("OUT_A"), 3353fd6e7d9SKevin Cernekee SND_SOC_DAPM_OUTPUT("OUT_B"), 3363fd6e7d9SKevin Cernekee SND_SOC_DAPM_OUTPUT("OUT_C"), 3373fd6e7d9SKevin Cernekee SND_SOC_DAPM_OUTPUT("OUT_D"), 3383fd6e7d9SKevin Cernekee }; 3393fd6e7d9SKevin Cernekee 3403fd6e7d9SKevin Cernekee static const struct snd_soc_dapm_route tas571x_dapm_routes[] = { 3413fd6e7d9SKevin Cernekee { "DACL", NULL, "Playback" }, 3423fd6e7d9SKevin Cernekee { "DACR", NULL, "Playback" }, 3433fd6e7d9SKevin Cernekee 3443fd6e7d9SKevin Cernekee { "OUT_A", NULL, "DACL" }, 3453fd6e7d9SKevin Cernekee { "OUT_B", NULL, "DACL" }, 3463fd6e7d9SKevin Cernekee { "OUT_C", NULL, "DACR" }, 3473fd6e7d9SKevin Cernekee { "OUT_D", NULL, "DACR" }, 3483fd6e7d9SKevin Cernekee }; 3493fd6e7d9SKevin Cernekee 3503fd6e7d9SKevin Cernekee static const struct snd_soc_codec_driver tas571x_codec = { 3513fd6e7d9SKevin Cernekee .set_bias_level = tas571x_set_bias_level, 3523fd6e7d9SKevin Cernekee .idle_bias_off = true, 3533fd6e7d9SKevin Cernekee 3543fd6e7d9SKevin Cernekee .dapm_widgets = tas571x_dapm_widgets, 3553fd6e7d9SKevin Cernekee .num_dapm_widgets = ARRAY_SIZE(tas571x_dapm_widgets), 3563fd6e7d9SKevin Cernekee .dapm_routes = tas571x_dapm_routes, 3573fd6e7d9SKevin Cernekee .num_dapm_routes = ARRAY_SIZE(tas571x_dapm_routes), 3583fd6e7d9SKevin Cernekee }; 3593fd6e7d9SKevin Cernekee 3603fd6e7d9SKevin Cernekee static struct snd_soc_dai_driver tas571x_dai = { 3613fd6e7d9SKevin Cernekee .name = "tas571x-hifi", 3623fd6e7d9SKevin Cernekee .playback = { 3633fd6e7d9SKevin Cernekee .stream_name = "Playback", 3643fd6e7d9SKevin Cernekee .channels_min = 2, 3653fd6e7d9SKevin Cernekee .channels_max = 2, 3663fd6e7d9SKevin Cernekee .rates = SNDRV_PCM_RATE_8000_48000, 3673fd6e7d9SKevin Cernekee .formats = SNDRV_PCM_FMTBIT_S32_LE | 3683fd6e7d9SKevin Cernekee SNDRV_PCM_FMTBIT_S24_LE | 3693fd6e7d9SKevin Cernekee SNDRV_PCM_FMTBIT_S16_LE, 3703fd6e7d9SKevin Cernekee }, 3713fd6e7d9SKevin Cernekee .ops = &tas571x_dai_ops, 3723fd6e7d9SKevin Cernekee }; 3733fd6e7d9SKevin Cernekee 3743fd6e7d9SKevin Cernekee static const struct of_device_id tas571x_of_match[]; 3753fd6e7d9SKevin Cernekee 3763fd6e7d9SKevin Cernekee static int tas571x_i2c_probe(struct i2c_client *client, 3773fd6e7d9SKevin Cernekee const struct i2c_device_id *id) 3783fd6e7d9SKevin Cernekee { 3793fd6e7d9SKevin Cernekee struct tas571x_private *priv; 3803fd6e7d9SKevin Cernekee struct device *dev = &client->dev; 3813fd6e7d9SKevin Cernekee int i, ret; 3823fd6e7d9SKevin Cernekee 3833fd6e7d9SKevin Cernekee priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 3843fd6e7d9SKevin Cernekee if (!priv) 3853fd6e7d9SKevin Cernekee return -ENOMEM; 3863fd6e7d9SKevin Cernekee i2c_set_clientdata(client, priv); 3873fd6e7d9SKevin Cernekee 3883fd6e7d9SKevin Cernekee if (dev->of_node) { 3893fd6e7d9SKevin Cernekee const struct of_device_id *of_id; 3903fd6e7d9SKevin Cernekee 3913fd6e7d9SKevin Cernekee of_id = of_match_device(tas571x_of_match, dev); 3923fd6e7d9SKevin Cernekee if (of_id) 3933fd6e7d9SKevin Cernekee priv->chip = of_id->data; 3943fd6e7d9SKevin Cernekee } 3953fd6e7d9SKevin Cernekee 3963fd6e7d9SKevin Cernekee if (!priv->chip) { 3973fd6e7d9SKevin Cernekee dev_err(dev, "Unknown device type\n"); 3983fd6e7d9SKevin Cernekee return -EINVAL; 3993fd6e7d9SKevin Cernekee } 4003fd6e7d9SKevin Cernekee 4013fd6e7d9SKevin Cernekee priv->mclk = devm_clk_get(dev, "mclk"); 4023fd6e7d9SKevin Cernekee if (IS_ERR(priv->mclk) && PTR_ERR(priv->mclk) != -ENOENT) { 4033fd6e7d9SKevin Cernekee dev_err(dev, "Failed to request mclk: %ld\n", 4043fd6e7d9SKevin Cernekee PTR_ERR(priv->mclk)); 4053fd6e7d9SKevin Cernekee return PTR_ERR(priv->mclk); 4063fd6e7d9SKevin Cernekee } 4073fd6e7d9SKevin Cernekee 4083fd6e7d9SKevin Cernekee BUG_ON(priv->chip->num_supply_names > TAS571X_MAX_SUPPLIES); 4093fd6e7d9SKevin Cernekee for (i = 0; i < priv->chip->num_supply_names; i++) 4103fd6e7d9SKevin Cernekee priv->supplies[i].supply = priv->chip->supply_names[i]; 4113fd6e7d9SKevin Cernekee 4123fd6e7d9SKevin Cernekee ret = devm_regulator_bulk_get(dev, priv->chip->num_supply_names, 4133fd6e7d9SKevin Cernekee priv->supplies); 4143fd6e7d9SKevin Cernekee if (ret) { 4153fd6e7d9SKevin Cernekee dev_err(dev, "Failed to get supplies: %d\n", ret); 4163fd6e7d9SKevin Cernekee return ret; 4173fd6e7d9SKevin Cernekee } 4183fd6e7d9SKevin Cernekee ret = regulator_bulk_enable(priv->chip->num_supply_names, 4193fd6e7d9SKevin Cernekee priv->supplies); 4203fd6e7d9SKevin Cernekee if (ret) { 4213fd6e7d9SKevin Cernekee dev_err(dev, "Failed to enable supplies: %d\n", ret); 4223fd6e7d9SKevin Cernekee return ret; 4233fd6e7d9SKevin Cernekee } 4243fd6e7d9SKevin Cernekee 4253fd6e7d9SKevin Cernekee priv->regmap = devm_regmap_init(dev, NULL, client, 4263fd6e7d9SKevin Cernekee priv->chip->regmap_config); 4273fd6e7d9SKevin Cernekee if (IS_ERR(priv->regmap)) 4283fd6e7d9SKevin Cernekee return PTR_ERR(priv->regmap); 4293fd6e7d9SKevin Cernekee 4303fd6e7d9SKevin Cernekee priv->pdn_gpio = devm_gpiod_get_optional(dev, "pdn", GPIOD_OUT_LOW); 4313fd6e7d9SKevin Cernekee if (IS_ERR(priv->pdn_gpio)) { 4323fd6e7d9SKevin Cernekee dev_err(dev, "error requesting pdn_gpio: %ld\n", 4333fd6e7d9SKevin Cernekee PTR_ERR(priv->pdn_gpio)); 4343fd6e7d9SKevin Cernekee return PTR_ERR(priv->pdn_gpio); 4353fd6e7d9SKevin Cernekee } 4363fd6e7d9SKevin Cernekee 4373fd6e7d9SKevin Cernekee priv->reset_gpio = devm_gpiod_get_optional(dev, "reset", 4383fd6e7d9SKevin Cernekee GPIOD_OUT_HIGH); 4393fd6e7d9SKevin Cernekee if (IS_ERR(priv->reset_gpio)) { 4403fd6e7d9SKevin Cernekee dev_err(dev, "error requesting reset_gpio: %ld\n", 4413fd6e7d9SKevin Cernekee PTR_ERR(priv->reset_gpio)); 4423fd6e7d9SKevin Cernekee return PTR_ERR(priv->reset_gpio); 4433fd6e7d9SKevin Cernekee } else if (priv->reset_gpio) { 4443fd6e7d9SKevin Cernekee /* pulse the active low reset line for ~100us */ 4453fd6e7d9SKevin Cernekee usleep_range(100, 200); 4463fd6e7d9SKevin Cernekee gpiod_set_value(priv->reset_gpio, 0); 4473fd6e7d9SKevin Cernekee usleep_range(12000, 20000); 4483fd6e7d9SKevin Cernekee } 4493fd6e7d9SKevin Cernekee 4503fd6e7d9SKevin Cernekee ret = regmap_write(priv->regmap, TAS571X_OSC_TRIM_REG, 0); 4513fd6e7d9SKevin Cernekee if (ret) 4523fd6e7d9SKevin Cernekee return ret; 4533fd6e7d9SKevin Cernekee 4543fd6e7d9SKevin Cernekee ret = regmap_update_bits(priv->regmap, TAS571X_SYS_CTRL_2_REG, 4553fd6e7d9SKevin Cernekee TAS571X_SYS_CTRL_2_SDN_MASK, 0); 4563fd6e7d9SKevin Cernekee if (ret) 4573fd6e7d9SKevin Cernekee return ret; 4583fd6e7d9SKevin Cernekee 4593fd6e7d9SKevin Cernekee memcpy(&priv->codec_driver, &tas571x_codec, sizeof(priv->codec_driver)); 4603fd6e7d9SKevin Cernekee priv->codec_driver.controls = priv->chip->controls; 4613fd6e7d9SKevin Cernekee priv->codec_driver.num_controls = priv->chip->num_controls; 4623fd6e7d9SKevin Cernekee 4633fd6e7d9SKevin Cernekee if (priv->chip->vol_reg_size == 2) { 4643fd6e7d9SKevin Cernekee /* 4653fd6e7d9SKevin Cernekee * The master volume defaults to 0x3ff (mute), but we ignore 4663fd6e7d9SKevin Cernekee * (zero) the LSB because the hardware step size is 0.125 dB 4673fd6e7d9SKevin Cernekee * and TLV_DB_SCALE_ITEM has a resolution of 0.01 dB. 4683fd6e7d9SKevin Cernekee */ 4693fd6e7d9SKevin Cernekee ret = regmap_update_bits(priv->regmap, TAS571X_MVOL_REG, 1, 0); 4703fd6e7d9SKevin Cernekee if (ret) 4713fd6e7d9SKevin Cernekee return ret; 4723fd6e7d9SKevin Cernekee } 4733fd6e7d9SKevin Cernekee 4743fd6e7d9SKevin Cernekee regcache_cache_only(priv->regmap, true); 4753fd6e7d9SKevin Cernekee gpiod_set_value(priv->pdn_gpio, 1); 4763fd6e7d9SKevin Cernekee 4773fd6e7d9SKevin Cernekee return snd_soc_register_codec(&client->dev, &priv->codec_driver, 4783fd6e7d9SKevin Cernekee &tas571x_dai, 1); 4793fd6e7d9SKevin Cernekee } 4803fd6e7d9SKevin Cernekee 4813fd6e7d9SKevin Cernekee static int tas571x_i2c_remove(struct i2c_client *client) 4823fd6e7d9SKevin Cernekee { 4833fd6e7d9SKevin Cernekee struct tas571x_private *priv = i2c_get_clientdata(client); 4843fd6e7d9SKevin Cernekee 4853fd6e7d9SKevin Cernekee snd_soc_unregister_codec(&client->dev); 4863fd6e7d9SKevin Cernekee regulator_bulk_disable(priv->chip->num_supply_names, priv->supplies); 4873fd6e7d9SKevin Cernekee 4883fd6e7d9SKevin Cernekee return 0; 4893fd6e7d9SKevin Cernekee } 4903fd6e7d9SKevin Cernekee 4913fd6e7d9SKevin Cernekee static const struct of_device_id tas571x_of_match[] = { 4923fd6e7d9SKevin Cernekee { .compatible = "ti,tas5711", .data = &tas5711_chip, }, 4933fd6e7d9SKevin Cernekee { .compatible = "ti,tas5717", .data = &tas5717_chip, }, 4943fd6e7d9SKevin Cernekee { .compatible = "ti,tas5719", .data = &tas5717_chip, }, 4953fd6e7d9SKevin Cernekee { } 4963fd6e7d9SKevin Cernekee }; 4973fd6e7d9SKevin Cernekee MODULE_DEVICE_TABLE(of, tas571x_of_match); 4983fd6e7d9SKevin Cernekee 4993fd6e7d9SKevin Cernekee static const struct i2c_device_id tas571x_i2c_id[] = { 5003fd6e7d9SKevin Cernekee { "tas5711", 0 }, 5013fd6e7d9SKevin Cernekee { "tas5717", 0 }, 5023fd6e7d9SKevin Cernekee { "tas5719", 0 }, 5033fd6e7d9SKevin Cernekee { } 5043fd6e7d9SKevin Cernekee }; 5053fd6e7d9SKevin Cernekee MODULE_DEVICE_TABLE(i2c, tas571x_i2c_id); 5063fd6e7d9SKevin Cernekee 5073fd6e7d9SKevin Cernekee static struct i2c_driver tas571x_i2c_driver = { 5083fd6e7d9SKevin Cernekee .driver = { 5093fd6e7d9SKevin Cernekee .name = "tas571x", 5103fd6e7d9SKevin Cernekee .of_match_table = of_match_ptr(tas571x_of_match), 5113fd6e7d9SKevin Cernekee }, 5123fd6e7d9SKevin Cernekee .probe = tas571x_i2c_probe, 5133fd6e7d9SKevin Cernekee .remove = tas571x_i2c_remove, 5143fd6e7d9SKevin Cernekee .id_table = tas571x_i2c_id, 5153fd6e7d9SKevin Cernekee }; 5163fd6e7d9SKevin Cernekee module_i2c_driver(tas571x_i2c_driver); 5173fd6e7d9SKevin Cernekee 5183fd6e7d9SKevin Cernekee MODULE_DESCRIPTION("ASoC TAS571x driver"); 5193fd6e7d9SKevin Cernekee MODULE_AUTHOR("Kevin Cernekee <cernekee@chromium.org>"); 5203fd6e7d9SKevin Cernekee MODULE_LICENSE("GPL"); 521