linux/sound/soc/codecs/pcm179x.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * PCM179X ASoC codec driver
   4 *
   5 * Copyright (c) Amarula Solutions B.V. 2013
   6 *
   7 *     Michael Trimarchi <michael@amarulasolutions.com>
   8 */
   9
  10#include <linux/module.h>
  11#include <linux/slab.h>
  12#include <linux/kernel.h>
  13#include <linux/device.h>
  14
  15#include <sound/core.h>
  16#include <sound/pcm.h>
  17#include <sound/pcm_params.h>
  18#include <sound/initval.h>
  19#include <sound/soc.h>
  20#include <sound/tlv.h>
  21#include <linux/of.h>
  22
  23#include "pcm179x.h"
  24
  25#define PCM179X_DAC_VOL_LEFT    0x10
  26#define PCM179X_DAC_VOL_RIGHT   0x11
  27#define PCM179X_FMT_CONTROL     0x12
  28#define PCM179X_MODE_CONTROL    0x13
  29#define PCM179X_SOFT_MUTE       PCM179X_FMT_CONTROL
  30
  31#define PCM179X_FMT_MASK        0x70
  32#define PCM179X_FMT_SHIFT       4
  33#define PCM179X_MUTE_MASK       0x01
  34#define PCM179X_MUTE_SHIFT      0
  35#define PCM179X_ATLD_ENABLE     (1 << 7)
  36
  37static const struct reg_default pcm179x_reg_defaults[] = {
  38        { 0x10, 0xff },
  39        { 0x11, 0xff },
  40        { 0x12, 0x50 },
  41        { 0x13, 0x00 },
  42        { 0x14, 0x00 },
  43        { 0x15, 0x01 },
  44        { 0x16, 0x00 },
  45        { 0x17, 0x00 },
  46};
  47
  48static bool pcm179x_accessible_reg(struct device *dev, unsigned int reg)
  49{
  50        return reg >= 0x10 && reg <= 0x17;
  51}
  52
  53static bool pcm179x_writeable_reg(struct device *dev, unsigned int reg)
  54{
  55        bool accessible;
  56
  57        accessible = pcm179x_accessible_reg(dev, reg);
  58
  59        return accessible && reg != 0x16 && reg != 0x17;
  60}
  61
  62struct pcm179x_private {
  63        struct regmap *regmap;
  64        unsigned int format;
  65        unsigned int rate;
  66};
  67
  68static int pcm179x_set_dai_fmt(struct snd_soc_dai *codec_dai,
  69                             unsigned int format)
  70{
  71        struct snd_soc_component *component = codec_dai->component;
  72        struct pcm179x_private *priv = snd_soc_component_get_drvdata(component);
  73
  74        priv->format = format;
  75
  76        return 0;
  77}
  78
  79static int pcm179x_mute(struct snd_soc_dai *dai, int mute, int direction)
  80{
  81        struct snd_soc_component *component = dai->component;
  82        struct pcm179x_private *priv = snd_soc_component_get_drvdata(component);
  83        int ret;
  84
  85        ret = regmap_update_bits(priv->regmap, PCM179X_SOFT_MUTE,
  86                                 PCM179X_MUTE_MASK, !!mute);
  87        if (ret < 0)
  88                return ret;
  89
  90        return 0;
  91}
  92
  93static int pcm179x_hw_params(struct snd_pcm_substream *substream,
  94                             struct snd_pcm_hw_params *params,
  95                             struct snd_soc_dai *dai)
  96{
  97        struct snd_soc_component *component = dai->component;
  98        struct pcm179x_private *priv = snd_soc_component_get_drvdata(component);
  99        int val = 0, ret;
 100
 101        priv->rate = params_rate(params);
 102
 103        switch (priv->format & SND_SOC_DAIFMT_FORMAT_MASK) {
 104        case SND_SOC_DAIFMT_RIGHT_J:
 105                switch (params_width(params)) {
 106                case 24:
 107                case 32:
 108                        val = 2;
 109                        break;
 110                case 16:
 111                        val = 0;
 112                        break;
 113                default:
 114                        return -EINVAL;
 115                }
 116                break;
 117        case SND_SOC_DAIFMT_I2S:
 118                switch (params_width(params)) {
 119                case 24:
 120                case 32:
 121                        val = 5;
 122                        break;
 123                case 16:
 124                        val = 4;
 125                        break;
 126                default:
 127                        return -EINVAL;
 128                }
 129                break;
 130        default:
 131                dev_err(component->dev, "Invalid DAI format\n");
 132                return -EINVAL;
 133        }
 134
 135        val = val << PCM179X_FMT_SHIFT | PCM179X_ATLD_ENABLE;
 136
 137        ret = regmap_update_bits(priv->regmap, PCM179X_FMT_CONTROL,
 138                                 PCM179X_FMT_MASK | PCM179X_ATLD_ENABLE, val);
 139        if (ret < 0)
 140                return ret;
 141
 142        return 0;
 143}
 144
 145static const struct snd_soc_dai_ops pcm179x_dai_ops = {
 146        .set_fmt        = pcm179x_set_dai_fmt,
 147        .hw_params      = pcm179x_hw_params,
 148        .mute_stream    = pcm179x_mute,
 149        .no_capture_mute = 1,
 150};
 151
 152static const DECLARE_TLV_DB_SCALE(pcm179x_dac_tlv, -12000, 50, 1);
 153
 154static const struct snd_kcontrol_new pcm179x_controls[] = {
 155        SOC_DOUBLE_R_RANGE_TLV("DAC Playback Volume", PCM179X_DAC_VOL_LEFT,
 156                         PCM179X_DAC_VOL_RIGHT, 0, 0xf, 0xff, 0,
 157                         pcm179x_dac_tlv),
 158        SOC_SINGLE("DAC Invert Output Switch", PCM179X_MODE_CONTROL, 7, 1, 0),
 159        SOC_SINGLE("DAC Rolloff Filter Switch", PCM179X_MODE_CONTROL, 1, 1, 0),
 160};
 161
 162static const struct snd_soc_dapm_widget pcm179x_dapm_widgets[] = {
 163SND_SOC_DAPM_OUTPUT("IOUTL+"),
 164SND_SOC_DAPM_OUTPUT("IOUTL-"),
 165SND_SOC_DAPM_OUTPUT("IOUTR+"),
 166SND_SOC_DAPM_OUTPUT("IOUTR-"),
 167};
 168
 169static const struct snd_soc_dapm_route pcm179x_dapm_routes[] = {
 170        { "IOUTL+", NULL, "Playback" },
 171        { "IOUTL-", NULL, "Playback" },
 172        { "IOUTR+", NULL, "Playback" },
 173        { "IOUTR-", NULL, "Playback" },
 174};
 175
 176static struct snd_soc_dai_driver pcm179x_dai = {
 177        .name = "pcm179x-hifi",
 178        .playback = {
 179                .stream_name = "Playback",
 180                .channels_min = 2,
 181                .channels_max = 2,
 182                .rates = SNDRV_PCM_RATE_CONTINUOUS,
 183                .rate_min = 10000,
 184                .rate_max = 200000,
 185                .formats = PCM1792A_FORMATS, },
 186        .ops = &pcm179x_dai_ops,
 187};
 188
 189const struct regmap_config pcm179x_regmap_config = {
 190        .reg_bits               = 8,
 191        .val_bits               = 8,
 192        .max_register           = 23,
 193        .reg_defaults           = pcm179x_reg_defaults,
 194        .num_reg_defaults       = ARRAY_SIZE(pcm179x_reg_defaults),
 195        .writeable_reg          = pcm179x_writeable_reg,
 196        .readable_reg           = pcm179x_accessible_reg,
 197};
 198EXPORT_SYMBOL_GPL(pcm179x_regmap_config);
 199
 200static const struct snd_soc_component_driver soc_component_dev_pcm179x = {
 201        .controls               = pcm179x_controls,
 202        .num_controls           = ARRAY_SIZE(pcm179x_controls),
 203        .dapm_widgets           = pcm179x_dapm_widgets,
 204        .num_dapm_widgets       = ARRAY_SIZE(pcm179x_dapm_widgets),
 205        .dapm_routes            = pcm179x_dapm_routes,
 206        .num_dapm_routes        = ARRAY_SIZE(pcm179x_dapm_routes),
 207        .idle_bias_on           = 1,
 208        .use_pmdown_time        = 1,
 209        .endianness             = 1,
 210        .non_legacy_dai_naming  = 1,
 211};
 212
 213int pcm179x_common_init(struct device *dev, struct regmap *regmap)
 214{
 215        struct pcm179x_private *pcm179x;
 216
 217        pcm179x = devm_kzalloc(dev, sizeof(struct pcm179x_private),
 218                                GFP_KERNEL);
 219        if (!pcm179x)
 220                return -ENOMEM;
 221
 222        pcm179x->regmap = regmap;
 223        dev_set_drvdata(dev, pcm179x);
 224
 225        return devm_snd_soc_register_component(dev,
 226                        &soc_component_dev_pcm179x, &pcm179x_dai, 1);
 227}
 228EXPORT_SYMBOL_GPL(pcm179x_common_init);
 229
 230MODULE_DESCRIPTION("ASoC PCM179X driver");
 231MODULE_AUTHOR("Michael Trimarchi <michael@amarulasolutions.com>");
 232MODULE_LICENSE("GPL");
 233