linux/sound/soc/codecs/tpa6130a2.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * ALSA SoC Texas Instruments TPA6130A2 headset stereo amplifier driver
   4 *
   5 * Copyright (C) Nokia Corporation
   6 *
   7 * Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
   8 */
   9
  10#include <linux/module.h>
  11#include <linux/errno.h>
  12#include <linux/device.h>
  13#include <linux/i2c.h>
  14#include <linux/gpio.h>
  15#include <linux/regulator/consumer.h>
  16#include <linux/slab.h>
  17#include <sound/tpa6130a2-plat.h>
  18#include <sound/soc.h>
  19#include <sound/tlv.h>
  20#include <linux/of.h>
  21#include <linux/of_gpio.h>
  22#include <linux/regmap.h>
  23
  24#include "tpa6130a2.h"
  25
  26enum tpa_model {
  27        TPA6130A2,
  28        TPA6140A2,
  29};
  30
  31/* This struct is used to save the context */
  32struct tpa6130a2_data {
  33        struct device *dev;
  34        struct regmap *regmap;
  35        struct regulator *supply;
  36        int power_gpio;
  37        enum tpa_model id;
  38};
  39
  40static int tpa6130a2_power(struct tpa6130a2_data *data, bool enable)
  41{
  42        int ret = 0, ret2;
  43
  44        if (enable) {
  45                ret = regulator_enable(data->supply);
  46                if (ret != 0) {
  47                        dev_err(data->dev,
  48                                "Failed to enable supply: %d\n", ret);
  49                        return ret;
  50                }
  51                /* Power on */
  52                if (data->power_gpio >= 0)
  53                        gpio_set_value(data->power_gpio, 1);
  54
  55                /* Sync registers */
  56                regcache_cache_only(data->regmap, false);
  57                ret = regcache_sync(data->regmap);
  58                if (ret != 0) {
  59                        dev_err(data->dev,
  60                                "Failed to sync registers: %d\n", ret);
  61                        regcache_cache_only(data->regmap, true);
  62                        if (data->power_gpio >= 0)
  63                                gpio_set_value(data->power_gpio, 0);
  64                        ret2 = regulator_disable(data->supply);
  65                        if (ret2 != 0)
  66                                dev_err(data->dev,
  67                                        "Failed to disable supply: %d\n", ret2);
  68                        return ret;
  69                }
  70        } else {
  71                /* Powered off device does not retain registers. While device
  72                 * is off, any register updates (i.e. volume changes) should
  73                 * happen in cache only.
  74                 */
  75                regcache_mark_dirty(data->regmap);
  76                regcache_cache_only(data->regmap, true);
  77
  78                /* Power off */
  79                if (data->power_gpio >= 0)
  80                        gpio_set_value(data->power_gpio, 0);
  81
  82                ret = regulator_disable(data->supply);
  83                if (ret != 0) {
  84                        dev_err(data->dev,
  85                                "Failed to disable supply: %d\n", ret);
  86                        return ret;
  87                }
  88        }
  89
  90        return ret;
  91}
  92
  93static int tpa6130a2_power_event(struct snd_soc_dapm_widget *w,
  94                                 struct snd_kcontrol *kctrl, int event)
  95{
  96        struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);
  97        struct tpa6130a2_data *data = snd_soc_component_get_drvdata(c);
  98
  99        if (SND_SOC_DAPM_EVENT_ON(event)) {
 100                /* Before widget power up: turn chip on, sync registers */
 101                return tpa6130a2_power(data, true);
 102        } else {
 103                /* After widget power down: turn chip off */
 104                return tpa6130a2_power(data, false);
 105        }
 106}
 107
 108/*
 109 * TPA6130 volume. From -59.5 to 4 dB with increasing step size when going
 110 * down in gain.
 111 */
 112static const DECLARE_TLV_DB_RANGE(tpa6130_tlv,
 113        0, 1, TLV_DB_SCALE_ITEM(-5950, 600, 0),
 114        2, 3, TLV_DB_SCALE_ITEM(-5000, 250, 0),
 115        4, 5, TLV_DB_SCALE_ITEM(-4550, 160, 0),
 116        6, 7, TLV_DB_SCALE_ITEM(-4140, 190, 0),
 117        8, 9, TLV_DB_SCALE_ITEM(-3650, 120, 0),
 118        10, 11, TLV_DB_SCALE_ITEM(-3330, 160, 0),
 119        12, 13, TLV_DB_SCALE_ITEM(-3040, 180, 0),
 120        14, 20, TLV_DB_SCALE_ITEM(-2710, 110, 0),
 121        21, 37, TLV_DB_SCALE_ITEM(-1960, 74, 0),
 122        38, 63, TLV_DB_SCALE_ITEM(-720, 45, 0)
 123);
 124
 125static const struct snd_kcontrol_new tpa6130a2_controls[] = {
 126        SOC_SINGLE_TLV("Headphone Playback Volume",
 127                       TPA6130A2_REG_VOL_MUTE, 0, 0x3f, 0,
 128                       tpa6130_tlv),
 129};
 130
 131static const DECLARE_TLV_DB_RANGE(tpa6140_tlv,
 132        0, 8, TLV_DB_SCALE_ITEM(-5900, 400, 0),
 133        9, 16, TLV_DB_SCALE_ITEM(-2500, 200, 0),
 134        17, 31, TLV_DB_SCALE_ITEM(-1000, 100, 0)
 135);
 136
 137static const struct snd_kcontrol_new tpa6140a2_controls[] = {
 138        SOC_SINGLE_TLV("Headphone Playback Volume",
 139                       TPA6130A2_REG_VOL_MUTE, 1, 0x1f, 0,
 140                       tpa6140_tlv),
 141};
 142
 143static int tpa6130a2_component_probe(struct snd_soc_component *component)
 144{
 145        struct tpa6130a2_data *data = snd_soc_component_get_drvdata(component);
 146
 147        if (data->id == TPA6140A2)
 148                return snd_soc_add_component_controls(component,
 149                        tpa6140a2_controls, ARRAY_SIZE(tpa6140a2_controls));
 150        else
 151                return snd_soc_add_component_controls(component,
 152                        tpa6130a2_controls, ARRAY_SIZE(tpa6130a2_controls));
 153}
 154
 155static const struct snd_soc_dapm_widget tpa6130a2_dapm_widgets[] = {
 156        SND_SOC_DAPM_INPUT("LEFTIN"),
 157        SND_SOC_DAPM_INPUT("RIGHTIN"),
 158        SND_SOC_DAPM_OUTPUT("HPLEFT"),
 159        SND_SOC_DAPM_OUTPUT("HPRIGHT"),
 160
 161        SND_SOC_DAPM_PGA("Left Mute", TPA6130A2_REG_VOL_MUTE,
 162                         TPA6130A2_HP_EN_L_SHIFT, 1, NULL, 0),
 163        SND_SOC_DAPM_PGA("Right Mute", TPA6130A2_REG_VOL_MUTE,
 164                         TPA6130A2_HP_EN_R_SHIFT, 1, NULL, 0),
 165        SND_SOC_DAPM_PGA("Left PGA", TPA6130A2_REG_CONTROL,
 166                         TPA6130A2_HP_EN_L_SHIFT, 0, NULL, 0),
 167        SND_SOC_DAPM_PGA("Right PGA", TPA6130A2_REG_CONTROL,
 168                         TPA6130A2_HP_EN_R_SHIFT, 0, NULL, 0),
 169
 170        SND_SOC_DAPM_SUPPLY("Power", TPA6130A2_REG_CONTROL,
 171                            TPA6130A2_SWS_SHIFT, 1, tpa6130a2_power_event,
 172                            SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
 173};
 174
 175static const struct snd_soc_dapm_route tpa6130a2_dapm_routes[] = {
 176        { "Left PGA", NULL, "LEFTIN" },
 177        { "Right PGA", NULL, "RIGHTIN" },
 178
 179        { "Left Mute", NULL, "Left PGA" },
 180        { "Right Mute", NULL, "Right PGA" },
 181
 182        { "HPLEFT", NULL, "Left Mute" },
 183        { "HPRIGHT", NULL, "Right Mute" },
 184
 185        { "Left PGA", NULL, "Power" },
 186        { "Right PGA", NULL, "Power" },
 187};
 188
 189static const struct snd_soc_component_driver tpa6130a2_component_driver = {
 190        .name = "tpa6130a2",
 191        .probe = tpa6130a2_component_probe,
 192        .dapm_widgets = tpa6130a2_dapm_widgets,
 193        .num_dapm_widgets = ARRAY_SIZE(tpa6130a2_dapm_widgets),
 194        .dapm_routes = tpa6130a2_dapm_routes,
 195        .num_dapm_routes = ARRAY_SIZE(tpa6130a2_dapm_routes),
 196};
 197
 198static const struct reg_default tpa6130a2_reg_defaults[] = {
 199        { TPA6130A2_REG_CONTROL, TPA6130A2_SWS },
 200        { TPA6130A2_REG_VOL_MUTE, TPA6130A2_MUTE_R | TPA6130A2_MUTE_L },
 201};
 202
 203static const struct regmap_config tpa6130a2_regmap_config = {
 204        .reg_bits = 8,
 205        .val_bits = 8,
 206        .max_register = TPA6130A2_REG_VERSION,
 207        .reg_defaults = tpa6130a2_reg_defaults,
 208        .num_reg_defaults = ARRAY_SIZE(tpa6130a2_reg_defaults),
 209        .cache_type = REGCACHE_RBTREE,
 210};
 211
 212static int tpa6130a2_probe(struct i2c_client *client,
 213                           const struct i2c_device_id *id)
 214{
 215        struct device *dev;
 216        struct tpa6130a2_data *data;
 217        struct tpa6130a2_platform_data *pdata = client->dev.platform_data;
 218        struct device_node *np = client->dev.of_node;
 219        const char *regulator;
 220        unsigned int version;
 221        int ret;
 222
 223        dev = &client->dev;
 224
 225        data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
 226        if (!data)
 227                return -ENOMEM;
 228
 229        data->dev = dev;
 230
 231        data->regmap = devm_regmap_init_i2c(client, &tpa6130a2_regmap_config);
 232        if (IS_ERR(data->regmap))
 233                return PTR_ERR(data->regmap);
 234
 235        if (pdata) {
 236                data->power_gpio = pdata->power_gpio;
 237        } else if (np) {
 238                data->power_gpio = of_get_named_gpio(np, "power-gpio", 0);
 239        } else {
 240                dev_err(dev, "Platform data not set\n");
 241                dump_stack();
 242                return -ENODEV;
 243        }
 244
 245        i2c_set_clientdata(client, data);
 246
 247        data->id = id->driver_data;
 248
 249        if (data->power_gpio >= 0) {
 250                ret = devm_gpio_request(dev, data->power_gpio,
 251                                        "tpa6130a2 enable");
 252                if (ret < 0) {
 253                        dev_err(dev, "Failed to request power GPIO (%d)\n",
 254                                data->power_gpio);
 255                        return ret;
 256                }
 257                gpio_direction_output(data->power_gpio, 0);
 258        }
 259
 260        switch (data->id) {
 261        default:
 262                dev_warn(dev, "Unknown TPA model (%d). Assuming 6130A2\n",
 263                         data->id);
 264                /* fall through */
 265        case TPA6130A2:
 266                regulator = "Vdd";
 267                break;
 268        case TPA6140A2:
 269                regulator = "AVdd";
 270                break;
 271        }
 272
 273        data->supply = devm_regulator_get(dev, regulator);
 274        if (IS_ERR(data->supply)) {
 275                ret = PTR_ERR(data->supply);
 276                dev_err(dev, "Failed to request supply: %d\n", ret);
 277                return ret;
 278        }
 279
 280        ret = tpa6130a2_power(data, true);
 281        if (ret != 0)
 282                return ret;
 283
 284
 285        /* Read version */
 286        regmap_read(data->regmap, TPA6130A2_REG_VERSION, &version);
 287        version &= TPA6130A2_VERSION_MASK;
 288        if ((version != 1) && (version != 2))
 289                dev_warn(dev, "UNTESTED version detected (%d)\n", version);
 290
 291        /* Disable the chip */
 292        ret = tpa6130a2_power(data, false);
 293        if (ret != 0)
 294                return ret;
 295
 296        return devm_snd_soc_register_component(&client->dev,
 297                        &tpa6130a2_component_driver, NULL, 0);
 298}
 299
 300static const struct i2c_device_id tpa6130a2_id[] = {
 301        { "tpa6130a2", TPA6130A2 },
 302        { "tpa6140a2", TPA6140A2 },
 303        { }
 304};
 305MODULE_DEVICE_TABLE(i2c, tpa6130a2_id);
 306
 307#if IS_ENABLED(CONFIG_OF)
 308static const struct of_device_id tpa6130a2_of_match[] = {
 309        { .compatible = "ti,tpa6130a2", },
 310        { .compatible = "ti,tpa6140a2" },
 311        {},
 312};
 313MODULE_DEVICE_TABLE(of, tpa6130a2_of_match);
 314#endif
 315
 316static struct i2c_driver tpa6130a2_i2c_driver = {
 317        .driver = {
 318                .name = "tpa6130a2",
 319                .of_match_table = of_match_ptr(tpa6130a2_of_match),
 320        },
 321        .probe = tpa6130a2_probe,
 322        .id_table = tpa6130a2_id,
 323};
 324
 325module_i2c_driver(tpa6130a2_i2c_driver);
 326
 327MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");
 328MODULE_DESCRIPTION("TPA6130A2 Headphone amplifier driver");
 329MODULE_LICENSE("GPL");
 330