linux/sound/soc/atmel/atmel-classd.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/* Atmel ALSA SoC Audio Class D Amplifier (CLASSD) driver
   3 *
   4 * Copyright (C) 2015 Atmel
   5 *
   6 * Author: Songjun Wu <songjun.wu@atmel.com>
   7 */
   8
   9#include <linux/of.h>
  10#include <linux/clk.h>
  11#include <linux/module.h>
  12#include <linux/platform_device.h>
  13#include <linux/regmap.h>
  14#include <sound/core.h>
  15#include <sound/dmaengine_pcm.h>
  16#include <sound/pcm_params.h>
  17#include <sound/tlv.h>
  18#include "atmel-classd.h"
  19
  20struct atmel_classd_pdata {
  21        bool non_overlap_enable;
  22        int non_overlap_time;
  23        int pwm_type;
  24        const char *card_name;
  25};
  26
  27struct atmel_classd {
  28        dma_addr_t phy_base;
  29        struct regmap *regmap;
  30        struct clk *pclk;
  31        struct clk *gclk;
  32        struct device *dev;
  33        int irq;
  34        const struct atmel_classd_pdata *pdata;
  35};
  36
  37#ifdef CONFIG_OF
  38static const struct of_device_id atmel_classd_of_match[] = {
  39        {
  40                .compatible = "atmel,sama5d2-classd",
  41        }, {
  42                /* sentinel */
  43        }
  44};
  45MODULE_DEVICE_TABLE(of, atmel_classd_of_match);
  46
  47static struct atmel_classd_pdata *atmel_classd_dt_init(struct device *dev)
  48{
  49        struct device_node *np = dev->of_node;
  50        struct atmel_classd_pdata *pdata;
  51        const char *pwm_type_s;
  52        int ret;
  53
  54        if (!np) {
  55                dev_err(dev, "device node not found\n");
  56                return ERR_PTR(-EINVAL);
  57        }
  58
  59        pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
  60        if (!pdata)
  61                return ERR_PTR(-ENOMEM);
  62
  63        ret = of_property_read_string(np, "atmel,pwm-type", &pwm_type_s);
  64        if ((ret == 0) && (strcmp(pwm_type_s, "diff") == 0))
  65                pdata->pwm_type = CLASSD_MR_PWMTYP_DIFF;
  66        else
  67                pdata->pwm_type = CLASSD_MR_PWMTYP_SINGLE;
  68
  69        ret = of_property_read_u32(np,
  70                        "atmel,non-overlap-time", &pdata->non_overlap_time);
  71        if (ret)
  72                pdata->non_overlap_enable = false;
  73        else
  74                pdata->non_overlap_enable = true;
  75
  76        ret = of_property_read_string(np, "atmel,model", &pdata->card_name);
  77        if (ret)
  78                pdata->card_name = "CLASSD";
  79
  80        return pdata;
  81}
  82#else
  83static inline struct atmel_classd_pdata *
  84atmel_classd_dt_init(struct device *dev)
  85{
  86        return ERR_PTR(-EINVAL);
  87}
  88#endif
  89
  90#define ATMEL_CLASSD_RATES (SNDRV_PCM_RATE_8000 \
  91                        | SNDRV_PCM_RATE_16000  | SNDRV_PCM_RATE_22050 \
  92                        | SNDRV_PCM_RATE_32000  | SNDRV_PCM_RATE_44100 \
  93                        | SNDRV_PCM_RATE_48000  | SNDRV_PCM_RATE_88200 \
  94                        | SNDRV_PCM_RATE_96000)
  95
  96static const struct snd_pcm_hardware atmel_classd_hw = {
  97        .info                   = SNDRV_PCM_INFO_MMAP
  98                                | SNDRV_PCM_INFO_MMAP_VALID
  99                                | SNDRV_PCM_INFO_INTERLEAVED
 100                                | SNDRV_PCM_INFO_RESUME
 101                                | SNDRV_PCM_INFO_PAUSE,
 102        .formats                = (SNDRV_PCM_FMTBIT_S16_LE),
 103        .rates                  = ATMEL_CLASSD_RATES,
 104        .rate_min               = 8000,
 105        .rate_max               = 96000,
 106        .channels_min           = 1,
 107        .channels_max           = 2,
 108        .buffer_bytes_max       = 64 * 1024,
 109        .period_bytes_min       = 256,
 110        .period_bytes_max       = 32 * 1024,
 111        .periods_min            = 2,
 112        .periods_max            = 256,
 113};
 114
 115#define ATMEL_CLASSD_PREALLOC_BUF_SIZE  (64 * 1024)
 116
 117/* cpu dai component */
 118static int atmel_classd_cpu_dai_startup(struct snd_pcm_substream *substream,
 119                                        struct snd_soc_dai *cpu_dai)
 120{
 121        struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
 122        struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card);
 123        int err;
 124
 125        regmap_write(dd->regmap, CLASSD_THR, 0x0);
 126
 127        err = clk_prepare_enable(dd->pclk);
 128        if (err)
 129                return err;
 130        err = clk_prepare_enable(dd->gclk);
 131        if (err) {
 132                clk_disable_unprepare(dd->pclk);
 133                return err;
 134        }
 135        return 0;
 136}
 137
 138/* platform */
 139static int
 140atmel_classd_platform_configure_dma(struct snd_pcm_substream *substream,
 141        struct snd_pcm_hw_params *params,
 142        struct dma_slave_config *slave_config)
 143{
 144        struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
 145        struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card);
 146
 147        if (params_physical_width(params) != 16) {
 148                dev_err(dd->dev,
 149                        "only supports 16-bit audio data\n");
 150                return -EINVAL;
 151        }
 152
 153        if (params_channels(params) == 1)
 154                slave_config->dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
 155        else
 156                slave_config->dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
 157
 158        slave_config->direction         = DMA_MEM_TO_DEV;
 159        slave_config->dst_addr          = dd->phy_base + CLASSD_THR;
 160        slave_config->dst_maxburst      = 1;
 161        slave_config->src_maxburst      = 1;
 162        slave_config->device_fc         = false;
 163
 164        return 0;
 165}
 166
 167static const struct snd_dmaengine_pcm_config
 168atmel_classd_dmaengine_pcm_config = {
 169        .prepare_slave_config   = atmel_classd_platform_configure_dma,
 170        .pcm_hardware           = &atmel_classd_hw,
 171        .prealloc_buffer_size   = ATMEL_CLASSD_PREALLOC_BUF_SIZE,
 172};
 173
 174/* codec */
 175static const char * const mono_mode_text[] = {
 176        "mix", "sat", "left", "right"
 177};
 178
 179static SOC_ENUM_SINGLE_DECL(classd_mono_mode_enum,
 180                        CLASSD_INTPMR, CLASSD_INTPMR_MONO_MODE_SHIFT,
 181                        mono_mode_text);
 182
 183static const char * const eqcfg_text[] = {
 184        "Treble-12dB", "Treble-6dB",
 185        "Medium-8dB", "Medium-3dB",
 186        "Bass-12dB", "Bass-6dB",
 187        "0 dB",
 188        "Bass+6dB", "Bass+12dB",
 189        "Medium+3dB", "Medium+8dB",
 190        "Treble+6dB", "Treble+12dB",
 191};
 192
 193static const unsigned int eqcfg_value[] = {
 194        CLASSD_INTPMR_EQCFG_T_CUT_12, CLASSD_INTPMR_EQCFG_T_CUT_6,
 195        CLASSD_INTPMR_EQCFG_M_CUT_8, CLASSD_INTPMR_EQCFG_M_CUT_3,
 196        CLASSD_INTPMR_EQCFG_B_CUT_12, CLASSD_INTPMR_EQCFG_B_CUT_6,
 197        CLASSD_INTPMR_EQCFG_FLAT,
 198        CLASSD_INTPMR_EQCFG_B_BOOST_6, CLASSD_INTPMR_EQCFG_B_BOOST_12,
 199        CLASSD_INTPMR_EQCFG_M_BOOST_3, CLASSD_INTPMR_EQCFG_M_BOOST_8,
 200        CLASSD_INTPMR_EQCFG_T_BOOST_6, CLASSD_INTPMR_EQCFG_T_BOOST_12,
 201};
 202
 203static SOC_VALUE_ENUM_SINGLE_DECL(classd_eqcfg_enum,
 204                CLASSD_INTPMR, CLASSD_INTPMR_EQCFG_SHIFT, 0xf,
 205                eqcfg_text, eqcfg_value);
 206
 207static const DECLARE_TLV_DB_SCALE(classd_digital_tlv, -7800, 100, 1);
 208
 209static const struct snd_kcontrol_new atmel_classd_snd_controls[] = {
 210SOC_DOUBLE_TLV("Playback Volume", CLASSD_INTPMR,
 211                CLASSD_INTPMR_ATTL_SHIFT, CLASSD_INTPMR_ATTR_SHIFT,
 212                78, 1, classd_digital_tlv),
 213
 214SOC_SINGLE("Deemphasis Switch", CLASSD_INTPMR,
 215                CLASSD_INTPMR_DEEMP_SHIFT, 1, 0),
 216
 217SOC_SINGLE("Mono Switch", CLASSD_INTPMR, CLASSD_INTPMR_MONO_SHIFT, 1, 0),
 218
 219SOC_SINGLE("Swap Switch", CLASSD_INTPMR, CLASSD_INTPMR_SWAP_SHIFT, 1, 0),
 220
 221SOC_ENUM("Mono Mode", classd_mono_mode_enum),
 222
 223SOC_ENUM("EQ", classd_eqcfg_enum),
 224};
 225
 226static const char * const pwm_type[] = {
 227        "Single ended", "Differential"
 228};
 229
 230static int atmel_classd_component_probe(struct snd_soc_component *component)
 231{
 232        struct snd_soc_card *card = snd_soc_component_get_drvdata(component);
 233        struct atmel_classd *dd = snd_soc_card_get_drvdata(card);
 234        const struct atmel_classd_pdata *pdata = dd->pdata;
 235        u32 mask, val;
 236
 237        mask = CLASSD_MR_PWMTYP_MASK;
 238        val = pdata->pwm_type << CLASSD_MR_PWMTYP_SHIFT;
 239
 240        mask |= CLASSD_MR_NON_OVERLAP_MASK;
 241        if (pdata->non_overlap_enable) {
 242                val |= (CLASSD_MR_NON_OVERLAP_EN
 243                        << CLASSD_MR_NON_OVERLAP_SHIFT);
 244
 245                mask |= CLASSD_MR_NOVR_VAL_MASK;
 246                switch (pdata->non_overlap_time) {
 247                case 5:
 248                        val |= (CLASSD_MR_NOVR_VAL_5NS
 249                                << CLASSD_MR_NOVR_VAL_SHIFT);
 250                        break;
 251                case 10:
 252                        val |= (CLASSD_MR_NOVR_VAL_10NS
 253                                << CLASSD_MR_NOVR_VAL_SHIFT);
 254                        break;
 255                case 15:
 256                        val |= (CLASSD_MR_NOVR_VAL_15NS
 257                                << CLASSD_MR_NOVR_VAL_SHIFT);
 258                        break;
 259                case 20:
 260                        val |= (CLASSD_MR_NOVR_VAL_20NS
 261                                << CLASSD_MR_NOVR_VAL_SHIFT);
 262                        break;
 263                default:
 264                        val |= (CLASSD_MR_NOVR_VAL_10NS
 265                                << CLASSD_MR_NOVR_VAL_SHIFT);
 266                        dev_warn(component->dev,
 267                                "non-overlapping value %d is invalid, the default value 10 is specified\n",
 268                                pdata->non_overlap_time);
 269                        break;
 270                }
 271        }
 272
 273        snd_soc_component_update_bits(component, CLASSD_MR, mask, val);
 274
 275        dev_info(component->dev,
 276                "PWM modulation type is %s, non-overlapping is %s\n",
 277                pwm_type[pdata->pwm_type],
 278                pdata->non_overlap_enable?"enabled":"disabled");
 279
 280        return 0;
 281}
 282
 283static int atmel_classd_component_resume(struct snd_soc_component *component)
 284{
 285        struct snd_soc_card *card = snd_soc_component_get_drvdata(component);
 286        struct atmel_classd *dd = snd_soc_card_get_drvdata(card);
 287
 288        return regcache_sync(dd->regmap);
 289}
 290
 291static int atmel_classd_cpu_dai_mute_stream(struct snd_soc_dai *cpu_dai,
 292                                            int mute, int direction)
 293{
 294        struct snd_soc_component *component = cpu_dai->component;
 295        u32 mask, val;
 296
 297        mask = CLASSD_MR_LMUTE_MASK | CLASSD_MR_RMUTE_MASK;
 298
 299        if (mute)
 300                val = mask;
 301        else
 302                val = 0;
 303
 304        snd_soc_component_update_bits(component, CLASSD_MR, mask, val);
 305
 306        return 0;
 307}
 308
 309#define CLASSD_GCLK_RATE_11M2896_MPY_8 (112896 * 100 * 8)
 310#define CLASSD_GCLK_RATE_12M288_MPY_8  (12288 * 1000 * 8)
 311
 312static struct {
 313        int rate;
 314        int sample_rate;
 315        int dsp_clk;
 316        unsigned long gclk_rate;
 317} const sample_rates[] = {
 318        { 8000,  CLASSD_INTPMR_FRAME_8K,
 319        CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_GCLK_RATE_12M288_MPY_8 },
 320        { 16000, CLASSD_INTPMR_FRAME_16K,
 321        CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_GCLK_RATE_12M288_MPY_8 },
 322        { 32000, CLASSD_INTPMR_FRAME_32K,
 323        CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_GCLK_RATE_12M288_MPY_8 },
 324        { 48000, CLASSD_INTPMR_FRAME_48K,
 325        CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_GCLK_RATE_12M288_MPY_8 },
 326        { 96000, CLASSD_INTPMR_FRAME_96K,
 327        CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_GCLK_RATE_12M288_MPY_8 },
 328        { 22050, CLASSD_INTPMR_FRAME_22K,
 329        CLASSD_INTPMR_DSP_CLK_FREQ_11M2896, CLASSD_GCLK_RATE_11M2896_MPY_8 },
 330        { 44100, CLASSD_INTPMR_FRAME_44K,
 331        CLASSD_INTPMR_DSP_CLK_FREQ_11M2896, CLASSD_GCLK_RATE_11M2896_MPY_8 },
 332        { 88200, CLASSD_INTPMR_FRAME_88K,
 333        CLASSD_INTPMR_DSP_CLK_FREQ_11M2896, CLASSD_GCLK_RATE_11M2896_MPY_8 },
 334};
 335
 336static int
 337atmel_classd_cpu_dai_hw_params(struct snd_pcm_substream *substream,
 338                               struct snd_pcm_hw_params *params,
 339                               struct snd_soc_dai *cpu_dai)
 340{
 341        struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
 342        struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card);
 343        struct snd_soc_component *component = cpu_dai->component;
 344        int fs;
 345        int i, best, best_val, cur_val, ret;
 346        u32 mask, val;
 347
 348        fs = params_rate(params);
 349
 350        best = 0;
 351        best_val = abs(fs - sample_rates[0].rate);
 352        for (i = 1; i < ARRAY_SIZE(sample_rates); i++) {
 353                /* Closest match */
 354                cur_val = abs(fs - sample_rates[i].rate);
 355                if (cur_val < best_val) {
 356                        best = i;
 357                        best_val = cur_val;
 358                }
 359        }
 360
 361        dev_dbg(component->dev,
 362                "Selected SAMPLE_RATE of %dHz, GCLK_RATE of %ldHz\n",
 363                sample_rates[best].rate, sample_rates[best].gclk_rate);
 364
 365        clk_disable_unprepare(dd->gclk);
 366
 367        ret = clk_set_rate(dd->gclk, sample_rates[best].gclk_rate);
 368        if (ret)
 369                return ret;
 370
 371        mask = CLASSD_INTPMR_DSP_CLK_FREQ_MASK | CLASSD_INTPMR_FRAME_MASK;
 372        val = (sample_rates[best].dsp_clk << CLASSD_INTPMR_DSP_CLK_FREQ_SHIFT)
 373        | (sample_rates[best].sample_rate << CLASSD_INTPMR_FRAME_SHIFT);
 374
 375        snd_soc_component_update_bits(component, CLASSD_INTPMR, mask, val);
 376
 377        return clk_prepare_enable(dd->gclk);
 378}
 379
 380static void
 381atmel_classd_cpu_dai_shutdown(struct snd_pcm_substream *substream,
 382                              struct snd_soc_dai *cpu_dai)
 383{
 384        struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
 385        struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card);
 386
 387        clk_disable_unprepare(dd->gclk);
 388}
 389
 390static int atmel_classd_cpu_dai_prepare(struct snd_pcm_substream *substream,
 391                                        struct snd_soc_dai *cpu_dai)
 392{
 393        struct snd_soc_component *component = cpu_dai->component;
 394
 395        snd_soc_component_update_bits(component, CLASSD_MR,
 396                                CLASSD_MR_LEN_MASK | CLASSD_MR_REN_MASK,
 397                                (CLASSD_MR_LEN_DIS << CLASSD_MR_LEN_SHIFT)
 398                                |(CLASSD_MR_REN_DIS << CLASSD_MR_REN_SHIFT));
 399
 400        return 0;
 401}
 402
 403static int atmel_classd_cpu_dai_trigger(struct snd_pcm_substream *substream,
 404                                        int cmd, struct snd_soc_dai *cpu_dai)
 405{
 406        struct snd_soc_component *component = cpu_dai->component;
 407        u32 mask, val;
 408
 409        mask = CLASSD_MR_LEN_MASK | CLASSD_MR_REN_MASK;
 410
 411        switch (cmd) {
 412        case SNDRV_PCM_TRIGGER_START:
 413        case SNDRV_PCM_TRIGGER_RESUME:
 414        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 415                val = mask;
 416                break;
 417        case SNDRV_PCM_TRIGGER_STOP:
 418        case SNDRV_PCM_TRIGGER_SUSPEND:
 419        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 420                val = (CLASSD_MR_LEN_DIS << CLASSD_MR_LEN_SHIFT)
 421                        | (CLASSD_MR_REN_DIS << CLASSD_MR_REN_SHIFT);
 422                break;
 423        default:
 424                return -EINVAL;
 425        }
 426
 427        snd_soc_component_update_bits(component, CLASSD_MR, mask, val);
 428
 429        return 0;
 430}
 431
 432static const struct snd_soc_dai_ops atmel_classd_cpu_dai_ops = {
 433        .startup        = atmel_classd_cpu_dai_startup,
 434        .shutdown       = atmel_classd_cpu_dai_shutdown,
 435        .mute_stream    = atmel_classd_cpu_dai_mute_stream,
 436        .hw_params      = atmel_classd_cpu_dai_hw_params,
 437        .prepare        = atmel_classd_cpu_dai_prepare,
 438        .trigger        = atmel_classd_cpu_dai_trigger,
 439        .no_capture_mute = 1,
 440};
 441
 442static struct snd_soc_dai_driver atmel_classd_cpu_dai = {
 443        .playback = {
 444                .stream_name    = "Playback",
 445                .channels_min   = 1,
 446                .channels_max   = 2,
 447                .rates          = ATMEL_CLASSD_RATES,
 448                .formats        = SNDRV_PCM_FMTBIT_S16_LE,
 449        },
 450        .ops = &atmel_classd_cpu_dai_ops,
 451};
 452
 453static const struct snd_soc_component_driver atmel_classd_cpu_dai_component = {
 454        .name                   = "atmel-classd",
 455        .probe                  = atmel_classd_component_probe,
 456        .resume                 = atmel_classd_component_resume,
 457        .controls               = atmel_classd_snd_controls,
 458        .num_controls           = ARRAY_SIZE(atmel_classd_snd_controls),
 459        .idle_bias_on           = 1,
 460        .use_pmdown_time        = 1,
 461};
 462
 463/* ASoC sound card */
 464static int atmel_classd_asoc_card_init(struct device *dev,
 465                                        struct snd_soc_card *card)
 466{
 467        struct snd_soc_dai_link *dai_link;
 468        struct atmel_classd *dd = snd_soc_card_get_drvdata(card);
 469        struct snd_soc_dai_link_component *comp;
 470
 471        dai_link = devm_kzalloc(dev, sizeof(*dai_link), GFP_KERNEL);
 472        if (!dai_link)
 473                return -ENOMEM;
 474
 475        comp = devm_kzalloc(dev, 3 * sizeof(*comp), GFP_KERNEL);
 476        if (!comp)
 477                return -ENOMEM;
 478
 479        dai_link->cpus          = &comp[0];
 480        dai_link->codecs        = &comp[1];
 481        dai_link->platforms     = &comp[2];
 482
 483        dai_link->num_cpus      = 1;
 484        dai_link->num_codecs    = 1;
 485        dai_link->num_platforms = 1;
 486
 487        dai_link->name                  = "CLASSD";
 488        dai_link->stream_name           = "CLASSD PCM";
 489        dai_link->codecs->dai_name      = "snd-soc-dummy-dai";
 490        dai_link->cpus->dai_name        = dev_name(dev);
 491        dai_link->codecs->name          = "snd-soc-dummy";
 492        dai_link->platforms->name       = dev_name(dev);
 493
 494        card->dai_link  = dai_link;
 495        card->num_links = 1;
 496        card->name      = dd->pdata->card_name;
 497        card->dev       = dev;
 498
 499        return 0;
 500};
 501
 502/* regmap configuration */
 503static const struct reg_default atmel_classd_reg_defaults[] = {
 504        { CLASSD_INTPMR,   0x00301212 },
 505};
 506
 507#define ATMEL_CLASSD_REG_MAX    0xE4
 508static const struct regmap_config atmel_classd_regmap_config = {
 509        .reg_bits       = 32,
 510        .reg_stride     = 4,
 511        .val_bits       = 32,
 512        .max_register   = ATMEL_CLASSD_REG_MAX,
 513
 514        .cache_type             = REGCACHE_FLAT,
 515        .reg_defaults           = atmel_classd_reg_defaults,
 516        .num_reg_defaults       = ARRAY_SIZE(atmel_classd_reg_defaults),
 517};
 518
 519static int atmel_classd_probe(struct platform_device *pdev)
 520{
 521        struct device *dev = &pdev->dev;
 522        struct atmel_classd *dd;
 523        struct resource *res;
 524        void __iomem *io_base;
 525        const struct atmel_classd_pdata *pdata;
 526        struct snd_soc_card *card;
 527        int ret;
 528
 529        pdata = dev_get_platdata(dev);
 530        if (!pdata) {
 531                pdata = atmel_classd_dt_init(dev);
 532                if (IS_ERR(pdata))
 533                        return PTR_ERR(pdata);
 534        }
 535
 536        dd = devm_kzalloc(dev, sizeof(*dd), GFP_KERNEL);
 537        if (!dd)
 538                return -ENOMEM;
 539
 540        dd->pdata = pdata;
 541
 542        dd->irq = platform_get_irq(pdev, 0);
 543        if (dd->irq < 0)
 544                return dd->irq;
 545
 546        dd->pclk = devm_clk_get(dev, "pclk");
 547        if (IS_ERR(dd->pclk)) {
 548                ret = PTR_ERR(dd->pclk);
 549                dev_err(dev, "failed to get peripheral clock: %d\n", ret);
 550                return ret;
 551        }
 552
 553        dd->gclk = devm_clk_get(dev, "gclk");
 554        if (IS_ERR(dd->gclk)) {
 555                ret = PTR_ERR(dd->gclk);
 556                dev_err(dev, "failed to get GCK clock: %d\n", ret);
 557                return ret;
 558        }
 559
 560        io_base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
 561        if (IS_ERR(io_base))
 562                return PTR_ERR(io_base);
 563
 564        dd->phy_base = res->start;
 565        dd->dev = dev;
 566
 567        dd->regmap = devm_regmap_init_mmio(dev, io_base,
 568                                        &atmel_classd_regmap_config);
 569        if (IS_ERR(dd->regmap)) {
 570                ret = PTR_ERR(dd->regmap);
 571                dev_err(dev, "failed to init register map: %d\n", ret);
 572                return ret;
 573        }
 574
 575        ret = devm_snd_soc_register_component(dev,
 576                                        &atmel_classd_cpu_dai_component,
 577                                        &atmel_classd_cpu_dai, 1);
 578        if (ret) {
 579                dev_err(dev, "could not register CPU DAI: %d\n", ret);
 580                return ret;
 581        }
 582
 583        ret = devm_snd_dmaengine_pcm_register(dev,
 584                                        &atmel_classd_dmaengine_pcm_config,
 585                                        0);
 586        if (ret) {
 587                dev_err(dev, "could not register platform: %d\n", ret);
 588                return ret;
 589        }
 590
 591        /* register sound card */
 592        card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
 593        if (!card) {
 594                ret = -ENOMEM;
 595                goto unregister_codec;
 596        }
 597
 598        snd_soc_card_set_drvdata(card, dd);
 599
 600        ret = atmel_classd_asoc_card_init(dev, card);
 601        if (ret) {
 602                dev_err(dev, "failed to init sound card\n");
 603                goto unregister_codec;
 604        }
 605
 606        ret = devm_snd_soc_register_card(dev, card);
 607        if (ret) {
 608                dev_err(dev, "failed to register sound card: %d\n", ret);
 609                goto unregister_codec;
 610        }
 611
 612        return 0;
 613
 614unregister_codec:
 615        return ret;
 616}
 617
 618static int atmel_classd_remove(struct platform_device *pdev)
 619{
 620        return 0;
 621}
 622
 623static struct platform_driver atmel_classd_driver = {
 624        .driver = {
 625                .name           = "atmel-classd",
 626                .of_match_table = of_match_ptr(atmel_classd_of_match),
 627                .pm             = &snd_soc_pm_ops,
 628        },
 629        .probe  = atmel_classd_probe,
 630        .remove = atmel_classd_remove,
 631};
 632module_platform_driver(atmel_classd_driver);
 633
 634MODULE_DESCRIPTION("Atmel ClassD driver under ALSA SoC architecture");
 635MODULE_AUTHOR("Songjun Wu <songjun.wu@atmel.com>");
 636MODULE_LICENSE("GPL");
 637