linux/sound/soc/uniphier/aio-cpu.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2//
   3// Socionext UniPhier AIO ALSA CPU DAI driver.
   4//
   5// Copyright (c) 2016-2018 Socionext Inc.
   6
   7#include <linux/clk.h>
   8#include <linux/errno.h>
   9#include <linux/kernel.h>
  10#include <linux/mfd/syscon.h>
  11#include <linux/module.h>
  12#include <linux/of.h>
  13#include <linux/of_platform.h>
  14#include <linux/platform_device.h>
  15#include <linux/reset.h>
  16#include <sound/core.h>
  17#include <sound/pcm.h>
  18#include <sound/pcm_params.h>
  19#include <sound/soc.h>
  20
  21#include "aio.h"
  22
  23static bool is_valid_pll(struct uniphier_aio_chip *chip, int pll_id)
  24{
  25        struct device *dev = &chip->pdev->dev;
  26
  27        if (pll_id < 0 || chip->num_plls <= pll_id) {
  28                dev_err(dev, "PLL(%d) is not supported\n", pll_id);
  29                return false;
  30        }
  31
  32        return chip->plls[pll_id].enable;
  33}
  34
  35/**
  36 * find_volume - find volume supported HW port by HW port number
  37 * @chip: the AIO chip pointer
  38 * @oport_hw: HW port number, one of AUD_HW_XXXX
  39 *
  40 * Find AIO device from device list by HW port number. Volume feature is
  41 * available only in Output and PCM ports, this limitation comes from HW
  42 * specifications.
  43 *
  44 * Return: The pointer of AIO substream if successful, otherwise NULL on error.
  45 */
  46static struct uniphier_aio_sub *find_volume(struct uniphier_aio_chip *chip,
  47                                            int oport_hw)
  48{
  49        int i;
  50
  51        for (i = 0; i < chip->num_aios; i++) {
  52                struct uniphier_aio_sub *sub = &chip->aios[i].sub[0];
  53
  54                if (!sub->swm)
  55                        continue;
  56
  57                if (sub->swm->oport.hw == oport_hw)
  58                        return sub;
  59        }
  60
  61        return NULL;
  62}
  63
  64static bool match_spec(const struct uniphier_aio_spec *spec,
  65                       const char *name, int dir)
  66{
  67        if (dir == SNDRV_PCM_STREAM_PLAYBACK &&
  68            spec->swm.dir != PORT_DIR_OUTPUT) {
  69                return false;
  70        }
  71
  72        if (dir == SNDRV_PCM_STREAM_CAPTURE &&
  73            spec->swm.dir != PORT_DIR_INPUT) {
  74                return false;
  75        }
  76
  77        if (spec->name && strcmp(spec->name, name) == 0)
  78                return true;
  79
  80        if (spec->gname && strcmp(spec->gname, name) == 0)
  81                return true;
  82
  83        return false;
  84}
  85
  86/**
  87 * find_spec - find HW specification info by name
  88 * @aio: the AIO device pointer
  89 * @name: name of device
  90 * @direction: the direction of substream, SNDRV_PCM_STREAM_*
  91 *
  92 * Find hardware specification information from list by device name. This
  93 * information is used for telling the difference of SoCs to driver.
  94 *
  95 * Specification list is array of 'struct uniphier_aio_spec' which is defined
  96 * in each drivers (see: aio-i2s.c).
  97 *
  98 * Return: The pointer of hardware specification of AIO if successful,
  99 * otherwise NULL on error.
 100 */
 101static const struct uniphier_aio_spec *find_spec(struct uniphier_aio *aio,
 102                                                 const char *name,
 103                                                 int direction)
 104{
 105        const struct uniphier_aio_chip_spec *chip_spec = aio->chip->chip_spec;
 106        int i;
 107
 108        for (i = 0; i < chip_spec->num_specs; i++) {
 109                const struct uniphier_aio_spec *spec = &chip_spec->specs[i];
 110
 111                if (match_spec(spec, name, direction))
 112                        return spec;
 113        }
 114
 115        return NULL;
 116}
 117
 118/**
 119 * find_divider - find clock divider by frequency
 120 * @aio: the AIO device pointer
 121 * @pll_id: PLL ID, should be AUD_PLL_XX
 122 * @freq: required frequency
 123 *
 124 * Find suitable clock divider by frequency.
 125 *
 126 * Return: The ID of PLL if successful, otherwise negative error value.
 127 */
 128static int find_divider(struct uniphier_aio *aio, int pll_id, unsigned int freq)
 129{
 130        struct uniphier_aio_pll *pll;
 131        int mul[] = { 1, 1, 1, 2, };
 132        int div[] = { 2, 3, 1, 3, };
 133        int i;
 134
 135        if (!is_valid_pll(aio->chip, pll_id))
 136                return -EINVAL;
 137
 138        pll = &aio->chip->plls[pll_id];
 139        for (i = 0; i < ARRAY_SIZE(mul); i++)
 140                if (pll->freq * mul[i] / div[i] == freq)
 141                        return i;
 142
 143        return -ENOTSUPP;
 144}
 145
 146static int uniphier_aio_set_sysclk(struct snd_soc_dai *dai, int clk_id,
 147                                   unsigned int freq, int dir)
 148{
 149        struct uniphier_aio *aio = uniphier_priv(dai);
 150        struct device *dev = &aio->chip->pdev->dev;
 151        bool pll_auto = false;
 152        int pll_id, div_id;
 153
 154        switch (clk_id) {
 155        case AUD_CLK_IO:
 156                return -ENOTSUPP;
 157        case AUD_CLK_A1:
 158                pll_id = AUD_PLL_A1;
 159                break;
 160        case AUD_CLK_F1:
 161                pll_id = AUD_PLL_F1;
 162                break;
 163        case AUD_CLK_A2:
 164                pll_id = AUD_PLL_A2;
 165                break;
 166        case AUD_CLK_F2:
 167                pll_id = AUD_PLL_F2;
 168                break;
 169        case AUD_CLK_A:
 170                pll_id = AUD_PLL_A1;
 171                pll_auto = true;
 172                break;
 173        case AUD_CLK_F:
 174                pll_id = AUD_PLL_F1;
 175                pll_auto = true;
 176                break;
 177        case AUD_CLK_APLL:
 178                pll_id = AUD_PLL_APLL;
 179                break;
 180        case AUD_CLK_RX0:
 181                pll_id = AUD_PLL_RX0;
 182                break;
 183        case AUD_CLK_USB0:
 184                pll_id = AUD_PLL_USB0;
 185                break;
 186        case AUD_CLK_HSC0:
 187                pll_id = AUD_PLL_HSC0;
 188                break;
 189        default:
 190                dev_err(dev, "Sysclk(%d) is not supported\n", clk_id);
 191                return -EINVAL;
 192        }
 193
 194        if (pll_auto) {
 195                for (pll_id = 0; pll_id < aio->chip->num_plls; pll_id++) {
 196                        div_id = find_divider(aio, pll_id, freq);
 197                        if (div_id >= 0) {
 198                                aio->plldiv = div_id;
 199                                break;
 200                        }
 201                }
 202                if (pll_id == aio->chip->num_plls) {
 203                        dev_err(dev, "Sysclk frequency is not supported(%d)\n",
 204                                freq);
 205                        return -EINVAL;
 206                }
 207        }
 208
 209        if (dir == SND_SOC_CLOCK_OUT)
 210                aio->pll_out = pll_id;
 211        else
 212                aio->pll_in = pll_id;
 213
 214        return 0;
 215}
 216
 217static int uniphier_aio_set_pll(struct snd_soc_dai *dai, int pll_id,
 218                                int source, unsigned int freq_in,
 219                                unsigned int freq_out)
 220{
 221        struct uniphier_aio *aio = uniphier_priv(dai);
 222        int ret;
 223
 224        if (!is_valid_pll(aio->chip, pll_id))
 225                return -EINVAL;
 226
 227        ret = aio_chip_set_pll(aio->chip, pll_id, freq_out);
 228        if (ret < 0)
 229                return ret;
 230
 231        return 0;
 232}
 233
 234static int uniphier_aio_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
 235{
 236        struct uniphier_aio *aio = uniphier_priv(dai);
 237        struct device *dev = &aio->chip->pdev->dev;
 238
 239        switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
 240        case SND_SOC_DAIFMT_LEFT_J:
 241        case SND_SOC_DAIFMT_RIGHT_J:
 242        case SND_SOC_DAIFMT_I2S:
 243                aio->fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
 244                break;
 245        default:
 246                dev_err(dev, "Format is not supported(%d)\n",
 247                        fmt & SND_SOC_DAIFMT_FORMAT_MASK);
 248                return -EINVAL;
 249        }
 250
 251        return 0;
 252}
 253
 254static int uniphier_aio_startup(struct snd_pcm_substream *substream,
 255                                struct snd_soc_dai *dai)
 256{
 257        struct uniphier_aio *aio = uniphier_priv(dai);
 258        struct uniphier_aio_sub *sub = &aio->sub[substream->stream];
 259        int ret;
 260
 261        sub->substream = substream;
 262        sub->pass_through = 0;
 263        sub->use_mmap = true;
 264
 265        ret = aio_init(sub);
 266        if (ret)
 267                return ret;
 268
 269        return 0;
 270}
 271
 272static void uniphier_aio_shutdown(struct snd_pcm_substream *substream,
 273                                  struct snd_soc_dai *dai)
 274{
 275        struct uniphier_aio *aio = uniphier_priv(dai);
 276        struct uniphier_aio_sub *sub = &aio->sub[substream->stream];
 277
 278        sub->substream = NULL;
 279}
 280
 281static int uniphier_aio_hw_params(struct snd_pcm_substream *substream,
 282                                  struct snd_pcm_hw_params *params,
 283                                  struct snd_soc_dai *dai)
 284{
 285        struct uniphier_aio *aio = uniphier_priv(dai);
 286        struct uniphier_aio_sub *sub = &aio->sub[substream->stream];
 287        struct device *dev = &aio->chip->pdev->dev;
 288        int freq, ret;
 289
 290        switch (params_rate(params)) {
 291        case 48000:
 292        case 32000:
 293        case 24000:
 294                freq = 12288000;
 295                break;
 296        case 44100:
 297        case 22050:
 298                freq = 11289600;
 299                break;
 300        default:
 301                dev_err(dev, "Rate is not supported(%d)\n",
 302                        params_rate(params));
 303                return -EINVAL;
 304        }
 305        ret = snd_soc_dai_set_sysclk(dai, AUD_CLK_A,
 306                                     freq, SND_SOC_CLOCK_OUT);
 307        if (ret)
 308                return ret;
 309
 310        sub->params = *params;
 311        sub->setting = 1;
 312
 313        aio_port_reset(sub);
 314        aio_port_set_volume(sub, sub->vol);
 315        aio_src_reset(sub);
 316
 317        return 0;
 318}
 319
 320static int uniphier_aio_hw_free(struct snd_pcm_substream *substream,
 321                                struct snd_soc_dai *dai)
 322{
 323        struct uniphier_aio *aio = uniphier_priv(dai);
 324        struct uniphier_aio_sub *sub = &aio->sub[substream->stream];
 325
 326        sub->setting = 0;
 327
 328        return 0;
 329}
 330
 331static int uniphier_aio_prepare(struct snd_pcm_substream *substream,
 332                                struct snd_soc_dai *dai)
 333{
 334        struct uniphier_aio *aio = uniphier_priv(dai);
 335        struct uniphier_aio_sub *sub = &aio->sub[substream->stream];
 336        int ret;
 337
 338        ret = aio_port_set_param(sub, sub->pass_through, &sub->params);
 339        if (ret)
 340                return ret;
 341        ret = aio_src_set_param(sub, &sub->params);
 342        if (ret)
 343                return ret;
 344        aio_port_set_enable(sub, 1);
 345
 346        ret = aio_if_set_param(sub, sub->pass_through);
 347        if (ret)
 348                return ret;
 349
 350        if (sub->swm->type == PORT_TYPE_CONV) {
 351                ret = aio_srcif_set_param(sub);
 352                if (ret)
 353                        return ret;
 354                ret = aio_srcch_set_param(sub);
 355                if (ret)
 356                        return ret;
 357                aio_srcch_set_enable(sub, 1);
 358        }
 359
 360        return 0;
 361}
 362
 363const struct snd_soc_dai_ops uniphier_aio_i2s_ops = {
 364        .set_sysclk  = uniphier_aio_set_sysclk,
 365        .set_pll     = uniphier_aio_set_pll,
 366        .set_fmt     = uniphier_aio_set_fmt,
 367        .startup     = uniphier_aio_startup,
 368        .shutdown    = uniphier_aio_shutdown,
 369        .hw_params   = uniphier_aio_hw_params,
 370        .hw_free     = uniphier_aio_hw_free,
 371        .prepare     = uniphier_aio_prepare,
 372};
 373EXPORT_SYMBOL_GPL(uniphier_aio_i2s_ops);
 374
 375const struct snd_soc_dai_ops uniphier_aio_spdif_ops = {
 376        .set_sysclk  = uniphier_aio_set_sysclk,
 377        .set_pll     = uniphier_aio_set_pll,
 378        .startup     = uniphier_aio_startup,
 379        .shutdown    = uniphier_aio_shutdown,
 380        .hw_params   = uniphier_aio_hw_params,
 381        .hw_free     = uniphier_aio_hw_free,
 382        .prepare     = uniphier_aio_prepare,
 383};
 384EXPORT_SYMBOL_GPL(uniphier_aio_spdif_ops);
 385
 386int uniphier_aio_dai_probe(struct snd_soc_dai *dai)
 387{
 388        struct uniphier_aio *aio = uniphier_priv(dai);
 389        int i;
 390
 391        for (i = 0; i < ARRAY_SIZE(aio->sub); i++) {
 392                struct uniphier_aio_sub *sub = &aio->sub[i];
 393                const struct uniphier_aio_spec *spec;
 394
 395                spec = find_spec(aio, dai->name, i);
 396                if (!spec)
 397                        continue;
 398
 399                sub->swm = &spec->swm;
 400                sub->spec = spec;
 401
 402                sub->vol = AUD_VOL_INIT;
 403        }
 404
 405        aio_iecout_set_enable(aio->chip, true);
 406        aio_chip_init(aio->chip);
 407        aio->chip->active = 1;
 408
 409        return 0;
 410}
 411EXPORT_SYMBOL_GPL(uniphier_aio_dai_probe);
 412
 413int uniphier_aio_dai_remove(struct snd_soc_dai *dai)
 414{
 415        struct uniphier_aio *aio = uniphier_priv(dai);
 416
 417        aio->chip->active = 0;
 418
 419        return 0;
 420}
 421EXPORT_SYMBOL_GPL(uniphier_aio_dai_remove);
 422
 423int uniphier_aio_dai_suspend(struct snd_soc_dai *dai)
 424{
 425        struct uniphier_aio *aio = uniphier_priv(dai);
 426
 427        reset_control_assert(aio->chip->rst);
 428        clk_disable_unprepare(aio->chip->clk);
 429
 430        return 0;
 431}
 432EXPORT_SYMBOL_GPL(uniphier_aio_dai_suspend);
 433
 434int uniphier_aio_dai_resume(struct snd_soc_dai *dai)
 435{
 436        struct uniphier_aio *aio = uniphier_priv(dai);
 437        int ret, i;
 438
 439        if (!aio->chip->active)
 440                return 0;
 441
 442        ret = clk_prepare_enable(aio->chip->clk);
 443        if (ret)
 444                return ret;
 445
 446        ret = reset_control_deassert(aio->chip->rst);
 447        if (ret)
 448                goto err_out_clock;
 449
 450        aio_iecout_set_enable(aio->chip, true);
 451        aio_chip_init(aio->chip);
 452
 453        for (i = 0; i < ARRAY_SIZE(aio->sub); i++) {
 454                struct uniphier_aio_sub *sub = &aio->sub[i];
 455
 456                if (!sub->spec || !sub->substream)
 457                        continue;
 458
 459                ret = aio_init(sub);
 460                if (ret)
 461                        goto err_out_clock;
 462
 463                if (!sub->setting)
 464                        continue;
 465
 466                aio_port_reset(sub);
 467                aio_src_reset(sub);
 468        }
 469
 470        return 0;
 471
 472err_out_clock:
 473        clk_disable_unprepare(aio->chip->clk);
 474
 475        return ret;
 476}
 477EXPORT_SYMBOL_GPL(uniphier_aio_dai_resume);
 478
 479static int uniphier_aio_vol_info(struct snd_kcontrol *kcontrol,
 480                                 struct snd_ctl_elem_info *uinfo)
 481{
 482        uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
 483        uinfo->count = 1;
 484        uinfo->value.integer.min = 0;
 485        uinfo->value.integer.max = AUD_VOL_MAX;
 486
 487        return 0;
 488}
 489
 490static int uniphier_aio_vol_get(struct snd_kcontrol *kcontrol,
 491                                struct snd_ctl_elem_value *ucontrol)
 492{
 493        struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
 494        struct uniphier_aio_chip *chip = snd_soc_component_get_drvdata(comp);
 495        struct uniphier_aio_sub *sub;
 496        int oport_hw = kcontrol->private_value;
 497
 498        sub = find_volume(chip, oport_hw);
 499        if (!sub)
 500                return 0;
 501
 502        ucontrol->value.integer.value[0] = sub->vol;
 503
 504        return 0;
 505}
 506
 507static int uniphier_aio_vol_put(struct snd_kcontrol *kcontrol,
 508                                struct snd_ctl_elem_value *ucontrol)
 509{
 510        struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
 511        struct uniphier_aio_chip *chip = snd_soc_component_get_drvdata(comp);
 512        struct uniphier_aio_sub *sub;
 513        int oport_hw = kcontrol->private_value;
 514
 515        sub = find_volume(chip, oport_hw);
 516        if (!sub)
 517                return 0;
 518
 519        if (sub->vol == ucontrol->value.integer.value[0])
 520                return 0;
 521        sub->vol = ucontrol->value.integer.value[0];
 522
 523        aio_port_set_volume(sub, sub->vol);
 524
 525        return 0;
 526}
 527
 528static const struct snd_kcontrol_new uniphier_aio_controls[] = {
 529        {
 530                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 531                .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
 532                .name = "HPCMOUT1 Volume",
 533                .info = uniphier_aio_vol_info,
 534                .get = uniphier_aio_vol_get,
 535                .put = uniphier_aio_vol_put,
 536                .private_value = AUD_HW_HPCMOUT1,
 537        },
 538        {
 539                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 540                .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
 541                .name = "PCMOUT1 Volume",
 542                .info = uniphier_aio_vol_info,
 543                .get = uniphier_aio_vol_get,
 544                .put = uniphier_aio_vol_put,
 545                .private_value = AUD_HW_PCMOUT1,
 546        },
 547        {
 548                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 549                .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
 550                .name = "PCMOUT2 Volume",
 551                .info = uniphier_aio_vol_info,
 552                .get = uniphier_aio_vol_get,
 553                .put = uniphier_aio_vol_put,
 554                .private_value = AUD_HW_PCMOUT2,
 555        },
 556        {
 557                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 558                .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
 559                .name = "PCMOUT3 Volume",
 560                .info = uniphier_aio_vol_info,
 561                .get = uniphier_aio_vol_get,
 562                .put = uniphier_aio_vol_put,
 563                .private_value = AUD_HW_PCMOUT3,
 564        },
 565        {
 566                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 567                .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
 568                .name = "HIECOUT1 Volume",
 569                .info = uniphier_aio_vol_info,
 570                .get = uniphier_aio_vol_get,
 571                .put = uniphier_aio_vol_put,
 572                .private_value = AUD_HW_HIECOUT1,
 573        },
 574        {
 575                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 576                .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
 577                .name = "IECOUT1 Volume",
 578                .info = uniphier_aio_vol_info,
 579                .get = uniphier_aio_vol_get,
 580                .put = uniphier_aio_vol_put,
 581                .private_value = AUD_HW_IECOUT1,
 582        },
 583};
 584
 585static const struct snd_soc_component_driver uniphier_aio_component = {
 586        .name = "uniphier-aio",
 587        .controls = uniphier_aio_controls,
 588        .num_controls = ARRAY_SIZE(uniphier_aio_controls),
 589};
 590
 591int uniphier_aio_probe(struct platform_device *pdev)
 592{
 593        struct uniphier_aio_chip *chip;
 594        struct device *dev = &pdev->dev;
 595        int ret, i, j;
 596
 597        chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
 598        if (!chip)
 599                return -ENOMEM;
 600
 601        chip->chip_spec = of_device_get_match_data(dev);
 602        if (!chip->chip_spec)
 603                return -EINVAL;
 604
 605        chip->regmap_sg = syscon_regmap_lookup_by_phandle(dev->of_node,
 606                                                          "socionext,syscon");
 607        if (IS_ERR(chip->regmap_sg)) {
 608                if (PTR_ERR(chip->regmap_sg) == -EPROBE_DEFER)
 609                        return -EPROBE_DEFER;
 610                chip->regmap_sg = NULL;
 611        }
 612
 613        chip->clk = devm_clk_get(dev, "aio");
 614        if (IS_ERR(chip->clk))
 615                return PTR_ERR(chip->clk);
 616
 617        chip->rst = devm_reset_control_get_shared(dev, "aio");
 618        if (IS_ERR(chip->rst))
 619                return PTR_ERR(chip->rst);
 620
 621        chip->num_aios = chip->chip_spec->num_dais;
 622        chip->aios = devm_kcalloc(dev,
 623                                  chip->num_aios, sizeof(struct uniphier_aio),
 624                                  GFP_KERNEL);
 625        if (!chip->aios)
 626                return -ENOMEM;
 627
 628        chip->num_plls = chip->chip_spec->num_plls;
 629        chip->plls = devm_kcalloc(dev,
 630                                  chip->num_plls,
 631                                  sizeof(struct uniphier_aio_pll),
 632                                  GFP_KERNEL);
 633        if (!chip->plls)
 634                return -ENOMEM;
 635        memcpy(chip->plls, chip->chip_spec->plls,
 636               sizeof(struct uniphier_aio_pll) * chip->num_plls);
 637
 638        for (i = 0; i < chip->num_aios; i++) {
 639                struct uniphier_aio *aio = &chip->aios[i];
 640
 641                aio->chip = chip;
 642                aio->fmt = SND_SOC_DAIFMT_I2S;
 643
 644                for (j = 0; j < ARRAY_SIZE(aio->sub); j++) {
 645                        struct uniphier_aio_sub *sub = &aio->sub[j];
 646
 647                        sub->aio = aio;
 648                        spin_lock_init(&sub->lock);
 649                }
 650        }
 651
 652        chip->pdev = pdev;
 653        platform_set_drvdata(pdev, chip);
 654
 655        ret = clk_prepare_enable(chip->clk);
 656        if (ret)
 657                return ret;
 658
 659        ret = reset_control_deassert(chip->rst);
 660        if (ret)
 661                goto err_out_clock;
 662
 663        ret = devm_snd_soc_register_component(dev, &uniphier_aio_component,
 664                                              chip->chip_spec->dais,
 665                                              chip->chip_spec->num_dais);
 666        if (ret) {
 667                dev_err(dev, "Register component failed.\n");
 668                goto err_out_reset;
 669        }
 670
 671        ret = uniphier_aiodma_soc_register_platform(pdev);
 672        if (ret) {
 673                dev_err(dev, "Register platform failed.\n");
 674                goto err_out_reset;
 675        }
 676
 677        return 0;
 678
 679err_out_reset:
 680        reset_control_assert(chip->rst);
 681
 682err_out_clock:
 683        clk_disable_unprepare(chip->clk);
 684
 685        return ret;
 686}
 687EXPORT_SYMBOL_GPL(uniphier_aio_probe);
 688
 689int uniphier_aio_remove(struct platform_device *pdev)
 690{
 691        struct uniphier_aio_chip *chip = platform_get_drvdata(pdev);
 692
 693        reset_control_assert(chip->rst);
 694        clk_disable_unprepare(chip->clk);
 695
 696        return 0;
 697}
 698EXPORT_SYMBOL_GPL(uniphier_aio_remove);
 699
 700MODULE_AUTHOR("Katsuhiro Suzuki <suzuki.katsuhiro@socionext.com>");
 701MODULE_DESCRIPTION("UniPhier AIO CPU DAI driver.");
 702MODULE_LICENSE("GPL v2");
 703