linux/sound/soc/img/pistachio-internal-dac.c
<<
>>
Prefs
   1/*
   2 * Pistachio internal dac driver
   3 *
   4 * Copyright (C) 2015 Imagination Technologies Ltd.
   5 *
   6 * Author: Damien Horsley <Damien.Horsley@imgtec.com>
   7 *
   8 * This program is free software; you can redistribute it and/or modify it
   9 * under the terms and conditions of the GNU General Public License,
  10 * version 2, as published by the Free Software Foundation.
  11 */
  12
  13#include <linux/clk.h>
  14#include <linux/delay.h>
  15#include <linux/mfd/syscon.h>
  16#include <linux/module.h>
  17#include <linux/pm_runtime.h>
  18#include <linux/regmap.h>
  19#include <linux/regulator/consumer.h>
  20
  21#include <sound/pcm_params.h>
  22#include <sound/soc.h>
  23
  24#define PISTACHIO_INTERNAL_DAC_CTRL                     0x40
  25#define PISTACHIO_INTERNAL_DAC_CTRL_PWR_SEL_MASK        0x2
  26#define PISTACHIO_INTERNAL_DAC_CTRL_PWRDN_MASK          0x1
  27
  28#define PISTACHIO_INTERNAL_DAC_SRST                     0x44
  29#define PISTACHIO_INTERNAL_DAC_SRST_MASK                0x1
  30
  31#define PISTACHIO_INTERNAL_DAC_GTI_CTRL                 0x48
  32#define PISTACHIO_INTERNAL_DAC_GTI_CTRL_ADDR_SHIFT      0
  33#define PISTACHIO_INTERNAL_DAC_GTI_CTRL_ADDR_MASK       0xFFF
  34#define PISTACHIO_INTERNAL_DAC_GTI_CTRL_WE_MASK         0x1000
  35#define PISTACHIO_INTERNAL_DAC_GTI_CTRL_WDATA_SHIFT     13
  36#define PISTACHIO_INTERNAL_DAC_GTI_CTRL_WDATA_MASK      0x1FE000
  37
  38#define PISTACHIO_INTERNAL_DAC_PWR                      0x1
  39#define PISTACHIO_INTERNAL_DAC_PWR_MASK                 0x1
  40
  41#define PISTACHIO_INTERNAL_DAC_FORMATS (SNDRV_PCM_FMTBIT_S24_LE |  \
  42                                        SNDRV_PCM_FMTBIT_S32_LE)
  43
  44/* codec private data */
  45struct pistachio_internal_dac {
  46        struct regmap *regmap;
  47        struct regulator *supply;
  48        bool mute;
  49};
  50
  51static const struct snd_kcontrol_new pistachio_internal_dac_snd_controls[] = {
  52        SOC_SINGLE("Playback Switch", PISTACHIO_INTERNAL_DAC_CTRL, 2, 1, 1)
  53};
  54
  55static const struct snd_soc_dapm_widget pistachio_internal_dac_widgets[] = {
  56        SND_SOC_DAPM_DAC("DAC", "Playback", SND_SOC_NOPM, 0, 0),
  57        SND_SOC_DAPM_OUTPUT("AOUTL"),
  58        SND_SOC_DAPM_OUTPUT("AOUTR"),
  59};
  60
  61static const struct snd_soc_dapm_route pistachio_internal_dac_routes[] = {
  62        { "AOUTL", NULL, "DAC" },
  63        { "AOUTR", NULL, "DAC" },
  64};
  65
  66static void pistachio_internal_dac_reg_writel(struct regmap *top_regs,
  67                                                u32 val, u32 reg)
  68{
  69        regmap_update_bits(top_regs, PISTACHIO_INTERNAL_DAC_GTI_CTRL,
  70                        PISTACHIO_INTERNAL_DAC_GTI_CTRL_ADDR_MASK,
  71                        reg << PISTACHIO_INTERNAL_DAC_GTI_CTRL_ADDR_SHIFT);
  72
  73        regmap_update_bits(top_regs, PISTACHIO_INTERNAL_DAC_GTI_CTRL,
  74                        PISTACHIO_INTERNAL_DAC_GTI_CTRL_WDATA_MASK,
  75                        val << PISTACHIO_INTERNAL_DAC_GTI_CTRL_WDATA_SHIFT);
  76
  77        regmap_update_bits(top_regs, PISTACHIO_INTERNAL_DAC_GTI_CTRL,
  78                        PISTACHIO_INTERNAL_DAC_GTI_CTRL_WE_MASK,
  79                        PISTACHIO_INTERNAL_DAC_GTI_CTRL_WE_MASK);
  80
  81        regmap_update_bits(top_regs, PISTACHIO_INTERNAL_DAC_GTI_CTRL,
  82                        PISTACHIO_INTERNAL_DAC_GTI_CTRL_WE_MASK, 0);
  83}
  84
  85static void pistachio_internal_dac_pwr_off(struct pistachio_internal_dac *dac)
  86{
  87        regmap_update_bits(dac->regmap, PISTACHIO_INTERNAL_DAC_CTRL,
  88                PISTACHIO_INTERNAL_DAC_CTRL_PWRDN_MASK,
  89                PISTACHIO_INTERNAL_DAC_CTRL_PWRDN_MASK);
  90
  91        pistachio_internal_dac_reg_writel(dac->regmap, 0,
  92                                        PISTACHIO_INTERNAL_DAC_PWR);
  93}
  94
  95static void pistachio_internal_dac_pwr_on(struct pistachio_internal_dac *dac)
  96{
  97        regmap_update_bits(dac->regmap, PISTACHIO_INTERNAL_DAC_SRST,
  98                        PISTACHIO_INTERNAL_DAC_SRST_MASK,
  99                        PISTACHIO_INTERNAL_DAC_SRST_MASK);
 100
 101        regmap_update_bits(dac->regmap, PISTACHIO_INTERNAL_DAC_SRST,
 102                        PISTACHIO_INTERNAL_DAC_SRST_MASK, 0);
 103
 104        pistachio_internal_dac_reg_writel(dac->regmap,
 105                                        PISTACHIO_INTERNAL_DAC_PWR_MASK,
 106                                        PISTACHIO_INTERNAL_DAC_PWR);
 107
 108        regmap_update_bits(dac->regmap, PISTACHIO_INTERNAL_DAC_CTRL,
 109                        PISTACHIO_INTERNAL_DAC_CTRL_PWRDN_MASK, 0);
 110}
 111
 112static struct snd_soc_dai_driver pistachio_internal_dac_dais[] = {
 113        {
 114                .name = "pistachio_internal_dac",
 115                .playback = {
 116                        .stream_name = "Playback",
 117                        .channels_min = 2,
 118                        .channels_max = 2,
 119                        .rates = SNDRV_PCM_RATE_8000_48000,
 120                        .formats = PISTACHIO_INTERNAL_DAC_FORMATS,
 121                }
 122        },
 123};
 124
 125static int pistachio_internal_dac_codec_probe(struct snd_soc_codec *codec)
 126{
 127        struct pistachio_internal_dac *dac = snd_soc_codec_get_drvdata(codec);
 128
 129        snd_soc_codec_init_regmap(codec, dac->regmap);
 130
 131        return 0;
 132}
 133
 134static const struct snd_soc_codec_driver pistachio_internal_dac_driver = {
 135        .probe = pistachio_internal_dac_codec_probe,
 136        .idle_bias_off = true,
 137        .component_driver = {
 138                .controls               = pistachio_internal_dac_snd_controls,
 139                .num_controls           = ARRAY_SIZE(pistachio_internal_dac_snd_controls),
 140                .dapm_widgets           = pistachio_internal_dac_widgets,
 141                .num_dapm_widgets       = ARRAY_SIZE(pistachio_internal_dac_widgets),
 142                .dapm_routes            = pistachio_internal_dac_routes,
 143                .num_dapm_routes        = ARRAY_SIZE(pistachio_internal_dac_routes),
 144        },
 145};
 146
 147static int pistachio_internal_dac_probe(struct platform_device *pdev)
 148{
 149        struct pistachio_internal_dac *dac;
 150        int ret, voltage;
 151        struct device *dev = &pdev->dev;
 152        u32 reg;
 153
 154        dac = devm_kzalloc(dev, sizeof(*dac), GFP_KERNEL);
 155
 156        if (!dac)
 157                return -ENOMEM;
 158
 159        platform_set_drvdata(pdev, dac);
 160
 161        dac->regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
 162                                                            "img,cr-top");
 163        if (IS_ERR(dac->regmap))
 164                return PTR_ERR(dac->regmap);
 165
 166        dac->supply = devm_regulator_get(dev, "VDD");
 167        if (IS_ERR(dac->supply)) {
 168                ret = PTR_ERR(dac->supply);
 169                if (ret != -EPROBE_DEFER)
 170                        dev_err(dev, "failed to acquire supply 'VDD-supply': %d\n", ret);
 171                return ret;
 172        }
 173
 174        ret = regulator_enable(dac->supply);
 175        if (ret) {
 176                dev_err(dev, "failed to enable supply: %d\n", ret);
 177                return ret;
 178        }
 179
 180        voltage = regulator_get_voltage(dac->supply);
 181
 182        switch (voltage) {
 183        case 1800000:
 184                reg = 0;
 185                break;
 186        case 3300000:
 187                reg = PISTACHIO_INTERNAL_DAC_CTRL_PWR_SEL_MASK;
 188                break;
 189        default:
 190                dev_err(dev, "invalid voltage: %d\n", voltage);
 191                ret = -EINVAL;
 192                goto err_regulator;
 193        }
 194
 195        regmap_update_bits(dac->regmap, PISTACHIO_INTERNAL_DAC_CTRL,
 196                        PISTACHIO_INTERNAL_DAC_CTRL_PWR_SEL_MASK, reg);
 197
 198        pistachio_internal_dac_pwr_off(dac);
 199        pistachio_internal_dac_pwr_on(dac);
 200
 201        pm_runtime_set_active(dev);
 202        pm_runtime_enable(dev);
 203        pm_runtime_idle(dev);
 204
 205        ret = snd_soc_register_codec(dev, &pistachio_internal_dac_driver,
 206                        pistachio_internal_dac_dais,
 207                        ARRAY_SIZE(pistachio_internal_dac_dais));
 208        if (ret) {
 209                dev_err(dev, "failed to register codec: %d\n", ret);
 210                goto err_pwr;
 211        }
 212
 213        return 0;
 214
 215err_pwr:
 216        pm_runtime_disable(&pdev->dev);
 217        pistachio_internal_dac_pwr_off(dac);
 218err_regulator:
 219        regulator_disable(dac->supply);
 220
 221        return ret;
 222}
 223
 224static int pistachio_internal_dac_remove(struct platform_device *pdev)
 225{
 226        struct pistachio_internal_dac *dac = dev_get_drvdata(&pdev->dev);
 227
 228        snd_soc_unregister_codec(&pdev->dev);
 229        pm_runtime_disable(&pdev->dev);
 230        pistachio_internal_dac_pwr_off(dac);
 231        regulator_disable(dac->supply);
 232
 233        return 0;
 234}
 235
 236#ifdef CONFIG_PM
 237static int pistachio_internal_dac_rt_resume(struct device *dev)
 238{
 239        struct pistachio_internal_dac *dac = dev_get_drvdata(dev);
 240        int ret;
 241
 242        ret = regulator_enable(dac->supply);
 243        if (ret) {
 244                dev_err(dev, "failed to enable supply: %d\n", ret);
 245                return ret;
 246        }
 247
 248        pistachio_internal_dac_pwr_on(dac);
 249
 250        return 0;
 251}
 252
 253static int pistachio_internal_dac_rt_suspend(struct device *dev)
 254{
 255        struct pistachio_internal_dac *dac = dev_get_drvdata(dev);
 256
 257        pistachio_internal_dac_pwr_off(dac);
 258
 259        regulator_disable(dac->supply);
 260
 261        return 0;
 262}
 263#endif
 264
 265static const struct dev_pm_ops pistachio_internal_dac_pm_ops = {
 266        SET_RUNTIME_PM_OPS(pistachio_internal_dac_rt_suspend,
 267                        pistachio_internal_dac_rt_resume, NULL)
 268};
 269
 270static const struct of_device_id pistachio_internal_dac_of_match[] = {
 271        { .compatible = "img,pistachio-internal-dac" },
 272        {}
 273};
 274MODULE_DEVICE_TABLE(of, pistachio_internal_dac_of_match);
 275
 276static struct platform_driver pistachio_internal_dac_plat_driver = {
 277        .driver = {
 278                .name = "img-pistachio-internal-dac",
 279                .of_match_table = pistachio_internal_dac_of_match,
 280                .pm = &pistachio_internal_dac_pm_ops
 281        },
 282        .probe = pistachio_internal_dac_probe,
 283        .remove = pistachio_internal_dac_remove
 284};
 285module_platform_driver(pistachio_internal_dac_plat_driver);
 286
 287MODULE_DESCRIPTION("Pistachio Internal DAC driver");
 288MODULE_AUTHOR("Damien Horsley <Damien.Horsley@imgtec.com>");
 289MODULE_LICENSE("GPL v2");
 290