linux/sound/soc/codecs/es7134.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2017 BayLibre, SAS.
   3 * Author: Jerome Brunet <jbrunet@baylibre.com>
   4 *
   5 * This program is free software; you can redistribute it and/or modify
   6 * it under the terms of version 2 of the GNU General Public License as
   7 * published by the Free Software Foundation.
   8 *
   9 * This program is distributed in the hope that it will be useful, but
  10 * WITHOUT ANY WARRANTY; without even the implied warranty of
  11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  12 * General Public License for more details.
  13 *
  14 * You should have received a copy of the GNU General Public License
  15 * along with this program; if not, see <http://www.gnu.org/licenses/>.
  16 * The full GNU General Public License is included in this distribution
  17 * in the file called COPYING.
  18 */
  19
  20#include <linux/of_platform.h>
  21#include <linux/module.h>
  22#include <sound/soc.h>
  23
  24/*
  25 * The everest 7134 is a very simple DA converter with no register
  26 */
  27
  28struct es7134_clock_mode {
  29        unsigned int rate_min;
  30        unsigned int rate_max;
  31        unsigned int *mclk_fs;
  32        unsigned int mclk_fs_num;
  33};
  34
  35struct es7134_chip {
  36        struct snd_soc_dai_driver *dai_drv;
  37        const struct es7134_clock_mode *modes;
  38        unsigned int mode_num;
  39        const struct snd_soc_dapm_widget *extra_widgets;
  40        unsigned int extra_widget_num;
  41        const struct snd_soc_dapm_route *extra_routes;
  42        unsigned int extra_route_num;
  43};
  44
  45struct es7134_data {
  46        unsigned int mclk;
  47        const struct es7134_chip *chip;
  48};
  49
  50static int es7134_check_mclk(struct snd_soc_dai *dai,
  51                             struct es7134_data *priv,
  52                             unsigned int rate)
  53{
  54        unsigned int mfs = priv->mclk / rate;
  55        int i, j;
  56
  57        for (i = 0; i < priv->chip->mode_num; i++) {
  58                const struct es7134_clock_mode *mode = &priv->chip->modes[i];
  59
  60                if (rate < mode->rate_min || rate > mode->rate_max)
  61                        continue;
  62
  63                for (j = 0; j < mode->mclk_fs_num; j++) {
  64                        if (mode->mclk_fs[j] == mfs)
  65                                return 0;
  66                }
  67
  68                dev_err(dai->dev, "unsupported mclk_fs %u for rate %u\n",
  69                        mfs, rate);
  70                return -EINVAL;
  71        }
  72
  73        /* should not happen */
  74        dev_err(dai->dev, "unsupported rate: %u\n", rate);
  75        return -EINVAL;
  76}
  77
  78static int es7134_hw_params(struct snd_pcm_substream *substream,
  79                            struct snd_pcm_hw_params *params,
  80                            struct snd_soc_dai *dai)
  81{
  82        struct es7134_data *priv = snd_soc_dai_get_drvdata(dai);
  83
  84        /* mclk has not been provided, assume it is OK */
  85        if (!priv->mclk)
  86                return 0;
  87
  88        return es7134_check_mclk(dai, priv, params_rate(params));
  89}
  90
  91static int es7134_set_sysclk(struct snd_soc_dai *dai, int clk_id,
  92                             unsigned int freq, int dir)
  93{
  94        struct es7134_data *priv = snd_soc_dai_get_drvdata(dai);
  95
  96        if (dir == SND_SOC_CLOCK_IN && clk_id == 0) {
  97                priv->mclk = freq;
  98                return 0;
  99        }
 100
 101        return -ENOTSUPP;
 102}
 103
 104static int es7134_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
 105{
 106        fmt &= (SND_SOC_DAIFMT_FORMAT_MASK | SND_SOC_DAIFMT_INV_MASK |
 107                SND_SOC_DAIFMT_MASTER_MASK);
 108
 109        if (fmt != (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
 110                    SND_SOC_DAIFMT_CBS_CFS)) {
 111                dev_err(codec_dai->dev, "Invalid DAI format\n");
 112                return -EINVAL;
 113        }
 114
 115        return 0;
 116}
 117
 118static int es7134_component_probe(struct snd_soc_component *c)
 119{
 120        struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(c);
 121        struct es7134_data *priv = snd_soc_component_get_drvdata(c);
 122        const struct es7134_chip *chip = priv->chip;
 123        int ret;
 124
 125        if (chip->extra_widget_num) {
 126                ret = snd_soc_dapm_new_controls(dapm, chip->extra_widgets,
 127                                                chip->extra_widget_num);
 128                if (ret) {
 129                        dev_err(c->dev, "failed to add extra widgets\n");
 130                        return ret;
 131                }
 132        }
 133
 134        if (chip->extra_route_num) {
 135                ret = snd_soc_dapm_add_routes(dapm, chip->extra_routes,
 136                                              chip->extra_route_num);
 137                if (ret) {
 138                        dev_err(c->dev, "failed to add extra routes\n");
 139                        return ret;
 140                }
 141        }
 142
 143        return 0;
 144}
 145
 146static const struct snd_soc_dai_ops es7134_dai_ops = {
 147        .set_fmt        = es7134_set_fmt,
 148        .hw_params      = es7134_hw_params,
 149        .set_sysclk     = es7134_set_sysclk,
 150};
 151
 152static struct snd_soc_dai_driver es7134_dai = {
 153        .name = "es7134-hifi",
 154        .playback = {
 155                .stream_name = "Playback",
 156                .channels_min = 2,
 157                .channels_max = 2,
 158                .rates = (SNDRV_PCM_RATE_8000_48000 |
 159                          SNDRV_PCM_RATE_88200      |
 160                          SNDRV_PCM_RATE_96000      |
 161                          SNDRV_PCM_RATE_176400     |
 162                          SNDRV_PCM_RATE_192000),
 163                .formats = (SNDRV_PCM_FMTBIT_S16_LE  |
 164                            SNDRV_PCM_FMTBIT_S18_3LE |
 165                            SNDRV_PCM_FMTBIT_S20_3LE |
 166                            SNDRV_PCM_FMTBIT_S24_3LE |
 167                            SNDRV_PCM_FMTBIT_S24_LE),
 168        },
 169        .ops = &es7134_dai_ops,
 170};
 171
 172static const struct es7134_clock_mode es7134_modes[] = {
 173        {
 174                /* Single speed mode */
 175                .rate_min = 8000,
 176                .rate_max = 50000,
 177                .mclk_fs = (unsigned int[]) { 256, 384, 512, 768, 1024 },
 178                .mclk_fs_num = 5,
 179        }, {
 180                /* Double speed mode */
 181                .rate_min = 84000,
 182                .rate_max = 100000,
 183                .mclk_fs = (unsigned int[]) { 128, 192, 256, 384, 512 },
 184                .mclk_fs_num = 5,
 185        }, {
 186                /* Quad speed mode */
 187                .rate_min = 167000,
 188                .rate_max = 192000,
 189                .mclk_fs = (unsigned int[]) { 128, 192, 256 },
 190                .mclk_fs_num = 3,
 191        },
 192};
 193
 194/* Digital I/O are also supplied by VDD on the es7134 */
 195static const struct snd_soc_dapm_route es7134_extra_routes[] = {
 196        { "Playback", NULL, "VDD", }
 197};
 198
 199static const struct es7134_chip es7134_chip = {
 200        .dai_drv = &es7134_dai,
 201        .modes = es7134_modes,
 202        .mode_num = ARRAY_SIZE(es7134_modes),
 203        .extra_routes = es7134_extra_routes,
 204        .extra_route_num = ARRAY_SIZE(es7134_extra_routes),
 205};
 206
 207static const struct snd_soc_dapm_widget es7134_dapm_widgets[] = {
 208        SND_SOC_DAPM_OUTPUT("AOUTL"),
 209        SND_SOC_DAPM_OUTPUT("AOUTR"),
 210        SND_SOC_DAPM_DAC("DAC", "Playback", SND_SOC_NOPM, 0, 0),
 211        SND_SOC_DAPM_REGULATOR_SUPPLY("VDD", 0, 0),
 212};
 213
 214static const struct snd_soc_dapm_route es7134_dapm_routes[] = {
 215        { "AOUTL", NULL, "DAC" },
 216        { "AOUTR", NULL, "DAC" },
 217        { "DAC", NULL, "VDD" },
 218};
 219
 220static const struct snd_soc_component_driver es7134_component_driver = {
 221        .probe                  = es7134_component_probe,
 222        .dapm_widgets           = es7134_dapm_widgets,
 223        .num_dapm_widgets       = ARRAY_SIZE(es7134_dapm_widgets),
 224        .dapm_routes            = es7134_dapm_routes,
 225        .num_dapm_routes        = ARRAY_SIZE(es7134_dapm_routes),
 226        .idle_bias_on           = 1,
 227        .use_pmdown_time        = 1,
 228        .endianness             = 1,
 229        .non_legacy_dai_naming  = 1,
 230};
 231
 232static struct snd_soc_dai_driver es7154_dai = {
 233        .name = "es7154-hifi",
 234        .playback = {
 235                .stream_name = "Playback",
 236                .channels_min = 2,
 237                .channels_max = 2,
 238                .rates = (SNDRV_PCM_RATE_8000_48000 |
 239                          SNDRV_PCM_RATE_88200      |
 240                          SNDRV_PCM_RATE_96000),
 241                .formats = (SNDRV_PCM_FMTBIT_S16_LE  |
 242                            SNDRV_PCM_FMTBIT_S18_3LE |
 243                            SNDRV_PCM_FMTBIT_S20_3LE |
 244                            SNDRV_PCM_FMTBIT_S24_3LE |
 245                            SNDRV_PCM_FMTBIT_S24_LE),
 246        },
 247        .ops = &es7134_dai_ops,
 248};
 249
 250static const struct es7134_clock_mode es7154_modes[] = {
 251        {
 252                /* Single speed mode */
 253                .rate_min = 8000,
 254                .rate_max = 50000,
 255                .mclk_fs = (unsigned int[]) { 32, 64, 128, 192, 256,
 256                                              384, 512, 768, 1024 },
 257                .mclk_fs_num = 9,
 258        }, {
 259                /* Double speed mode */
 260                .rate_min = 84000,
 261                .rate_max = 100000,
 262                .mclk_fs = (unsigned int[]) { 128, 192, 256, 384, 512,
 263                                              768, 1024},
 264                .mclk_fs_num = 7,
 265        }
 266};
 267
 268/* Es7154 has a separate supply for digital I/O  */
 269static const struct snd_soc_dapm_widget es7154_extra_widgets[] = {
 270        SND_SOC_DAPM_REGULATOR_SUPPLY("PVDD", 0, 0),
 271};
 272
 273static const struct snd_soc_dapm_route es7154_extra_routes[] = {
 274        { "Playback", NULL, "PVDD", }
 275};
 276
 277static const struct es7134_chip es7154_chip = {
 278        .dai_drv = &es7154_dai,
 279        .modes = es7154_modes,
 280        .mode_num = ARRAY_SIZE(es7154_modes),
 281        .extra_routes = es7154_extra_routes,
 282        .extra_route_num = ARRAY_SIZE(es7154_extra_routes),
 283        .extra_widgets = es7154_extra_widgets,
 284        .extra_widget_num = ARRAY_SIZE(es7154_extra_widgets),
 285};
 286
 287static int es7134_probe(struct platform_device *pdev)
 288{
 289        struct device *dev = &pdev->dev;
 290        struct es7134_data *priv;
 291
 292        priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
 293        if (!priv)
 294                return -ENOMEM;
 295        platform_set_drvdata(pdev, priv);
 296
 297        priv->chip = of_device_get_match_data(dev);
 298        if (!priv->chip) {
 299                dev_err(dev, "failed to match device\n");
 300                return -ENODEV;
 301        }
 302
 303        return devm_snd_soc_register_component(&pdev->dev,
 304                                      &es7134_component_driver,
 305                                      priv->chip->dai_drv, 1);
 306}
 307
 308#ifdef CONFIG_OF
 309static const struct of_device_id es7134_ids[] = {
 310        { .compatible = "everest,es7134", .data = &es7134_chip },
 311        { .compatible = "everest,es7144", .data = &es7134_chip },
 312        { .compatible = "everest,es7154", .data = &es7154_chip },
 313        { }
 314};
 315MODULE_DEVICE_TABLE(of, es7134_ids);
 316#endif
 317
 318static struct platform_driver es7134_driver = {
 319        .driver = {
 320                .name = "es7134",
 321                .of_match_table = of_match_ptr(es7134_ids),
 322        },
 323        .probe = es7134_probe,
 324};
 325
 326module_platform_driver(es7134_driver);
 327
 328MODULE_DESCRIPTION("ASoC ES7134 audio codec driver");
 329MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
 330MODULE_LICENSE("GPL");
 331