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> 313fd6e7d9SKevin Cernekee 323fd6e7d9SKevin Cernekee #include "tas571x.h" 333fd6e7d9SKevin Cernekee 343fd6e7d9SKevin Cernekee #define TAS571X_MAX_SUPPLIES 6 353fd6e7d9SKevin Cernekee 363fd6e7d9SKevin Cernekee struct tas571x_chip { 373fd6e7d9SKevin Cernekee const char *const *supply_names; 383fd6e7d9SKevin Cernekee int num_supply_names; 393fd6e7d9SKevin Cernekee const struct snd_kcontrol_new *controls; 403fd6e7d9SKevin Cernekee int num_controls; 413fd6e7d9SKevin Cernekee const struct regmap_config *regmap_config; 423fd6e7d9SKevin Cernekee int vol_reg_size; 433fd6e7d9SKevin Cernekee }; 443fd6e7d9SKevin Cernekee 453fd6e7d9SKevin Cernekee struct tas571x_private { 463fd6e7d9SKevin Cernekee const struct tas571x_chip *chip; 473fd6e7d9SKevin Cernekee struct regmap *regmap; 483fd6e7d9SKevin Cernekee struct regulator_bulk_data supplies[TAS571X_MAX_SUPPLIES]; 493fd6e7d9SKevin Cernekee struct clk *mclk; 503fd6e7d9SKevin Cernekee unsigned int format; 513fd6e7d9SKevin Cernekee struct gpio_desc *reset_gpio; 523fd6e7d9SKevin Cernekee struct gpio_desc *pdn_gpio; 533fd6e7d9SKevin Cernekee struct snd_soc_codec_driver codec_driver; 543fd6e7d9SKevin Cernekee }; 553fd6e7d9SKevin Cernekee 563fd6e7d9SKevin Cernekee static int tas571x_register_size(struct tas571x_private *priv, unsigned int reg) 573fd6e7d9SKevin Cernekee { 583fd6e7d9SKevin Cernekee switch (reg) { 593fd6e7d9SKevin Cernekee case TAS571X_MVOL_REG: 603fd6e7d9SKevin Cernekee case TAS571X_CH1_VOL_REG: 613fd6e7d9SKevin Cernekee case TAS571X_CH2_VOL_REG: 623fd6e7d9SKevin Cernekee return priv->chip->vol_reg_size; 63a593ed09SPetr Kulhavy case TAS571X_INPUT_MUX_REG: 64a593ed09SPetr Kulhavy case TAS571X_CH4_SRC_SELECT_REG: 65a593ed09SPetr Kulhavy case TAS571X_PWM_MUX_REG: 66a593ed09SPetr Kulhavy return 4; 673fd6e7d9SKevin Cernekee default: 683fd6e7d9SKevin Cernekee return 1; 693fd6e7d9SKevin Cernekee } 703fd6e7d9SKevin Cernekee } 713fd6e7d9SKevin Cernekee 723fd6e7d9SKevin Cernekee static int tas571x_reg_write(void *context, unsigned int reg, 733fd6e7d9SKevin Cernekee unsigned int value) 743fd6e7d9SKevin Cernekee { 753fd6e7d9SKevin Cernekee struct i2c_client *client = context; 763fd6e7d9SKevin Cernekee struct tas571x_private *priv = i2c_get_clientdata(client); 773fd6e7d9SKevin Cernekee unsigned int i, size; 783fd6e7d9SKevin Cernekee uint8_t buf[5]; 793fd6e7d9SKevin Cernekee int ret; 803fd6e7d9SKevin Cernekee 813fd6e7d9SKevin Cernekee size = tas571x_register_size(priv, reg); 823fd6e7d9SKevin Cernekee buf[0] = reg; 833fd6e7d9SKevin Cernekee 843fd6e7d9SKevin Cernekee for (i = size; i >= 1; --i) { 853fd6e7d9SKevin Cernekee buf[i] = value; 863fd6e7d9SKevin Cernekee value >>= 8; 873fd6e7d9SKevin Cernekee } 883fd6e7d9SKevin Cernekee 893fd6e7d9SKevin Cernekee ret = i2c_master_send(client, buf, size + 1); 903fd6e7d9SKevin Cernekee if (ret == size + 1) 913fd6e7d9SKevin Cernekee return 0; 923fd6e7d9SKevin Cernekee else if (ret < 0) 933fd6e7d9SKevin Cernekee return ret; 943fd6e7d9SKevin Cernekee else 953fd6e7d9SKevin Cernekee return -EIO; 963fd6e7d9SKevin Cernekee } 973fd6e7d9SKevin Cernekee 983fd6e7d9SKevin Cernekee static int tas571x_reg_read(void *context, unsigned int reg, 993fd6e7d9SKevin Cernekee unsigned int *value) 1003fd6e7d9SKevin Cernekee { 1013fd6e7d9SKevin Cernekee struct i2c_client *client = context; 1023fd6e7d9SKevin Cernekee struct tas571x_private *priv = i2c_get_clientdata(client); 1033fd6e7d9SKevin Cernekee uint8_t send_buf, recv_buf[4]; 1043fd6e7d9SKevin Cernekee struct i2c_msg msgs[2]; 1053fd6e7d9SKevin Cernekee unsigned int size; 1063fd6e7d9SKevin Cernekee unsigned int i; 1073fd6e7d9SKevin Cernekee int ret; 1083fd6e7d9SKevin Cernekee 1093fd6e7d9SKevin Cernekee size = tas571x_register_size(priv, reg); 1103fd6e7d9SKevin Cernekee send_buf = reg; 1113fd6e7d9SKevin Cernekee 1123fd6e7d9SKevin Cernekee msgs[0].addr = client->addr; 1133fd6e7d9SKevin Cernekee msgs[0].len = sizeof(send_buf); 1143fd6e7d9SKevin Cernekee msgs[0].buf = &send_buf; 1153fd6e7d9SKevin Cernekee msgs[0].flags = 0; 1163fd6e7d9SKevin Cernekee 1173fd6e7d9SKevin Cernekee msgs[1].addr = client->addr; 1183fd6e7d9SKevin Cernekee msgs[1].len = size; 1193fd6e7d9SKevin Cernekee msgs[1].buf = recv_buf; 1203fd6e7d9SKevin Cernekee msgs[1].flags = I2C_M_RD; 1213fd6e7d9SKevin Cernekee 1223fd6e7d9SKevin Cernekee ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); 1233fd6e7d9SKevin Cernekee if (ret < 0) 1243fd6e7d9SKevin Cernekee return ret; 1253fd6e7d9SKevin Cernekee else if (ret != ARRAY_SIZE(msgs)) 1263fd6e7d9SKevin Cernekee return -EIO; 1273fd6e7d9SKevin Cernekee 1283fd6e7d9SKevin Cernekee *value = 0; 1293fd6e7d9SKevin Cernekee 1303fd6e7d9SKevin Cernekee for (i = 0; i < size; i++) { 1313fd6e7d9SKevin Cernekee *value <<= 8; 1323fd6e7d9SKevin Cernekee *value |= recv_buf[i]; 1333fd6e7d9SKevin Cernekee } 1343fd6e7d9SKevin Cernekee 1353fd6e7d9SKevin Cernekee return 0; 1363fd6e7d9SKevin Cernekee } 1373fd6e7d9SKevin Cernekee 1383fd6e7d9SKevin Cernekee static int tas571x_set_dai_fmt(struct snd_soc_dai *dai, unsigned int format) 1393fd6e7d9SKevin Cernekee { 1403fd6e7d9SKevin Cernekee struct tas571x_private *priv = snd_soc_codec_get_drvdata(dai->codec); 1413fd6e7d9SKevin Cernekee 1423fd6e7d9SKevin Cernekee priv->format = format; 1433fd6e7d9SKevin Cernekee 1443fd6e7d9SKevin Cernekee return 0; 1453fd6e7d9SKevin Cernekee } 1463fd6e7d9SKevin Cernekee 1473fd6e7d9SKevin Cernekee static int tas571x_hw_params(struct snd_pcm_substream *substream, 1483fd6e7d9SKevin Cernekee struct snd_pcm_hw_params *params, 1493fd6e7d9SKevin Cernekee struct snd_soc_dai *dai) 1503fd6e7d9SKevin Cernekee { 1513fd6e7d9SKevin Cernekee struct tas571x_private *priv = snd_soc_codec_get_drvdata(dai->codec); 1523fd6e7d9SKevin Cernekee u32 val; 1533fd6e7d9SKevin Cernekee 1543fd6e7d9SKevin Cernekee switch (priv->format & SND_SOC_DAIFMT_FORMAT_MASK) { 1553fd6e7d9SKevin Cernekee case SND_SOC_DAIFMT_RIGHT_J: 1563fd6e7d9SKevin Cernekee val = 0x00; 1573fd6e7d9SKevin Cernekee break; 1583fd6e7d9SKevin Cernekee case SND_SOC_DAIFMT_I2S: 1593fd6e7d9SKevin Cernekee val = 0x03; 1603fd6e7d9SKevin Cernekee break; 1613fd6e7d9SKevin Cernekee case SND_SOC_DAIFMT_LEFT_J: 1623fd6e7d9SKevin Cernekee val = 0x06; 1633fd6e7d9SKevin Cernekee break; 1643fd6e7d9SKevin Cernekee default: 1653fd6e7d9SKevin Cernekee return -EINVAL; 1663fd6e7d9SKevin Cernekee } 1673fd6e7d9SKevin Cernekee 1683fd6e7d9SKevin Cernekee if (params_width(params) >= 24) 1693fd6e7d9SKevin Cernekee val += 2; 1703fd6e7d9SKevin Cernekee else if (params_width(params) >= 20) 1713fd6e7d9SKevin Cernekee val += 1; 1723fd6e7d9SKevin Cernekee 1733fd6e7d9SKevin Cernekee return regmap_update_bits(priv->regmap, TAS571X_SDI_REG, 1743fd6e7d9SKevin Cernekee TAS571X_SDI_FMT_MASK, val); 1753fd6e7d9SKevin Cernekee } 1763fd6e7d9SKevin Cernekee 17704004850SPetr Kulhavy static int tas571x_mute(struct snd_soc_dai *dai, int mute) 17804004850SPetr Kulhavy { 17904004850SPetr Kulhavy struct snd_soc_codec *codec = dai->codec; 18004004850SPetr Kulhavy u8 sysctl2; 18104004850SPetr Kulhavy int ret; 18204004850SPetr Kulhavy 18304004850SPetr Kulhavy sysctl2 = mute ? TAS571X_SYS_CTRL_2_SDN_MASK : 0; 18404004850SPetr Kulhavy 18504004850SPetr Kulhavy ret = snd_soc_update_bits(codec, 18604004850SPetr Kulhavy TAS571X_SYS_CTRL_2_REG, 18704004850SPetr Kulhavy TAS571X_SYS_CTRL_2_SDN_MASK, 18804004850SPetr Kulhavy sysctl2); 18904004850SPetr Kulhavy usleep_range(1000, 2000); 19004004850SPetr Kulhavy 19104004850SPetr Kulhavy return ret; 19204004850SPetr Kulhavy } 19304004850SPetr Kulhavy 1943fd6e7d9SKevin Cernekee static int tas571x_set_bias_level(struct snd_soc_codec *codec, 1953fd6e7d9SKevin Cernekee enum snd_soc_bias_level level) 1963fd6e7d9SKevin Cernekee { 1973fd6e7d9SKevin Cernekee struct tas571x_private *priv = snd_soc_codec_get_drvdata(codec); 1983fd6e7d9SKevin Cernekee int ret; 1993fd6e7d9SKevin Cernekee 2003fd6e7d9SKevin Cernekee switch (level) { 2013fd6e7d9SKevin Cernekee case SND_SOC_BIAS_ON: 2023fd6e7d9SKevin Cernekee break; 2033fd6e7d9SKevin Cernekee case SND_SOC_BIAS_PREPARE: 2043fd6e7d9SKevin Cernekee break; 2053fd6e7d9SKevin Cernekee case SND_SOC_BIAS_STANDBY: 2068f218fa9SLars-Peter Clausen if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { 2073fd6e7d9SKevin Cernekee if (!IS_ERR(priv->mclk)) { 2083fd6e7d9SKevin Cernekee ret = clk_prepare_enable(priv->mclk); 2093fd6e7d9SKevin Cernekee if (ret) { 2103fd6e7d9SKevin Cernekee dev_err(codec->dev, 2113fd6e7d9SKevin Cernekee "Failed to enable master clock: %d\n", 2123fd6e7d9SKevin Cernekee ret); 2133fd6e7d9SKevin Cernekee return ret; 2143fd6e7d9SKevin Cernekee } 2153fd6e7d9SKevin Cernekee } 2163fd6e7d9SKevin Cernekee 2173fd6e7d9SKevin Cernekee gpiod_set_value(priv->pdn_gpio, 0); 2183fd6e7d9SKevin Cernekee usleep_range(5000, 6000); 2193fd6e7d9SKevin Cernekee 2203fd6e7d9SKevin Cernekee regcache_cache_only(priv->regmap, false); 2213fd6e7d9SKevin Cernekee ret = regcache_sync(priv->regmap); 2223fd6e7d9SKevin Cernekee if (ret) 2233fd6e7d9SKevin Cernekee return ret; 2243fd6e7d9SKevin Cernekee } 2253fd6e7d9SKevin Cernekee break; 2263fd6e7d9SKevin Cernekee case SND_SOC_BIAS_OFF: 2273fd6e7d9SKevin Cernekee regcache_cache_only(priv->regmap, true); 2283fd6e7d9SKevin Cernekee gpiod_set_value(priv->pdn_gpio, 1); 2293fd6e7d9SKevin Cernekee 2303fd6e7d9SKevin Cernekee if (!IS_ERR(priv->mclk)) 2313fd6e7d9SKevin Cernekee clk_disable_unprepare(priv->mclk); 2323fd6e7d9SKevin Cernekee break; 2333fd6e7d9SKevin Cernekee } 2343fd6e7d9SKevin Cernekee 2353fd6e7d9SKevin Cernekee return 0; 2363fd6e7d9SKevin Cernekee } 2373fd6e7d9SKevin Cernekee 2383fd6e7d9SKevin Cernekee static const struct snd_soc_dai_ops tas571x_dai_ops = { 2393fd6e7d9SKevin Cernekee .set_fmt = tas571x_set_dai_fmt, 2403fd6e7d9SKevin Cernekee .hw_params = tas571x_hw_params, 24104004850SPetr Kulhavy .digital_mute = tas571x_mute, 2423fd6e7d9SKevin Cernekee }; 2433fd6e7d9SKevin Cernekee 2443fd6e7d9SKevin Cernekee static const char *const tas5711_supply_names[] = { 2453fd6e7d9SKevin Cernekee "AVDD", 2463fd6e7d9SKevin Cernekee "DVDD", 2473fd6e7d9SKevin Cernekee "PVDD_A", 2483fd6e7d9SKevin Cernekee "PVDD_B", 2493fd6e7d9SKevin Cernekee "PVDD_C", 2503fd6e7d9SKevin Cernekee "PVDD_D", 2513fd6e7d9SKevin Cernekee }; 2523fd6e7d9SKevin Cernekee 2533fd6e7d9SKevin Cernekee static const DECLARE_TLV_DB_SCALE(tas5711_volume_tlv, -10350, 50, 1); 2543fd6e7d9SKevin Cernekee 2553fd6e7d9SKevin Cernekee static const struct snd_kcontrol_new tas5711_controls[] = { 2563fd6e7d9SKevin Cernekee SOC_SINGLE_TLV("Master Volume", 2573fd6e7d9SKevin Cernekee TAS571X_MVOL_REG, 2583fd6e7d9SKevin Cernekee 0, 0xff, 1, tas5711_volume_tlv), 2593fd6e7d9SKevin Cernekee SOC_DOUBLE_R_TLV("Speaker Volume", 2603fd6e7d9SKevin Cernekee TAS571X_CH1_VOL_REG, 2613fd6e7d9SKevin Cernekee TAS571X_CH2_VOL_REG, 2623fd6e7d9SKevin Cernekee 0, 0xff, 1, tas5711_volume_tlv), 2633fd6e7d9SKevin Cernekee SOC_DOUBLE("Speaker Switch", 2643fd6e7d9SKevin Cernekee TAS571X_SOFT_MUTE_REG, 2653fd6e7d9SKevin Cernekee TAS571X_SOFT_MUTE_CH1_SHIFT, TAS571X_SOFT_MUTE_CH2_SHIFT, 2663fd6e7d9SKevin Cernekee 1, 1), 2673fd6e7d9SKevin Cernekee }; 2683fd6e7d9SKevin Cernekee 269a593ed09SPetr Kulhavy static const struct regmap_range tas571x_readonly_regs_range[] = { 270a593ed09SPetr Kulhavy regmap_reg_range(TAS571X_CLK_CTRL_REG, TAS571X_DEV_ID_REG), 271a593ed09SPetr Kulhavy }; 272a593ed09SPetr Kulhavy 273a593ed09SPetr Kulhavy static const struct regmap_range tas571x_volatile_regs_range[] = { 274a593ed09SPetr Kulhavy regmap_reg_range(TAS571X_CLK_CTRL_REG, TAS571X_ERR_STATUS_REG), 275a593ed09SPetr Kulhavy regmap_reg_range(TAS571X_OSC_TRIM_REG, TAS571X_OSC_TRIM_REG), 276a593ed09SPetr Kulhavy }; 277a593ed09SPetr Kulhavy 278a593ed09SPetr Kulhavy static const struct regmap_access_table tas571x_write_regs = { 279a593ed09SPetr Kulhavy .no_ranges = tas571x_readonly_regs_range, 280a593ed09SPetr Kulhavy .n_no_ranges = ARRAY_SIZE(tas571x_readonly_regs_range), 281a593ed09SPetr Kulhavy }; 282a593ed09SPetr Kulhavy 283a593ed09SPetr Kulhavy static const struct regmap_access_table tas571x_volatile_regs = { 284a593ed09SPetr Kulhavy .yes_ranges = tas571x_volatile_regs_range, 285a593ed09SPetr Kulhavy .n_yes_ranges = ARRAY_SIZE(tas571x_volatile_regs_range), 286a593ed09SPetr Kulhavy 287a593ed09SPetr Kulhavy }; 288a593ed09SPetr Kulhavy 2893fd6e7d9SKevin Cernekee static const struct reg_default tas5711_reg_defaults[] = { 2903fd6e7d9SKevin Cernekee { 0x04, 0x05 }, 2913fd6e7d9SKevin Cernekee { 0x05, 0x40 }, 2923fd6e7d9SKevin Cernekee { 0x06, 0x00 }, 2933fd6e7d9SKevin Cernekee { 0x07, 0xff }, 2943fd6e7d9SKevin Cernekee { 0x08, 0x30 }, 2953fd6e7d9SKevin Cernekee { 0x09, 0x30 }, 2963fd6e7d9SKevin Cernekee { 0x1b, 0x82 }, 2973fd6e7d9SKevin Cernekee }; 2983fd6e7d9SKevin Cernekee 2993fd6e7d9SKevin Cernekee static const struct regmap_config tas5711_regmap_config = { 3003fd6e7d9SKevin Cernekee .reg_bits = 8, 3013fd6e7d9SKevin Cernekee .val_bits = 32, 3023fd6e7d9SKevin Cernekee .max_register = 0xff, 3033fd6e7d9SKevin Cernekee .reg_read = tas571x_reg_read, 3043fd6e7d9SKevin Cernekee .reg_write = tas571x_reg_write, 3053fd6e7d9SKevin Cernekee .reg_defaults = tas5711_reg_defaults, 3063fd6e7d9SKevin Cernekee .num_reg_defaults = ARRAY_SIZE(tas5711_reg_defaults), 3073fd6e7d9SKevin Cernekee .cache_type = REGCACHE_RBTREE, 308a593ed09SPetr Kulhavy .wr_table = &tas571x_write_regs, 309a593ed09SPetr Kulhavy .volatile_table = &tas571x_volatile_regs, 3103fd6e7d9SKevin Cernekee }; 3113fd6e7d9SKevin Cernekee 3123fd6e7d9SKevin Cernekee static const struct tas571x_chip tas5711_chip = { 3133fd6e7d9SKevin Cernekee .supply_names = tas5711_supply_names, 3143fd6e7d9SKevin Cernekee .num_supply_names = ARRAY_SIZE(tas5711_supply_names), 3153fd6e7d9SKevin Cernekee .controls = tas5711_controls, 3163fd6e7d9SKevin Cernekee .num_controls = ARRAY_SIZE(tas5711_controls), 3173fd6e7d9SKevin Cernekee .regmap_config = &tas5711_regmap_config, 3183fd6e7d9SKevin Cernekee .vol_reg_size = 1, 3193fd6e7d9SKevin Cernekee }; 3203fd6e7d9SKevin Cernekee 3213fd6e7d9SKevin Cernekee static const char *const tas5717_supply_names[] = { 3223fd6e7d9SKevin Cernekee "AVDD", 3233fd6e7d9SKevin Cernekee "DVDD", 3243fd6e7d9SKevin Cernekee "HPVDD", 3253fd6e7d9SKevin Cernekee "PVDD_AB", 3263fd6e7d9SKevin Cernekee "PVDD_CD", 3273fd6e7d9SKevin Cernekee }; 3283fd6e7d9SKevin Cernekee 3293fd6e7d9SKevin Cernekee static const DECLARE_TLV_DB_SCALE(tas5717_volume_tlv, -10375, 25, 0); 3303fd6e7d9SKevin Cernekee 3313fd6e7d9SKevin Cernekee static const struct snd_kcontrol_new tas5717_controls[] = { 3323fd6e7d9SKevin Cernekee /* MVOL LSB is ignored - see comments in tas571x_i2c_probe() */ 3333fd6e7d9SKevin Cernekee SOC_SINGLE_TLV("Master Volume", 3343fd6e7d9SKevin Cernekee TAS571X_MVOL_REG, 1, 0x1ff, 1, 3353fd6e7d9SKevin Cernekee tas5717_volume_tlv), 3363fd6e7d9SKevin Cernekee SOC_DOUBLE_R_TLV("Speaker Volume", 3373fd6e7d9SKevin Cernekee TAS571X_CH1_VOL_REG, TAS571X_CH2_VOL_REG, 3383fd6e7d9SKevin Cernekee 1, 0x1ff, 1, tas5717_volume_tlv), 3393fd6e7d9SKevin Cernekee SOC_DOUBLE("Speaker Switch", 3403fd6e7d9SKevin Cernekee TAS571X_SOFT_MUTE_REG, 3413fd6e7d9SKevin Cernekee TAS571X_SOFT_MUTE_CH1_SHIFT, TAS571X_SOFT_MUTE_CH2_SHIFT, 3423fd6e7d9SKevin Cernekee 1, 1), 3433fd6e7d9SKevin Cernekee }; 3443fd6e7d9SKevin Cernekee 3453fd6e7d9SKevin Cernekee static const struct reg_default tas5717_reg_defaults[] = { 3463fd6e7d9SKevin Cernekee { 0x04, 0x05 }, 3473fd6e7d9SKevin Cernekee { 0x05, 0x40 }, 3483fd6e7d9SKevin Cernekee { 0x06, 0x00 }, 3493fd6e7d9SKevin Cernekee { 0x07, 0x03ff }, 3503fd6e7d9SKevin Cernekee { 0x08, 0x00c0 }, 3513fd6e7d9SKevin Cernekee { 0x09, 0x00c0 }, 3523fd6e7d9SKevin Cernekee { 0x1b, 0x82 }, 3533fd6e7d9SKevin Cernekee }; 3543fd6e7d9SKevin Cernekee 3553fd6e7d9SKevin Cernekee static const struct regmap_config tas5717_regmap_config = { 3563fd6e7d9SKevin Cernekee .reg_bits = 8, 3573fd6e7d9SKevin Cernekee .val_bits = 32, 3583fd6e7d9SKevin Cernekee .max_register = 0xff, 3593fd6e7d9SKevin Cernekee .reg_read = tas571x_reg_read, 3603fd6e7d9SKevin Cernekee .reg_write = tas571x_reg_write, 3613fd6e7d9SKevin Cernekee .reg_defaults = tas5717_reg_defaults, 3623fd6e7d9SKevin Cernekee .num_reg_defaults = ARRAY_SIZE(tas5717_reg_defaults), 3633fd6e7d9SKevin Cernekee .cache_type = REGCACHE_RBTREE, 364a593ed09SPetr Kulhavy .wr_table = &tas571x_write_regs, 365a593ed09SPetr Kulhavy .volatile_table = &tas571x_volatile_regs, 3663fd6e7d9SKevin Cernekee }; 3673fd6e7d9SKevin Cernekee 3683fd6e7d9SKevin Cernekee /* This entry is reused for tas5719 as the software interface is identical. */ 3693fd6e7d9SKevin Cernekee static const struct tas571x_chip tas5717_chip = { 3703fd6e7d9SKevin Cernekee .supply_names = tas5717_supply_names, 3713fd6e7d9SKevin Cernekee .num_supply_names = ARRAY_SIZE(tas5717_supply_names), 3723fd6e7d9SKevin Cernekee .controls = tas5717_controls, 3733fd6e7d9SKevin Cernekee .num_controls = ARRAY_SIZE(tas5717_controls), 3743fd6e7d9SKevin Cernekee .regmap_config = &tas5717_regmap_config, 3753fd6e7d9SKevin Cernekee .vol_reg_size = 2, 3763fd6e7d9SKevin Cernekee }; 3773fd6e7d9SKevin Cernekee 37823a282c4SPetr Kulhavy static const char *const tas5721_supply_names[] = { 37923a282c4SPetr Kulhavy "AVDD", 38023a282c4SPetr Kulhavy "DVDD", 38123a282c4SPetr Kulhavy "DRVDD", 38223a282c4SPetr Kulhavy "PVDD", 38323a282c4SPetr Kulhavy }; 38423a282c4SPetr Kulhavy 38523a282c4SPetr Kulhavy static const struct snd_kcontrol_new tas5721_controls[] = { 38623a282c4SPetr Kulhavy SOC_SINGLE_TLV("Master Volume", 38723a282c4SPetr Kulhavy TAS571X_MVOL_REG, 38823a282c4SPetr Kulhavy 0, 0xff, 1, tas5711_volume_tlv), 38923a282c4SPetr Kulhavy SOC_DOUBLE_R_TLV("Speaker Volume", 39023a282c4SPetr Kulhavy TAS571X_CH1_VOL_REG, 39123a282c4SPetr Kulhavy TAS571X_CH2_VOL_REG, 39223a282c4SPetr Kulhavy 0, 0xff, 1, tas5711_volume_tlv), 39323a282c4SPetr Kulhavy SOC_DOUBLE("Speaker Switch", 39423a282c4SPetr Kulhavy TAS571X_SOFT_MUTE_REG, 39523a282c4SPetr Kulhavy TAS571X_SOFT_MUTE_CH1_SHIFT, TAS571X_SOFT_MUTE_CH2_SHIFT, 39623a282c4SPetr Kulhavy 1, 1), 39723a282c4SPetr Kulhavy }; 39823a282c4SPetr Kulhavy 39923a282c4SPetr Kulhavy static const struct reg_default tas5721_reg_defaults[] = { 40023a282c4SPetr Kulhavy {TAS571X_CLK_CTRL_REG, 0x6c}, 40123a282c4SPetr Kulhavy {TAS571X_DEV_ID_REG, 0x00}, 40223a282c4SPetr Kulhavy {TAS571X_ERR_STATUS_REG, 0x00}, 40323a282c4SPetr Kulhavy {TAS571X_SYS_CTRL_1_REG, 0xa0}, 40423a282c4SPetr Kulhavy {TAS571X_SDI_REG, 0x05}, 40523a282c4SPetr Kulhavy {TAS571X_SYS_CTRL_2_REG, 0x40}, 40623a282c4SPetr Kulhavy {TAS571X_SOFT_MUTE_REG, 0x00}, 40723a282c4SPetr Kulhavy {TAS571X_MVOL_REG, 0xff}, 40823a282c4SPetr Kulhavy {TAS571X_CH1_VOL_REG, 0x30}, 40923a282c4SPetr Kulhavy {TAS571X_CH2_VOL_REG, 0x30}, 41023a282c4SPetr Kulhavy {TAS571X_CH3_VOL_REG, 0x30}, 41123a282c4SPetr Kulhavy {TAS571X_VOL_CFG_REG, 0x91}, 41223a282c4SPetr Kulhavy {TAS571X_MODULATION_LIMIT_REG, 0x02}, 41323a282c4SPetr Kulhavy {TAS571X_IC_DELAY_CH1_REG, 0xac}, 41423a282c4SPetr Kulhavy {TAS571X_IC_DELAY_CH2_REG, 0x54}, 41523a282c4SPetr Kulhavy {TAS571X_IC_DELAY_CH3_REG, 0xac}, 41623a282c4SPetr Kulhavy {TAS571X_IC_DELAY_CH4_REG, 0x54}, 41723a282c4SPetr Kulhavy {TAS571X_PWM_CH_SDN_GROUP_REG, 0x30}, 41823a282c4SPetr Kulhavy {TAS571X_START_STOP_PERIOD_REG, 0x0f}, 41923a282c4SPetr Kulhavy {TAS571X_OSC_TRIM_REG, 0x82}, 42023a282c4SPetr Kulhavy {TAS571X_BKND_ERR_REG, 0x02}, 42123a282c4SPetr Kulhavy {TAS571X_INPUT_MUX_REG, 0x17772}, 42223a282c4SPetr Kulhavy {TAS571X_CH4_SRC_SELECT_REG, 0x4303}, 42323a282c4SPetr Kulhavy {TAS571X_PWM_MUX_REG, 0x1021345}, 42423a282c4SPetr Kulhavy }; 42523a282c4SPetr Kulhavy 42623a282c4SPetr Kulhavy static const struct regmap_config tas5721_regmap_config = { 42723a282c4SPetr Kulhavy .reg_bits = 8, 42823a282c4SPetr Kulhavy .val_bits = 32, 42923a282c4SPetr Kulhavy .max_register = 0xff, 43023a282c4SPetr Kulhavy .reg_read = tas571x_reg_read, 43123a282c4SPetr Kulhavy .reg_write = tas571x_reg_write, 43223a282c4SPetr Kulhavy .reg_defaults = tas5721_reg_defaults, 43323a282c4SPetr Kulhavy .num_reg_defaults = ARRAY_SIZE(tas5721_reg_defaults), 43423a282c4SPetr Kulhavy .cache_type = REGCACHE_RBTREE, 43523a282c4SPetr Kulhavy .wr_table = &tas571x_write_regs, 43623a282c4SPetr Kulhavy .volatile_table = &tas571x_volatile_regs, 43723a282c4SPetr Kulhavy }; 43823a282c4SPetr Kulhavy 43923a282c4SPetr Kulhavy 44023a282c4SPetr Kulhavy static const struct tas571x_chip tas5721_chip = { 44123a282c4SPetr Kulhavy .supply_names = tas5721_supply_names, 44223a282c4SPetr Kulhavy .num_supply_names = ARRAY_SIZE(tas5721_supply_names), 44323a282c4SPetr Kulhavy .controls = tas5711_controls, 44423a282c4SPetr Kulhavy .num_controls = ARRAY_SIZE(tas5711_controls), 44523a282c4SPetr Kulhavy .regmap_config = &tas5721_regmap_config, 44623a282c4SPetr Kulhavy .vol_reg_size = 1, 44723a282c4SPetr Kulhavy }; 44823a282c4SPetr Kulhavy 4493fd6e7d9SKevin Cernekee static const struct snd_soc_dapm_widget tas571x_dapm_widgets[] = { 4503fd6e7d9SKevin Cernekee SND_SOC_DAPM_DAC("DACL", NULL, SND_SOC_NOPM, 0, 0), 4513fd6e7d9SKevin Cernekee SND_SOC_DAPM_DAC("DACR", NULL, SND_SOC_NOPM, 0, 0), 4523fd6e7d9SKevin Cernekee 4533fd6e7d9SKevin Cernekee SND_SOC_DAPM_OUTPUT("OUT_A"), 4543fd6e7d9SKevin Cernekee SND_SOC_DAPM_OUTPUT("OUT_B"), 4553fd6e7d9SKevin Cernekee SND_SOC_DAPM_OUTPUT("OUT_C"), 4563fd6e7d9SKevin Cernekee SND_SOC_DAPM_OUTPUT("OUT_D"), 4573fd6e7d9SKevin Cernekee }; 4583fd6e7d9SKevin Cernekee 4593fd6e7d9SKevin Cernekee static const struct snd_soc_dapm_route tas571x_dapm_routes[] = { 4603fd6e7d9SKevin Cernekee { "DACL", NULL, "Playback" }, 4613fd6e7d9SKevin Cernekee { "DACR", NULL, "Playback" }, 4623fd6e7d9SKevin Cernekee 4633fd6e7d9SKevin Cernekee { "OUT_A", NULL, "DACL" }, 4643fd6e7d9SKevin Cernekee { "OUT_B", NULL, "DACL" }, 4653fd6e7d9SKevin Cernekee { "OUT_C", NULL, "DACR" }, 4663fd6e7d9SKevin Cernekee { "OUT_D", NULL, "DACR" }, 4673fd6e7d9SKevin Cernekee }; 4683fd6e7d9SKevin Cernekee 4693fd6e7d9SKevin Cernekee static const struct snd_soc_codec_driver tas571x_codec = { 4703fd6e7d9SKevin Cernekee .set_bias_level = tas571x_set_bias_level, 4713fd6e7d9SKevin Cernekee .idle_bias_off = true, 4723fd6e7d9SKevin Cernekee 4733fd6e7d9SKevin Cernekee .dapm_widgets = tas571x_dapm_widgets, 4743fd6e7d9SKevin Cernekee .num_dapm_widgets = ARRAY_SIZE(tas571x_dapm_widgets), 4753fd6e7d9SKevin Cernekee .dapm_routes = tas571x_dapm_routes, 4763fd6e7d9SKevin Cernekee .num_dapm_routes = ARRAY_SIZE(tas571x_dapm_routes), 4773fd6e7d9SKevin Cernekee }; 4783fd6e7d9SKevin Cernekee 4793fd6e7d9SKevin Cernekee static struct snd_soc_dai_driver tas571x_dai = { 4803fd6e7d9SKevin Cernekee .name = "tas571x-hifi", 4813fd6e7d9SKevin Cernekee .playback = { 4823fd6e7d9SKevin Cernekee .stream_name = "Playback", 4833fd6e7d9SKevin Cernekee .channels_min = 2, 4843fd6e7d9SKevin Cernekee .channels_max = 2, 4853fd6e7d9SKevin Cernekee .rates = SNDRV_PCM_RATE_8000_48000, 4863fd6e7d9SKevin Cernekee .formats = SNDRV_PCM_FMTBIT_S32_LE | 4873fd6e7d9SKevin Cernekee SNDRV_PCM_FMTBIT_S24_LE | 4883fd6e7d9SKevin Cernekee SNDRV_PCM_FMTBIT_S16_LE, 4893fd6e7d9SKevin Cernekee }, 4903fd6e7d9SKevin Cernekee .ops = &tas571x_dai_ops, 4913fd6e7d9SKevin Cernekee }; 4923fd6e7d9SKevin Cernekee 4933fd6e7d9SKevin Cernekee static const struct of_device_id tas571x_of_match[]; 4943fd6e7d9SKevin Cernekee 4953fd6e7d9SKevin Cernekee static int tas571x_i2c_probe(struct i2c_client *client, 4963fd6e7d9SKevin Cernekee const struct i2c_device_id *id) 4973fd6e7d9SKevin Cernekee { 4983fd6e7d9SKevin Cernekee struct tas571x_private *priv; 4993fd6e7d9SKevin Cernekee struct device *dev = &client->dev; 50097fceb4dSKevin Cernekee const struct of_device_id *of_id; 5013fd6e7d9SKevin Cernekee int i, ret; 5023fd6e7d9SKevin Cernekee 5033fd6e7d9SKevin Cernekee priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 5043fd6e7d9SKevin Cernekee if (!priv) 5053fd6e7d9SKevin Cernekee return -ENOMEM; 5063fd6e7d9SKevin Cernekee i2c_set_clientdata(client, priv); 5073fd6e7d9SKevin Cernekee 5083fd6e7d9SKevin Cernekee of_id = of_match_device(tas571x_of_match, dev); 509630e413dSPetr Kulhavy if (of_id) 51097fceb4dSKevin Cernekee priv->chip = of_id->data; 511630e413dSPetr Kulhavy else 512630e413dSPetr Kulhavy priv->chip = (void *) id->driver_data; 5133fd6e7d9SKevin Cernekee 5143fd6e7d9SKevin Cernekee priv->mclk = devm_clk_get(dev, "mclk"); 5153fd6e7d9SKevin Cernekee if (IS_ERR(priv->mclk) && PTR_ERR(priv->mclk) != -ENOENT) { 5163fd6e7d9SKevin Cernekee dev_err(dev, "Failed to request mclk: %ld\n", 5173fd6e7d9SKevin Cernekee PTR_ERR(priv->mclk)); 5183fd6e7d9SKevin Cernekee return PTR_ERR(priv->mclk); 5193fd6e7d9SKevin Cernekee } 5203fd6e7d9SKevin Cernekee 5213fd6e7d9SKevin Cernekee BUG_ON(priv->chip->num_supply_names > TAS571X_MAX_SUPPLIES); 5223fd6e7d9SKevin Cernekee for (i = 0; i < priv->chip->num_supply_names; i++) 5233fd6e7d9SKevin Cernekee priv->supplies[i].supply = priv->chip->supply_names[i]; 5243fd6e7d9SKevin Cernekee 5253fd6e7d9SKevin Cernekee ret = devm_regulator_bulk_get(dev, priv->chip->num_supply_names, 5263fd6e7d9SKevin Cernekee priv->supplies); 5273fd6e7d9SKevin Cernekee if (ret) { 5283fd6e7d9SKevin Cernekee dev_err(dev, "Failed to get supplies: %d\n", ret); 5293fd6e7d9SKevin Cernekee return ret; 5303fd6e7d9SKevin Cernekee } 5313fd6e7d9SKevin Cernekee ret = regulator_bulk_enable(priv->chip->num_supply_names, 5323fd6e7d9SKevin Cernekee priv->supplies); 5333fd6e7d9SKevin Cernekee if (ret) { 5343fd6e7d9SKevin Cernekee dev_err(dev, "Failed to enable supplies: %d\n", ret); 5353fd6e7d9SKevin Cernekee return ret; 5363fd6e7d9SKevin Cernekee } 5373fd6e7d9SKevin Cernekee 5383fd6e7d9SKevin Cernekee priv->regmap = devm_regmap_init(dev, NULL, client, 5393fd6e7d9SKevin Cernekee priv->chip->regmap_config); 5403fd6e7d9SKevin Cernekee if (IS_ERR(priv->regmap)) 5413fd6e7d9SKevin Cernekee return PTR_ERR(priv->regmap); 5423fd6e7d9SKevin Cernekee 5433fd6e7d9SKevin Cernekee priv->pdn_gpio = devm_gpiod_get_optional(dev, "pdn", GPIOD_OUT_LOW); 5443fd6e7d9SKevin Cernekee if (IS_ERR(priv->pdn_gpio)) { 5453fd6e7d9SKevin Cernekee dev_err(dev, "error requesting pdn_gpio: %ld\n", 5463fd6e7d9SKevin Cernekee PTR_ERR(priv->pdn_gpio)); 5473fd6e7d9SKevin Cernekee return PTR_ERR(priv->pdn_gpio); 5483fd6e7d9SKevin Cernekee } 5493fd6e7d9SKevin Cernekee 5503fd6e7d9SKevin Cernekee priv->reset_gpio = devm_gpiod_get_optional(dev, "reset", 5513fd6e7d9SKevin Cernekee GPIOD_OUT_HIGH); 5523fd6e7d9SKevin Cernekee if (IS_ERR(priv->reset_gpio)) { 5533fd6e7d9SKevin Cernekee dev_err(dev, "error requesting reset_gpio: %ld\n", 5543fd6e7d9SKevin Cernekee PTR_ERR(priv->reset_gpio)); 5553fd6e7d9SKevin Cernekee return PTR_ERR(priv->reset_gpio); 5563fd6e7d9SKevin Cernekee } else if (priv->reset_gpio) { 5573fd6e7d9SKevin Cernekee /* pulse the active low reset line for ~100us */ 5583fd6e7d9SKevin Cernekee usleep_range(100, 200); 5593fd6e7d9SKevin Cernekee gpiod_set_value(priv->reset_gpio, 0); 5603fd6e7d9SKevin Cernekee usleep_range(12000, 20000); 5613fd6e7d9SKevin Cernekee } 5623fd6e7d9SKevin Cernekee 5633fd6e7d9SKevin Cernekee ret = regmap_write(priv->regmap, TAS571X_OSC_TRIM_REG, 0); 5643fd6e7d9SKevin Cernekee if (ret) 5653fd6e7d9SKevin Cernekee return ret; 5663fd6e7d9SKevin Cernekee 5673fd6e7d9SKevin Cernekee 5683fd6e7d9SKevin Cernekee memcpy(&priv->codec_driver, &tas571x_codec, sizeof(priv->codec_driver)); 5693fd6e7d9SKevin Cernekee priv->codec_driver.controls = priv->chip->controls; 5703fd6e7d9SKevin Cernekee priv->codec_driver.num_controls = priv->chip->num_controls; 5713fd6e7d9SKevin Cernekee 5723fd6e7d9SKevin Cernekee if (priv->chip->vol_reg_size == 2) { 5733fd6e7d9SKevin Cernekee /* 5743fd6e7d9SKevin Cernekee * The master volume defaults to 0x3ff (mute), but we ignore 5753fd6e7d9SKevin Cernekee * (zero) the LSB because the hardware step size is 0.125 dB 5763fd6e7d9SKevin Cernekee * and TLV_DB_SCALE_ITEM has a resolution of 0.01 dB. 5773fd6e7d9SKevin Cernekee */ 5783fd6e7d9SKevin Cernekee ret = regmap_update_bits(priv->regmap, TAS571X_MVOL_REG, 1, 0); 5793fd6e7d9SKevin Cernekee if (ret) 5803fd6e7d9SKevin Cernekee return ret; 5813fd6e7d9SKevin Cernekee } 5823fd6e7d9SKevin Cernekee 5833fd6e7d9SKevin Cernekee regcache_cache_only(priv->regmap, true); 5843fd6e7d9SKevin Cernekee gpiod_set_value(priv->pdn_gpio, 1); 5853fd6e7d9SKevin Cernekee 5863fd6e7d9SKevin Cernekee return snd_soc_register_codec(&client->dev, &priv->codec_driver, 5873fd6e7d9SKevin Cernekee &tas571x_dai, 1); 5883fd6e7d9SKevin Cernekee } 5893fd6e7d9SKevin Cernekee 5903fd6e7d9SKevin Cernekee static int tas571x_i2c_remove(struct i2c_client *client) 5913fd6e7d9SKevin Cernekee { 5923fd6e7d9SKevin Cernekee struct tas571x_private *priv = i2c_get_clientdata(client); 5933fd6e7d9SKevin Cernekee 5943fd6e7d9SKevin Cernekee snd_soc_unregister_codec(&client->dev); 5953fd6e7d9SKevin Cernekee regulator_bulk_disable(priv->chip->num_supply_names, priv->supplies); 5963fd6e7d9SKevin Cernekee 5973fd6e7d9SKevin Cernekee return 0; 5983fd6e7d9SKevin Cernekee } 5993fd6e7d9SKevin Cernekee 6003fd6e7d9SKevin Cernekee static const struct of_device_id tas571x_of_match[] = { 6013fd6e7d9SKevin Cernekee { .compatible = "ti,tas5711", .data = &tas5711_chip, }, 6023fd6e7d9SKevin Cernekee { .compatible = "ti,tas5717", .data = &tas5717_chip, }, 6033fd6e7d9SKevin Cernekee { .compatible = "ti,tas5719", .data = &tas5717_chip, }, 60423a282c4SPetr Kulhavy { .compatible = "ti,tas5721", .data = &tas5721_chip, }, 6053fd6e7d9SKevin Cernekee { } 6063fd6e7d9SKevin Cernekee }; 6073fd6e7d9SKevin Cernekee MODULE_DEVICE_TABLE(of, tas571x_of_match); 6083fd6e7d9SKevin Cernekee 6093fd6e7d9SKevin Cernekee static const struct i2c_device_id tas571x_i2c_id[] = { 610630e413dSPetr Kulhavy { "tas5711", (kernel_ulong_t) &tas5711_chip }, 611630e413dSPetr Kulhavy { "tas5717", (kernel_ulong_t) &tas5717_chip }, 612630e413dSPetr Kulhavy { "tas5719", (kernel_ulong_t) &tas5717_chip }, 61323a282c4SPetr Kulhavy { "tas5721", (kernel_ulong_t) &tas5721_chip }, 6143fd6e7d9SKevin Cernekee { } 6153fd6e7d9SKevin Cernekee }; 6163fd6e7d9SKevin Cernekee MODULE_DEVICE_TABLE(i2c, tas571x_i2c_id); 6173fd6e7d9SKevin Cernekee 6183fd6e7d9SKevin Cernekee static struct i2c_driver tas571x_i2c_driver = { 6193fd6e7d9SKevin Cernekee .driver = { 6203fd6e7d9SKevin Cernekee .name = "tas571x", 6213fd6e7d9SKevin Cernekee .of_match_table = of_match_ptr(tas571x_of_match), 6223fd6e7d9SKevin Cernekee }, 6233fd6e7d9SKevin Cernekee .probe = tas571x_i2c_probe, 6243fd6e7d9SKevin Cernekee .remove = tas571x_i2c_remove, 6253fd6e7d9SKevin Cernekee .id_table = tas571x_i2c_id, 6263fd6e7d9SKevin Cernekee }; 6273fd6e7d9SKevin Cernekee module_i2c_driver(tas571x_i2c_driver); 6283fd6e7d9SKevin Cernekee 6293fd6e7d9SKevin Cernekee MODULE_DESCRIPTION("ASoC TAS571x driver"); 6303fd6e7d9SKevin Cernekee MODULE_AUTHOR("Kevin Cernekee <cernekee@chromium.org>"); 6313fd6e7d9SKevin Cernekee MODULE_LICENSE("GPL"); 632