linux/sound/soc/codecs/pcm1792a.c
<<
>>
Prefs
   1/*
   2 * PCM1792A ASoC codec driver
   3 *
   4 * Copyright (c) Amarula Solutions B.V. 2013
   5 *
   6 *     Michael Trimarchi <michael@amarulasolutions.com>
   7 *
   8 * This program is free software; you can redistribute it and/or
   9 * modify it under the terms of the GNU General Public License
  10 * as published by the Free Software Foundation; either version 2
  11 * of the License, or (at your option) any later version.
  12 *
  13 * This program is distributed in the hope that it will be useful,
  14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16 * GNU General Public License for more details.
  17 */
  18
  19#include <linux/module.h>
  20#include <linux/slab.h>
  21#include <linux/kernel.h>
  22#include <linux/device.h>
  23#include <linux/spi/spi.h>
  24
  25#include <sound/core.h>
  26#include <sound/pcm.h>
  27#include <sound/pcm_params.h>
  28#include <sound/initval.h>
  29#include <sound/soc.h>
  30#include <sound/tlv.h>
  31#include <linux/of.h>
  32#include <linux/of_device.h>
  33
  34#include "pcm1792a.h"
  35
  36#define PCM1792A_DAC_VOL_LEFT   0x10
  37#define PCM1792A_DAC_VOL_RIGHT  0x11
  38#define PCM1792A_FMT_CONTROL    0x12
  39#define PCM1792A_MODE_CONTROL   0x13
  40#define PCM1792A_SOFT_MUTE      PCM1792A_FMT_CONTROL
  41
  42#define PCM1792A_FMT_MASK       0x70
  43#define PCM1792A_FMT_SHIFT      4
  44#define PCM1792A_MUTE_MASK      0x01
  45#define PCM1792A_MUTE_SHIFT     0
  46#define PCM1792A_ATLD_ENABLE    (1 << 7)
  47
  48static const struct reg_default pcm1792a_reg_defaults[] = {
  49        { 0x10, 0xff },
  50        { 0x11, 0xff },
  51        { 0x12, 0x50 },
  52        { 0x13, 0x00 },
  53        { 0x14, 0x00 },
  54        { 0x15, 0x01 },
  55        { 0x16, 0x00 },
  56        { 0x17, 0x00 },
  57};
  58
  59static bool pcm1792a_accessible_reg(struct device *dev, unsigned int reg)
  60{
  61        return reg >= 0x10 && reg <= 0x17;
  62}
  63
  64static bool pcm1792a_writeable_reg(struct device *dev, unsigned register reg)
  65{
  66        bool accessible;
  67
  68        accessible = pcm1792a_accessible_reg(dev, reg);
  69
  70        return accessible && reg != 0x16 && reg != 0x17;
  71}
  72
  73struct pcm1792a_private {
  74        struct regmap *regmap;
  75        unsigned int format;
  76        unsigned int rate;
  77};
  78
  79static int pcm1792a_set_dai_fmt(struct snd_soc_dai *codec_dai,
  80                             unsigned int format)
  81{
  82        struct snd_soc_codec *codec = codec_dai->codec;
  83        struct pcm1792a_private *priv = snd_soc_codec_get_drvdata(codec);
  84
  85        priv->format = format;
  86
  87        return 0;
  88}
  89
  90static int pcm1792a_digital_mute(struct snd_soc_dai *dai, int mute)
  91{
  92        struct snd_soc_codec *codec = dai->codec;
  93        struct pcm1792a_private *priv = snd_soc_codec_get_drvdata(codec);
  94        int ret;
  95
  96        ret = regmap_update_bits(priv->regmap, PCM1792A_SOFT_MUTE,
  97                                 PCM1792A_MUTE_MASK, !!mute);
  98        if (ret < 0)
  99                return ret;
 100
 101        return 0;
 102}
 103
 104static int pcm1792a_hw_params(struct snd_pcm_substream *substream,
 105                             struct snd_pcm_hw_params *params,
 106                             struct snd_soc_dai *dai)
 107{
 108        struct snd_soc_codec *codec = dai->codec;
 109        struct pcm1792a_private *priv = snd_soc_codec_get_drvdata(codec);
 110        int val = 0, ret;
 111
 112        priv->rate = params_rate(params);
 113
 114        switch (priv->format & SND_SOC_DAIFMT_FORMAT_MASK) {
 115        case SND_SOC_DAIFMT_RIGHT_J:
 116                switch (params_width(params)) {
 117                case 24:
 118                case 32:
 119                        val = 2;
 120                        break;
 121                case 16:
 122                        val = 0;
 123                        break;
 124                default:
 125                        return -EINVAL;
 126                }
 127                break;
 128        case SND_SOC_DAIFMT_I2S:
 129                switch (params_width(params)) {
 130                case 24:
 131                case 32:
 132                        val = 5;
 133                        break;
 134                case 16:
 135                        val = 4;
 136                        break;
 137                default:
 138                        return -EINVAL;
 139                }
 140                break;
 141        default:
 142                dev_err(codec->dev, "Invalid DAI format\n");
 143                return -EINVAL;
 144        }
 145
 146        val = val << PCM1792A_FMT_SHIFT | PCM1792A_ATLD_ENABLE;
 147
 148        ret = regmap_update_bits(priv->regmap, PCM1792A_FMT_CONTROL,
 149                                 PCM1792A_FMT_MASK | PCM1792A_ATLD_ENABLE, val);
 150        if (ret < 0)
 151                return ret;
 152
 153        return 0;
 154}
 155
 156static const struct snd_soc_dai_ops pcm1792a_dai_ops = {
 157        .set_fmt        = pcm1792a_set_dai_fmt,
 158        .hw_params      = pcm1792a_hw_params,
 159        .digital_mute   = pcm1792a_digital_mute,
 160};
 161
 162static const DECLARE_TLV_DB_SCALE(pcm1792a_dac_tlv, -12000, 50, 1);
 163
 164static const struct snd_kcontrol_new pcm1792a_controls[] = {
 165        SOC_DOUBLE_R_RANGE_TLV("DAC Playback Volume", PCM1792A_DAC_VOL_LEFT,
 166                         PCM1792A_DAC_VOL_RIGHT, 0, 0xf, 0xff, 0,
 167                         pcm1792a_dac_tlv),
 168        SOC_SINGLE("DAC Invert Output Switch", PCM1792A_MODE_CONTROL, 7, 1, 0),
 169        SOC_SINGLE("DAC Rolloff Filter Switch", PCM1792A_MODE_CONTROL, 1, 1, 0),
 170};
 171
 172static const struct snd_soc_dapm_widget pcm1792a_dapm_widgets[] = {
 173SND_SOC_DAPM_OUTPUT("IOUTL+"),
 174SND_SOC_DAPM_OUTPUT("IOUTL-"),
 175SND_SOC_DAPM_OUTPUT("IOUTR+"),
 176SND_SOC_DAPM_OUTPUT("IOUTR-"),
 177};
 178
 179static const struct snd_soc_dapm_route pcm1792a_dapm_routes[] = {
 180        { "IOUTL+", NULL, "Playback" },
 181        { "IOUTL-", NULL, "Playback" },
 182        { "IOUTR+", NULL, "Playback" },
 183        { "IOUTR-", NULL, "Playback" },
 184};
 185
 186static struct snd_soc_dai_driver pcm1792a_dai = {
 187        .name = "pcm1792a-hifi",
 188        .playback = {
 189                .stream_name = "Playback",
 190                .channels_min = 2,
 191                .channels_max = 2,
 192                .rates = PCM1792A_RATES,
 193                .formats = PCM1792A_FORMATS, },
 194        .ops = &pcm1792a_dai_ops,
 195};
 196
 197static const struct of_device_id pcm1792a_of_match[] = {
 198        { .compatible = "ti,pcm1792a", },
 199        { }
 200};
 201MODULE_DEVICE_TABLE(of, pcm1792a_of_match);
 202
 203static const struct regmap_config pcm1792a_regmap = {
 204        .reg_bits               = 8,
 205        .val_bits               = 8,
 206        .max_register           = 23,
 207        .reg_defaults           = pcm1792a_reg_defaults,
 208        .num_reg_defaults       = ARRAY_SIZE(pcm1792a_reg_defaults),
 209        .writeable_reg          = pcm1792a_writeable_reg,
 210        .readable_reg           = pcm1792a_accessible_reg,
 211};
 212
 213static struct snd_soc_codec_driver soc_codec_dev_pcm1792a = {
 214        .controls               = pcm1792a_controls,
 215        .num_controls           = ARRAY_SIZE(pcm1792a_controls),
 216        .dapm_widgets           = pcm1792a_dapm_widgets,
 217        .num_dapm_widgets       = ARRAY_SIZE(pcm1792a_dapm_widgets),
 218        .dapm_routes            = pcm1792a_dapm_routes,
 219        .num_dapm_routes        = ARRAY_SIZE(pcm1792a_dapm_routes),
 220};
 221
 222static int pcm1792a_spi_probe(struct spi_device *spi)
 223{
 224        struct pcm1792a_private *pcm1792a;
 225        int ret;
 226
 227        pcm1792a = devm_kzalloc(&spi->dev, sizeof(struct pcm1792a_private),
 228                                GFP_KERNEL);
 229        if (!pcm1792a)
 230                return -ENOMEM;
 231
 232        spi_set_drvdata(spi, pcm1792a);
 233
 234        pcm1792a->regmap = devm_regmap_init_spi(spi, &pcm1792a_regmap);
 235        if (IS_ERR(pcm1792a->regmap)) {
 236                ret = PTR_ERR(pcm1792a->regmap);
 237                dev_err(&spi->dev, "Failed to register regmap: %d\n", ret);
 238                return ret;
 239        }
 240
 241        return snd_soc_register_codec(&spi->dev,
 242                        &soc_codec_dev_pcm1792a, &pcm1792a_dai, 1);
 243}
 244
 245static int pcm1792a_spi_remove(struct spi_device *spi)
 246{
 247        snd_soc_unregister_codec(&spi->dev);
 248        return 0;
 249}
 250
 251static const struct spi_device_id pcm1792a_spi_ids[] = {
 252        { "pcm1792a", 0 },
 253        { },
 254};
 255MODULE_DEVICE_TABLE(spi, pcm1792a_spi_ids);
 256
 257static struct spi_driver pcm1792a_codec_driver = {
 258        .driver = {
 259                .name = "pcm1792a",
 260                .owner = THIS_MODULE,
 261                .of_match_table = of_match_ptr(pcm1792a_of_match),
 262        },
 263        .id_table = pcm1792a_spi_ids,
 264        .probe = pcm1792a_spi_probe,
 265        .remove = pcm1792a_spi_remove,
 266};
 267
 268module_spi_driver(pcm1792a_codec_driver);
 269
 270MODULE_DESCRIPTION("ASoC PCM1792A driver");
 271MODULE_AUTHOR("Michael Trimarchi <michael@amarulasolutions.com>");
 272MODULE_LICENSE("GPL");
 273