root/sound/soc/codecs/pcm1789.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. pcm1789_accessible_reg
  2. pcm1789_writeable_reg
  3. pcm1789_set_dai_fmt
  4. pcm1789_digital_mute
  5. pcm1789_hw_params
  6. pcm1789_work_queue
  7. pcm1789_trigger
  8. pcm1789_common_init
  9. pcm1789_common_exit

   1 // SPDX-License-Identifier: GPL-2.0
   2 // Audio driver for PCM1789
   3 // Copyright (C) 2018 Bootlin
   4 // Mylène Josserand <mylene.josserand@bootlin.com>
   5 
   6 #include <linux/gpio/consumer.h>
   7 #include <linux/module.h>
   8 #include <linux/workqueue.h>
   9 
  10 #include <sound/pcm_params.h>
  11 #include <sound/soc.h>
  12 #include <sound/tlv.h>
  13 
  14 #include "pcm1789.h"
  15 
  16 #define PCM1789_MUTE_CONTROL    0x10
  17 #define PCM1789_FMT_CONTROL     0x11
  18 #define PCM1789_SOFT_MUTE       0x14
  19 #define PCM1789_DAC_VOL_LEFT    0x18
  20 #define PCM1789_DAC_VOL_RIGHT   0x19
  21 
  22 #define PCM1789_FMT_MASK        0x07
  23 #define PCM1789_MUTE_MASK       0x03
  24 #define PCM1789_MUTE_SRET       0x06
  25 
  26 struct pcm1789_private {
  27         struct regmap *regmap;
  28         unsigned int format;
  29         unsigned int rate;
  30         struct gpio_desc *reset;
  31         struct work_struct work;
  32         struct device *dev;
  33 };
  34 
  35 static const struct reg_default pcm1789_reg_defaults[] = {
  36         { PCM1789_FMT_CONTROL, 0x00 },
  37         { PCM1789_SOFT_MUTE, 0x00 },
  38         { PCM1789_DAC_VOL_LEFT, 0xff },
  39         { PCM1789_DAC_VOL_RIGHT, 0xff },
  40 };
  41 
  42 static bool pcm1789_accessible_reg(struct device *dev, unsigned int reg)
  43 {
  44         return reg >= PCM1789_MUTE_CONTROL && reg <= PCM1789_DAC_VOL_RIGHT;
  45 }
  46 
  47 static bool pcm1789_writeable_reg(struct device *dev, unsigned int reg)
  48 {
  49         return pcm1789_accessible_reg(dev, reg);
  50 }
  51 
  52 static int pcm1789_set_dai_fmt(struct snd_soc_dai *codec_dai,
  53                                unsigned int format)
  54 {
  55         struct snd_soc_component *component = codec_dai->component;
  56         struct pcm1789_private *priv = snd_soc_component_get_drvdata(component);
  57 
  58         priv->format = format;
  59 
  60         return 0;
  61 }
  62 
  63 static int pcm1789_digital_mute(struct snd_soc_dai *codec_dai, int mute)
  64 {
  65         struct snd_soc_component *component = codec_dai->component;
  66         struct pcm1789_private *priv = snd_soc_component_get_drvdata(component);
  67 
  68         return regmap_update_bits(priv->regmap, PCM1789_SOFT_MUTE,
  69                                   PCM1789_MUTE_MASK,
  70                                   mute ? 0 : PCM1789_MUTE_MASK);
  71 }
  72 
  73 static int pcm1789_hw_params(struct snd_pcm_substream *substream,
  74                              struct snd_pcm_hw_params *params,
  75                              struct snd_soc_dai *codec_dai)
  76 {
  77         struct snd_soc_component *component = codec_dai->component;
  78         struct pcm1789_private *priv = snd_soc_component_get_drvdata(component);
  79         int val = 0, ret;
  80 
  81         priv->rate = params_rate(params);
  82 
  83         switch (priv->format & SND_SOC_DAIFMT_FORMAT_MASK) {
  84         case SND_SOC_DAIFMT_RIGHT_J:
  85                 switch (params_width(params)) {
  86                 case 24:
  87                         val = 2;
  88                         break;
  89                 case 16:
  90                         val = 3;
  91                         break;
  92                 default:
  93                         return -EINVAL;
  94                 }
  95                 break;
  96         case SND_SOC_DAIFMT_I2S:
  97                 switch (params_width(params)) {
  98                 case 16:
  99                 case 24:
 100                 case 32:
 101                         val = 0;
 102                         break;
 103                 default:
 104                         return -EINVAL;
 105                 }
 106                 break;
 107         case SND_SOC_DAIFMT_LEFT_J:
 108                 switch (params_width(params)) {
 109                 case 16:
 110                 case 24:
 111                 case 32:
 112                         val = 1;
 113                         break;
 114                 default:
 115                         return -EINVAL;
 116                 }
 117                 break;
 118         default:
 119                 dev_err(component->dev, "Invalid DAI format\n");
 120                 return -EINVAL;
 121         }
 122 
 123         ret = regmap_update_bits(priv->regmap, PCM1789_FMT_CONTROL,
 124                                  PCM1789_FMT_MASK, val);
 125         if (ret < 0)
 126                 return ret;
 127 
 128         return 0;
 129 }
 130 
 131 static void pcm1789_work_queue(struct work_struct *work)
 132 {
 133         struct pcm1789_private *priv = container_of(work,
 134                                                     struct pcm1789_private,
 135                                                     work);
 136 
 137         /* Perform a software reset to remove codec from desynchronized state */
 138         if (regmap_update_bits(priv->regmap, PCM1789_MUTE_CONTROL,
 139                                0x3 << PCM1789_MUTE_SRET, 0) < 0)
 140                 dev_err(priv->dev, "Error while setting SRET");
 141 }
 142 
 143 static int pcm1789_trigger(struct snd_pcm_substream *substream, int cmd,
 144                            struct snd_soc_dai *dai)
 145 {
 146         struct snd_soc_component *component = dai->component;
 147         struct pcm1789_private *priv = snd_soc_component_get_drvdata(component);
 148         int ret = 0;
 149 
 150         switch (cmd) {
 151         case SNDRV_PCM_TRIGGER_START:
 152         case SNDRV_PCM_TRIGGER_RESUME:
 153         case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 154                 schedule_work(&priv->work);
 155                 break;
 156         case SNDRV_PCM_TRIGGER_STOP:
 157         case SNDRV_PCM_TRIGGER_SUSPEND:
 158         case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 159                 break;
 160         default:
 161                 ret = -EINVAL;
 162         }
 163 
 164         return ret;
 165 }
 166 
 167 static const struct snd_soc_dai_ops pcm1789_dai_ops = {
 168         .set_fmt        = pcm1789_set_dai_fmt,
 169         .hw_params      = pcm1789_hw_params,
 170         .digital_mute   = pcm1789_digital_mute,
 171         .trigger        = pcm1789_trigger,
 172 };
 173 
 174 static const DECLARE_TLV_DB_SCALE(pcm1789_dac_tlv, -12000, 50, 1);
 175 
 176 static const struct snd_kcontrol_new pcm1789_controls[] = {
 177         SOC_DOUBLE_R_RANGE_TLV("DAC Playback Volume", PCM1789_DAC_VOL_LEFT,
 178                                PCM1789_DAC_VOL_RIGHT, 0, 0xf, 0xff, 0,
 179                                pcm1789_dac_tlv),
 180 };
 181 
 182 static const struct snd_soc_dapm_widget pcm1789_dapm_widgets[] = {
 183         SND_SOC_DAPM_OUTPUT("IOUTL+"),
 184         SND_SOC_DAPM_OUTPUT("IOUTL-"),
 185         SND_SOC_DAPM_OUTPUT("IOUTR+"),
 186         SND_SOC_DAPM_OUTPUT("IOUTR-"),
 187 };
 188 
 189 static const struct snd_soc_dapm_route pcm1789_dapm_routes[] = {
 190         { "IOUTL+", NULL, "Playback" },
 191         { "IOUTL-", NULL, "Playback" },
 192         { "IOUTR+", NULL, "Playback" },
 193         { "IOUTR-", NULL, "Playback" },
 194 };
 195 
 196 static struct snd_soc_dai_driver pcm1789_dai = {
 197         .name = "pcm1789-hifi",
 198         .playback = {
 199                 .stream_name = "Playback",
 200                 .channels_min = 2,
 201                 .channels_max = 2,
 202                 .rates = SNDRV_PCM_RATE_CONTINUOUS,
 203                 .rate_min = 10000,
 204                 .rate_max = 200000,
 205                 .formats = PCM1789_FORMATS,
 206         },
 207         .ops = &pcm1789_dai_ops,
 208 };
 209 
 210 const struct regmap_config pcm1789_regmap_config = {
 211         .reg_bits               = 8,
 212         .val_bits               = 8,
 213         .max_register           = PCM1789_DAC_VOL_RIGHT,
 214         .reg_defaults           = pcm1789_reg_defaults,
 215         .num_reg_defaults       = ARRAY_SIZE(pcm1789_reg_defaults),
 216         .writeable_reg          = pcm1789_writeable_reg,
 217         .readable_reg           = pcm1789_accessible_reg,
 218 };
 219 EXPORT_SYMBOL_GPL(pcm1789_regmap_config);
 220 
 221 static const struct snd_soc_component_driver soc_component_dev_pcm1789 = {
 222         .controls               = pcm1789_controls,
 223         .num_controls           = ARRAY_SIZE(pcm1789_controls),
 224         .dapm_widgets           = pcm1789_dapm_widgets,
 225         .num_dapm_widgets       = ARRAY_SIZE(pcm1789_dapm_widgets),
 226         .dapm_routes            = pcm1789_dapm_routes,
 227         .num_dapm_routes        = ARRAY_SIZE(pcm1789_dapm_routes),
 228         .idle_bias_on           = 1,
 229         .use_pmdown_time        = 1,
 230         .endianness             = 1,
 231         .non_legacy_dai_naming  = 1,
 232 };
 233 
 234 int pcm1789_common_init(struct device *dev, struct regmap *regmap)
 235 {
 236         struct pcm1789_private *pcm1789;
 237 
 238         pcm1789 = devm_kzalloc(dev, sizeof(struct pcm1789_private),
 239                                GFP_KERNEL);
 240         if (!pcm1789)
 241                 return -ENOMEM;
 242 
 243         pcm1789->regmap = regmap;
 244         pcm1789->dev = dev;
 245         dev_set_drvdata(dev, pcm1789);
 246 
 247         pcm1789->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
 248         if (IS_ERR(pcm1789->reset))
 249                 return PTR_ERR(pcm1789->reset);
 250 
 251         gpiod_set_value_cansleep(pcm1789->reset, 0);
 252         msleep(300);
 253 
 254         INIT_WORK(&pcm1789->work, pcm1789_work_queue);
 255 
 256         return devm_snd_soc_register_component(dev, &soc_component_dev_pcm1789,
 257                                                &pcm1789_dai, 1);
 258 }
 259 EXPORT_SYMBOL_GPL(pcm1789_common_init);
 260 
 261 int pcm1789_common_exit(struct device *dev)
 262 {
 263         struct pcm1789_private *priv = dev_get_drvdata(dev);
 264 
 265         flush_work(&priv->work);
 266 
 267         return 0;
 268 }
 269 EXPORT_SYMBOL_GPL(pcm1789_common_exit);
 270 
 271 MODULE_DESCRIPTION("ASoC PCM1789 driver");
 272 MODULE_AUTHOR("Mylène Josserand <mylene.josserand@free-electrons.com>");
 273 MODULE_LICENSE("GPL");

/* [<][>][^][v][top][bottom][index][help] */