Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * PCM179X ASoC codec driver
0004  *
0005  * Copyright (c) Amarula Solutions B.V. 2013
0006  *
0007  *     Michael Trimarchi <michael@amarulasolutions.com>
0008  */
0009 
0010 #include <linux/module.h>
0011 #include <linux/slab.h>
0012 #include <linux/kernel.h>
0013 #include <linux/device.h>
0014 
0015 #include <sound/core.h>
0016 #include <sound/pcm.h>
0017 #include <sound/pcm_params.h>
0018 #include <sound/initval.h>
0019 #include <sound/soc.h>
0020 #include <sound/tlv.h>
0021 #include <linux/of.h>
0022 
0023 #include "pcm179x.h"
0024 
0025 #define PCM179X_DAC_VOL_LEFT    0x10
0026 #define PCM179X_DAC_VOL_RIGHT   0x11
0027 #define PCM179X_FMT_CONTROL 0x12
0028 #define PCM179X_MODE_CONTROL    0x13
0029 #define PCM179X_SOFT_MUTE   PCM179X_FMT_CONTROL
0030 
0031 #define PCM179X_FMT_MASK    0x70
0032 #define PCM179X_FMT_SHIFT   4
0033 #define PCM179X_MUTE_MASK   0x01
0034 #define PCM179X_MUTE_SHIFT  0
0035 #define PCM179X_ATLD_ENABLE (1 << 7)
0036 
0037 static const struct reg_default pcm179x_reg_defaults[] = {
0038     { 0x10, 0xff },
0039     { 0x11, 0xff },
0040     { 0x12, 0x50 },
0041     { 0x13, 0x00 },
0042     { 0x14, 0x00 },
0043     { 0x15, 0x01 },
0044     { 0x16, 0x00 },
0045     { 0x17, 0x00 },
0046 };
0047 
0048 static bool pcm179x_accessible_reg(struct device *dev, unsigned int reg)
0049 {
0050     return reg >= 0x10 && reg <= 0x17;
0051 }
0052 
0053 static bool pcm179x_writeable_reg(struct device *dev, unsigned int reg)
0054 {
0055     bool accessible;
0056 
0057     accessible = pcm179x_accessible_reg(dev, reg);
0058 
0059     return accessible && reg != 0x16 && reg != 0x17;
0060 }
0061 
0062 struct pcm179x_private {
0063     struct regmap *regmap;
0064     unsigned int format;
0065     unsigned int rate;
0066 };
0067 
0068 static int pcm179x_set_dai_fmt(struct snd_soc_dai *codec_dai,
0069                              unsigned int format)
0070 {
0071     struct snd_soc_component *component = codec_dai->component;
0072     struct pcm179x_private *priv = snd_soc_component_get_drvdata(component);
0073 
0074     priv->format = format;
0075 
0076     return 0;
0077 }
0078 
0079 static int pcm179x_mute(struct snd_soc_dai *dai, int mute, int direction)
0080 {
0081     struct snd_soc_component *component = dai->component;
0082     struct pcm179x_private *priv = snd_soc_component_get_drvdata(component);
0083     int ret;
0084 
0085     ret = regmap_update_bits(priv->regmap, PCM179X_SOFT_MUTE,
0086                  PCM179X_MUTE_MASK, !!mute);
0087     if (ret < 0)
0088         return ret;
0089 
0090     return 0;
0091 }
0092 
0093 static int pcm179x_hw_params(struct snd_pcm_substream *substream,
0094                  struct snd_pcm_hw_params *params,
0095                  struct snd_soc_dai *dai)
0096 {
0097     struct snd_soc_component *component = dai->component;
0098     struct pcm179x_private *priv = snd_soc_component_get_drvdata(component);
0099     int val = 0, ret;
0100 
0101     priv->rate = params_rate(params);
0102 
0103     switch (priv->format & SND_SOC_DAIFMT_FORMAT_MASK) {
0104     case SND_SOC_DAIFMT_RIGHT_J:
0105         switch (params_width(params)) {
0106         case 24:
0107         case 32:
0108             val = 2;
0109             break;
0110         case 16:
0111             val = 0;
0112             break;
0113         default:
0114             return -EINVAL;
0115         }
0116         break;
0117     case SND_SOC_DAIFMT_I2S:
0118         switch (params_width(params)) {
0119         case 24:
0120         case 32:
0121             val = 5;
0122             break;
0123         case 16:
0124             val = 4;
0125             break;
0126         default:
0127             return -EINVAL;
0128         }
0129         break;
0130     default:
0131         dev_err(component->dev, "Invalid DAI format\n");
0132         return -EINVAL;
0133     }
0134 
0135     val = val << PCM179X_FMT_SHIFT | PCM179X_ATLD_ENABLE;
0136 
0137     ret = regmap_update_bits(priv->regmap, PCM179X_FMT_CONTROL,
0138                  PCM179X_FMT_MASK | PCM179X_ATLD_ENABLE, val);
0139     if (ret < 0)
0140         return ret;
0141 
0142     return 0;
0143 }
0144 
0145 static const struct snd_soc_dai_ops pcm179x_dai_ops = {
0146     .set_fmt    = pcm179x_set_dai_fmt,
0147     .hw_params  = pcm179x_hw_params,
0148     .mute_stream    = pcm179x_mute,
0149     .no_capture_mute = 1,
0150 };
0151 
0152 static const DECLARE_TLV_DB_SCALE(pcm179x_dac_tlv, -12000, 50, 1);
0153 
0154 static const struct snd_kcontrol_new pcm179x_controls[] = {
0155     SOC_DOUBLE_R_RANGE_TLV("DAC Playback Volume", PCM179X_DAC_VOL_LEFT,
0156              PCM179X_DAC_VOL_RIGHT, 0, 0xf, 0xff, 0,
0157              pcm179x_dac_tlv),
0158     SOC_SINGLE("DAC Invert Output Switch", PCM179X_MODE_CONTROL, 7, 1, 0),
0159     SOC_SINGLE("DAC Rolloff Filter Switch", PCM179X_MODE_CONTROL, 1, 1, 0),
0160 };
0161 
0162 static const struct snd_soc_dapm_widget pcm179x_dapm_widgets[] = {
0163 SND_SOC_DAPM_OUTPUT("IOUTL+"),
0164 SND_SOC_DAPM_OUTPUT("IOUTL-"),
0165 SND_SOC_DAPM_OUTPUT("IOUTR+"),
0166 SND_SOC_DAPM_OUTPUT("IOUTR-"),
0167 };
0168 
0169 static const struct snd_soc_dapm_route pcm179x_dapm_routes[] = {
0170     { "IOUTL+", NULL, "Playback" },
0171     { "IOUTL-", NULL, "Playback" },
0172     { "IOUTR+", NULL, "Playback" },
0173     { "IOUTR-", NULL, "Playback" },
0174 };
0175 
0176 static struct snd_soc_dai_driver pcm179x_dai = {
0177     .name = "pcm179x-hifi",
0178     .playback = {
0179         .stream_name = "Playback",
0180         .channels_min = 2,
0181         .channels_max = 2,
0182         .rates = SNDRV_PCM_RATE_CONTINUOUS,
0183         .rate_min = 10000,
0184         .rate_max = 200000,
0185         .formats = PCM1792A_FORMATS, },
0186     .ops = &pcm179x_dai_ops,
0187 };
0188 
0189 const struct regmap_config pcm179x_regmap_config = {
0190     .reg_bits       = 8,
0191     .val_bits       = 8,
0192     .max_register       = 23,
0193     .reg_defaults       = pcm179x_reg_defaults,
0194     .num_reg_defaults   = ARRAY_SIZE(pcm179x_reg_defaults),
0195     .writeable_reg      = pcm179x_writeable_reg,
0196     .readable_reg       = pcm179x_accessible_reg,
0197 };
0198 EXPORT_SYMBOL_GPL(pcm179x_regmap_config);
0199 
0200 static const struct snd_soc_component_driver soc_component_dev_pcm179x = {
0201     .controls       = pcm179x_controls,
0202     .num_controls       = ARRAY_SIZE(pcm179x_controls),
0203     .dapm_widgets       = pcm179x_dapm_widgets,
0204     .num_dapm_widgets   = ARRAY_SIZE(pcm179x_dapm_widgets),
0205     .dapm_routes        = pcm179x_dapm_routes,
0206     .num_dapm_routes    = ARRAY_SIZE(pcm179x_dapm_routes),
0207     .idle_bias_on       = 1,
0208     .use_pmdown_time    = 1,
0209     .endianness     = 1,
0210 };
0211 
0212 int pcm179x_common_init(struct device *dev, struct regmap *regmap)
0213 {
0214     struct pcm179x_private *pcm179x;
0215 
0216     pcm179x = devm_kzalloc(dev, sizeof(struct pcm179x_private),
0217                 GFP_KERNEL);
0218     if (!pcm179x)
0219         return -ENOMEM;
0220 
0221     pcm179x->regmap = regmap;
0222     dev_set_drvdata(dev, pcm179x);
0223 
0224     return devm_snd_soc_register_component(dev,
0225             &soc_component_dev_pcm179x, &pcm179x_dai, 1);
0226 }
0227 EXPORT_SYMBOL_GPL(pcm179x_common_init);
0228 
0229 MODULE_DESCRIPTION("ASoC PCM179X driver");
0230 MODULE_AUTHOR("Michael Trimarchi <michael@amarulasolutions.com>");
0231 MODULE_LICENSE("GPL");