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