linux/sound/soc/meson/aiu.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2//
   3// Copyright (c) 2020 BayLibre, SAS.
   4// Author: Jerome Brunet <jbrunet@baylibre.com>
   5
   6#include <linux/bitfield.h>
   7#include <linux/clk.h>
   8#include <linux/module.h>
   9#include <linux/of_platform.h>
  10#include <linux/regmap.h>
  11#include <linux/reset.h>
  12#include <sound/soc.h>
  13#include <sound/soc-dai.h>
  14
  15#include <dt-bindings/sound/meson-aiu.h>
  16#include "aiu.h"
  17#include "aiu-fifo.h"
  18
  19#define AIU_I2S_MISC_958_SRC_SHIFT 3
  20
  21static const char * const aiu_spdif_encode_sel_texts[] = {
  22        "SPDIF", "I2S",
  23};
  24
  25static SOC_ENUM_SINGLE_DECL(aiu_spdif_encode_sel_enum, AIU_I2S_MISC,
  26                            AIU_I2S_MISC_958_SRC_SHIFT,
  27                            aiu_spdif_encode_sel_texts);
  28
  29static const struct snd_kcontrol_new aiu_spdif_encode_mux =
  30        SOC_DAPM_ENUM("SPDIF Buffer Src", aiu_spdif_encode_sel_enum);
  31
  32static const struct snd_soc_dapm_widget aiu_cpu_dapm_widgets[] = {
  33        SND_SOC_DAPM_MUX("SPDIF SRC SEL", SND_SOC_NOPM, 0, 0,
  34                         &aiu_spdif_encode_mux),
  35};
  36
  37static const struct snd_soc_dapm_route aiu_cpu_dapm_routes[] = {
  38        { "I2S Encoder Playback", NULL, "I2S FIFO Playback" },
  39        { "SPDIF SRC SEL", "SPDIF", "SPDIF FIFO Playback" },
  40        { "SPDIF SRC SEL", "I2S", "I2S FIFO Playback" },
  41        { "SPDIF Encoder Playback", NULL, "SPDIF SRC SEL" },
  42};
  43
  44int aiu_of_xlate_dai_name(struct snd_soc_component *component,
  45                          const struct of_phandle_args *args,
  46                          const char **dai_name,
  47                          unsigned int component_id)
  48{
  49        struct snd_soc_dai *dai;
  50        int id;
  51
  52        if (args->args_count != 2)
  53                return -EINVAL;
  54
  55        if (args->args[0] != component_id)
  56                return -EINVAL;
  57
  58        id = args->args[1];
  59
  60        if (id < 0 || id >= component->num_dai)
  61                return -EINVAL;
  62
  63        for_each_component_dais(component, dai) {
  64                if (id == 0)
  65                        break;
  66                id--;
  67        }
  68
  69        *dai_name = dai->driver->name;
  70
  71        return 0;
  72}
  73
  74static int aiu_cpu_of_xlate_dai_name(struct snd_soc_component *component,
  75                                     const struct of_phandle_args *args,
  76                                     const char **dai_name)
  77{
  78        return aiu_of_xlate_dai_name(component, args, dai_name, AIU_CPU);
  79}
  80
  81static int aiu_cpu_component_probe(struct snd_soc_component *component)
  82{
  83        struct aiu *aiu = snd_soc_component_get_drvdata(component);
  84
  85        /* Required for the SPDIF Source control operation */
  86        return clk_prepare_enable(aiu->i2s.clks[PCLK].clk);
  87}
  88
  89static void aiu_cpu_component_remove(struct snd_soc_component *component)
  90{
  91        struct aiu *aiu = snd_soc_component_get_drvdata(component);
  92
  93        clk_disable_unprepare(aiu->i2s.clks[PCLK].clk);
  94}
  95
  96static const struct snd_soc_component_driver aiu_cpu_component = {
  97        .name                   = "AIU CPU",
  98        .dapm_widgets           = aiu_cpu_dapm_widgets,
  99        .num_dapm_widgets       = ARRAY_SIZE(aiu_cpu_dapm_widgets),
 100        .dapm_routes            = aiu_cpu_dapm_routes,
 101        .num_dapm_routes        = ARRAY_SIZE(aiu_cpu_dapm_routes),
 102        .of_xlate_dai_name      = aiu_cpu_of_xlate_dai_name,
 103        .pointer                = aiu_fifo_pointer,
 104        .probe                  = aiu_cpu_component_probe,
 105        .remove                 = aiu_cpu_component_remove,
 106};
 107
 108static struct snd_soc_dai_driver aiu_cpu_dai_drv[] = {
 109        [CPU_I2S_FIFO] = {
 110                .name = "I2S FIFO",
 111                .playback = {
 112                        .stream_name    = "I2S FIFO Playback",
 113                        .channels_min   = 2,
 114                        .channels_max   = 8,
 115                        .rates          = SNDRV_PCM_RATE_CONTINUOUS,
 116                        .rate_min       = 5512,
 117                        .rate_max       = 192000,
 118                        .formats        = AIU_FORMATS,
 119                },
 120                .ops            = &aiu_fifo_i2s_dai_ops,
 121                .pcm_new        = aiu_fifo_pcm_new,
 122                .probe          = aiu_fifo_i2s_dai_probe,
 123                .remove         = aiu_fifo_dai_remove,
 124        },
 125        [CPU_SPDIF_FIFO] = {
 126                .name = "SPDIF FIFO",
 127                .playback = {
 128                        .stream_name    = "SPDIF FIFO Playback",
 129                        .channels_min   = 2,
 130                        .channels_max   = 2,
 131                        .rates          = SNDRV_PCM_RATE_CONTINUOUS,
 132                        .rate_min       = 5512,
 133                        .rate_max       = 192000,
 134                        .formats        = AIU_FORMATS,
 135                },
 136                .ops            = &aiu_fifo_spdif_dai_ops,
 137                .pcm_new        = aiu_fifo_pcm_new,
 138                .probe          = aiu_fifo_spdif_dai_probe,
 139                .remove         = aiu_fifo_dai_remove,
 140        },
 141        [CPU_I2S_ENCODER] = {
 142                .name = "I2S Encoder",
 143                .playback = {
 144                        .stream_name = "I2S Encoder Playback",
 145                        .channels_min = 2,
 146                        .channels_max = 8,
 147                        .rates = SNDRV_PCM_RATE_8000_192000,
 148                        .formats = AIU_FORMATS,
 149                },
 150                .ops = &aiu_encoder_i2s_dai_ops,
 151        },
 152        [CPU_SPDIF_ENCODER] = {
 153                .name = "SPDIF Encoder",
 154                .playback = {
 155                        .stream_name = "SPDIF Encoder Playback",
 156                        .channels_min = 2,
 157                        .channels_max = 2,
 158                        .rates = (SNDRV_PCM_RATE_32000  |
 159                                  SNDRV_PCM_RATE_44100  |
 160                                  SNDRV_PCM_RATE_48000  |
 161                                  SNDRV_PCM_RATE_88200  |
 162                                  SNDRV_PCM_RATE_96000  |
 163                                  SNDRV_PCM_RATE_176400 |
 164                                  SNDRV_PCM_RATE_192000),
 165                        .formats = AIU_FORMATS,
 166                },
 167                .ops = &aiu_encoder_spdif_dai_ops,
 168        }
 169};
 170
 171static const struct regmap_config aiu_regmap_cfg = {
 172        .reg_bits       = 32,
 173        .val_bits       = 32,
 174        .reg_stride     = 4,
 175        .max_register   = 0x2ac,
 176};
 177
 178static int aiu_clk_bulk_get(struct device *dev,
 179                            const char * const *ids,
 180                            unsigned int num,
 181                            struct aiu_interface *interface)
 182{
 183        struct clk_bulk_data *clks;
 184        int i, ret;
 185
 186        clks = devm_kcalloc(dev, num, sizeof(*clks), GFP_KERNEL);
 187        if (!clks)
 188                return -ENOMEM;
 189
 190        for (i = 0; i < num; i++)
 191                clks[i].id = ids[i];
 192
 193        ret = devm_clk_bulk_get(dev, num, clks);
 194        if (ret < 0)
 195                return ret;
 196
 197        interface->clks = clks;
 198        interface->clk_num = num;
 199        return 0;
 200}
 201
 202static const char * const aiu_i2s_ids[] = {
 203        [PCLK]  = "i2s_pclk",
 204        [AOCLK] = "i2s_aoclk",
 205        [MCLK]  = "i2s_mclk",
 206        [MIXER] = "i2s_mixer",
 207};
 208
 209static const char * const aiu_spdif_ids[] = {
 210        [PCLK]  = "spdif_pclk",
 211        [AOCLK] = "spdif_aoclk",
 212        [MCLK]  = "spdif_mclk_sel"
 213};
 214
 215static int aiu_clk_get(struct device *dev)
 216{
 217        struct aiu *aiu = dev_get_drvdata(dev);
 218        int ret;
 219
 220        aiu->pclk = devm_clk_get(dev, "pclk");
 221        if (IS_ERR(aiu->pclk)) {
 222                if (PTR_ERR(aiu->pclk) != -EPROBE_DEFER)
 223                        dev_err(dev, "Can't get the aiu pclk\n");
 224                return PTR_ERR(aiu->pclk);
 225        }
 226
 227        aiu->spdif_mclk = devm_clk_get(dev, "spdif_mclk");
 228        if (IS_ERR(aiu->spdif_mclk)) {
 229                if (PTR_ERR(aiu->spdif_mclk) != -EPROBE_DEFER)
 230                        dev_err(dev, "Can't get the aiu spdif master clock\n");
 231                return PTR_ERR(aiu->spdif_mclk);
 232        }
 233
 234        ret = aiu_clk_bulk_get(dev, aiu_i2s_ids, ARRAY_SIZE(aiu_i2s_ids),
 235                               &aiu->i2s);
 236        if (ret) {
 237                if (ret != -EPROBE_DEFER)
 238                        dev_err(dev, "Can't get the i2s clocks\n");
 239                return ret;
 240        }
 241
 242        ret = aiu_clk_bulk_get(dev, aiu_spdif_ids, ARRAY_SIZE(aiu_spdif_ids),
 243                               &aiu->spdif);
 244        if (ret) {
 245                if (ret != -EPROBE_DEFER)
 246                        dev_err(dev, "Can't get the spdif clocks\n");
 247                return ret;
 248        }
 249
 250        ret = clk_prepare_enable(aiu->pclk);
 251        if (ret) {
 252                dev_err(dev, "peripheral clock enable failed\n");
 253                return ret;
 254        }
 255
 256        ret = devm_add_action_or_reset(dev,
 257                                       (void(*)(void *))clk_disable_unprepare,
 258                                       aiu->pclk);
 259        if (ret)
 260                dev_err(dev, "failed to add reset action on pclk");
 261
 262        return ret;
 263}
 264
 265static int aiu_probe(struct platform_device *pdev)
 266{
 267        struct device *dev = &pdev->dev;
 268        void __iomem *regs;
 269        struct regmap *map;
 270        struct aiu *aiu;
 271        int ret;
 272
 273        aiu = devm_kzalloc(dev, sizeof(*aiu), GFP_KERNEL);
 274        if (!aiu)
 275                return -ENOMEM;
 276
 277        aiu->platform = device_get_match_data(dev);
 278        if (!aiu->platform)
 279                return -ENODEV;
 280
 281        platform_set_drvdata(pdev, aiu);
 282
 283        ret = device_reset(dev);
 284        if (ret) {
 285                if (ret != -EPROBE_DEFER)
 286                        dev_err(dev, "Failed to reset device\n");
 287                return ret;
 288        }
 289
 290        regs = devm_platform_ioremap_resource(pdev, 0);
 291        if (IS_ERR(regs))
 292                return PTR_ERR(regs);
 293
 294        map = devm_regmap_init_mmio(dev, regs, &aiu_regmap_cfg);
 295        if (IS_ERR(map)) {
 296                dev_err(dev, "failed to init regmap: %ld\n",
 297                        PTR_ERR(map));
 298                return PTR_ERR(map);
 299        }
 300
 301        aiu->i2s.irq = platform_get_irq_byname(pdev, "i2s");
 302        if (aiu->i2s.irq < 0)
 303                return aiu->i2s.irq;
 304
 305        aiu->spdif.irq = platform_get_irq_byname(pdev, "spdif");
 306        if (aiu->spdif.irq < 0)
 307                return aiu->spdif.irq;
 308
 309        ret = aiu_clk_get(dev);
 310        if (ret)
 311                return ret;
 312
 313        /* Register the cpu component of the aiu */
 314        ret = snd_soc_register_component(dev, &aiu_cpu_component,
 315                                         aiu_cpu_dai_drv,
 316                                         ARRAY_SIZE(aiu_cpu_dai_drv));
 317        if (ret) {
 318                dev_err(dev, "Failed to register cpu component\n");
 319                return ret;
 320        }
 321
 322        /* Register the hdmi codec control component */
 323        ret = aiu_hdmi_ctrl_register_component(dev);
 324        if (ret) {
 325                dev_err(dev, "Failed to register hdmi control component\n");
 326                goto err;
 327        }
 328
 329        /* Register the internal dac control component on gxl */
 330        if (aiu->platform->has_acodec) {
 331                ret = aiu_acodec_ctrl_register_component(dev);
 332                if (ret) {
 333                        dev_err(dev,
 334                            "Failed to register acodec control component\n");
 335                        goto err;
 336                }
 337        }
 338
 339        return 0;
 340err:
 341        snd_soc_unregister_component(dev);
 342        return ret;
 343}
 344
 345static int aiu_remove(struct platform_device *pdev)
 346{
 347        snd_soc_unregister_component(&pdev->dev);
 348
 349        return 0;
 350}
 351
 352static const struct aiu_platform_data aiu_gxbb_pdata = {
 353        .has_acodec = false,
 354        .has_clk_ctrl_more_i2s_div = true,
 355};
 356
 357static const struct aiu_platform_data aiu_gxl_pdata = {
 358        .has_acodec = true,
 359        .has_clk_ctrl_more_i2s_div = true,
 360};
 361
 362static const struct aiu_platform_data aiu_meson8_pdata = {
 363        .has_acodec = false,
 364        .has_clk_ctrl_more_i2s_div = false,
 365};
 366
 367static const struct of_device_id aiu_of_match[] = {
 368        { .compatible = "amlogic,aiu-gxbb", .data = &aiu_gxbb_pdata },
 369        { .compatible = "amlogic,aiu-gxl", .data = &aiu_gxl_pdata },
 370        { .compatible = "amlogic,aiu-meson8", .data = &aiu_meson8_pdata },
 371        { .compatible = "amlogic,aiu-meson8b", .data = &aiu_meson8_pdata },
 372        {}
 373};
 374MODULE_DEVICE_TABLE(of, aiu_of_match);
 375
 376static struct platform_driver aiu_pdrv = {
 377        .probe = aiu_probe,
 378        .remove = aiu_remove,
 379        .driver = {
 380                .name = "meson-aiu",
 381                .of_match_table = aiu_of_match,
 382        },
 383};
 384module_platform_driver(aiu_pdrv);
 385
 386MODULE_DESCRIPTION("Meson AIU Driver");
 387MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
 388MODULE_LICENSE("GPL v2");
 389