linux/sound/soc/loongson/loongson1_ac97.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * AC97 Controller Driver for Loongson-1 SoC
   4 *
   5 * Copyright (C) 2025 Keguang Zhang <keguang.zhang@gmail.com>
   6 */
   7
   8#include <linux/bitfield.h>
   9#include <linux/dma-mapping.h>
  10#include <linux/init.h>
  11#include <linux/module.h>
  12#include <linux/platform_device.h>
  13#include <linux/regmap.h>
  14
  15#include <sound/dmaengine_pcm.h>
  16#include <sound/pcm.h>
  17#include <sound/pcm_params.h>
  18#include <sound/soc.h>
  19
  20/* Loongson-1 AC97 Controller Registers */
  21#define AC97_CSR                0x0
  22#define AC97_OCC0               0x4
  23#define AC97_ICC                0x10
  24#define AC97_CRAC               0x18
  25#define AC97_INTRAW             0x54
  26#define AC97_INTM               0x58
  27#define AC97_INT_CW_CLR         0x68
  28#define AC97_INT_CR_CLR         0x6c
  29
  30/* Control Status Register Bits (CSR) */
  31#define CSR_RESUME              BIT(1)
  32#define CSR_RST_FORCE           BIT(0)
  33
  34/* MIC Channel Configuration Bits */
  35#define M_DMA_EN                BIT(22)
  36#define M_FIFO_THRES            GENMASK(21, 20)
  37#define M_FIFO_THRES_FULL       FIELD_PREP(M_FIFO_THRES, 3)
  38#define M_FIFO_THRES_HALF       FIELD_PREP(M_FIFO_THRES, 1)
  39#define M_FIFO_THRES_QUARTER    FIELD_PREP(M_FIFO_THRES, 0)
  40#define M_SW                    GENMASK(19, 18)
  41#define M_SW_16_BITS            FIELD_PREP(M_SW, 2)
  42#define M_SW_8_BITS             FIELD_PREP(M_SW, 0)
  43#define M_VSR                   BIT(17)
  44#define M_CH_EN                 BIT(16)
  45/* Right Channel Configuration Bits */
  46#define R_DMA_EN                BIT(14)
  47#define R_FIFO_THRES            GENMASK(13, 12)
  48#define R_FIFO_THRES_EMPTY      FIELD_PREP(R_FIFO_THRES, 3)
  49#define R_FIFO_THRES_HALF       FIELD_PREP(R_FIFO_THRES, 1)
  50#define R_FIFO_THRES_QUARTER    FIELD_PREP(R_FIFO_THRES, 0)
  51#define R_SW                    GENMASK(11, 10)
  52#define R_SW_16_BITS            FIELD_PREP(R_SW, 2)
  53#define R_SW_8_BITS             FIELD_PREP(R_SW, 0)
  54#define R_VSR                   BIT(9)
  55#define R_CH_EN                 BIT(8)
  56/* Left Channel Configuration Bits */
  57#define L_DMA_EN                BIT(6)
  58#define L_FIFO_THRES            GENMASK(5, 4)
  59#define L_FIFO_THRES_EMPTY      FIELD_PREP(L_FIFO_THRES, 3)
  60#define L_FIFO_THRES_HALF       FIELD_PREP(L_FIFO_THRES, 1)
  61#define L_FIFO_THRES_QUARTER    FIELD_PREP(L_FIFO_THRES, 0)
  62#define L_SW                    GENMASK(3, 2)
  63#define L_SW_16_BITS            FIELD_PREP(L_SW, 2)
  64#define L_SW_8_BITS             FIELD_PREP(L_SW, 0)
  65#define L_VSR                   BIT(1)
  66#define L_CH_EN                 BIT(0)
  67
  68/* Codec Register Access Command Bits (CRAC) */
  69#define CODEC_WR                BIT(31)
  70#define CODEC_ADR               GENMASK(22, 16)
  71#define CODEC_DAT               GENMASK(15, 0)
  72
  73/* Interrupt Register (INTRAW) */
  74#define CW_DONE                 BIT(1)
  75#define CR_DONE                 BIT(0)
  76
  77#define LS1X_AC97_DMA_TX_EN             BIT(31)
  78#define LS1X_AC97_DMA_STEREO            BIT(30)
  79#define LS1X_AC97_DMA_TX_BYTES          GENMASK(29, 28)
  80#define LS1X_AC97_DMA_TX_4_BYTES        FIELD_PREP(LS1X_AC97_DMA_TX_BYTES, 2)
  81#define LS1X_AC97_DMA_TX_2_BYTES        FIELD_PREP(LS1X_AC97_DMA_TX_BYTES, 1)
  82#define LS1X_AC97_DMA_TX_1_BYTE         FIELD_PREP(LS1X_AC97_DMA_TX_BYTES, 0)
  83#define LS1X_AC97_DMA_DADDR_MASK        GENMASK(27, 0)
  84
  85#define LS1X_AC97_DMA_FIFO_SIZE         128
  86
  87#define LS1X_AC97_TIMEOUT               3000
  88
  89struct ls1x_ac97 {
  90        void __iomem *reg_base;
  91        struct regmap *regmap;
  92        dma_addr_t tx_dma_base;
  93        dma_addr_t rx_dma_base;
  94        struct snd_dmaengine_dai_dma_data capture_dma_data;
  95        struct snd_dmaengine_dai_dma_data playback_dma_data;
  96};
  97
  98static struct ls1x_ac97 *ls1x_ac97;
  99
 100static const struct regmap_config ls1x_ac97_regmap_config = {
 101        .reg_bits = 32,
 102        .val_bits = 32,
 103        .reg_stride = 4,
 104};
 105
 106static void ls1x_ac97_reset(struct snd_ac97 *ac97)
 107{
 108        int val;
 109
 110        regmap_write(ls1x_ac97->regmap, AC97_CSR, CSR_RST_FORCE);
 111        regmap_read_poll_timeout(ls1x_ac97->regmap, AC97_CSR, val,
 112                                 !(val & CSR_RESUME), 0, LS1X_AC97_TIMEOUT);
 113}
 114
 115static void ls1x_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val)
 116{
 117        int tmp, ret;
 118
 119        tmp = FIELD_PREP(CODEC_ADR, reg) | FIELD_PREP(CODEC_DAT, val);
 120        regmap_write(ls1x_ac97->regmap, AC97_CRAC, tmp);
 121        ret = regmap_read_poll_timeout(ls1x_ac97->regmap, AC97_INTRAW, tmp,
 122                                       (tmp & CW_DONE), 0, LS1X_AC97_TIMEOUT);
 123        if (ret)
 124                pr_err("timeout on AC97 write! %d\n", ret);
 125
 126        regmap_read(ls1x_ac97->regmap, AC97_INT_CW_CLR, &ret);
 127}
 128
 129static unsigned short ls1x_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
 130{
 131        int val, ret;
 132
 133        val = CODEC_WR | FIELD_PREP(CODEC_ADR, reg);
 134        regmap_write(ls1x_ac97->regmap, AC97_CRAC, val);
 135        ret = regmap_read_poll_timeout(ls1x_ac97->regmap, AC97_INTRAW, val,
 136                                       (val & CR_DONE), 0, LS1X_AC97_TIMEOUT);
 137        if (ret) {
 138                pr_err("timeout on AC97 read! %d\n", ret);
 139                return ret;
 140        }
 141
 142        regmap_read(ls1x_ac97->regmap, AC97_INT_CR_CLR, &ret);
 143        regmap_read(ls1x_ac97->regmap, AC97_CRAC, &ret);
 144
 145        return (ret & CODEC_DAT);
 146}
 147
 148static void ls1x_ac97_init(struct snd_ac97 *ac97)
 149{
 150        writel(0, ls1x_ac97->reg_base + AC97_INTRAW);
 151        writel(0, ls1x_ac97->reg_base + AC97_INTM);
 152
 153        /* Config output channels */
 154        regmap_update_bits(ls1x_ac97->regmap, AC97_OCC0,
 155                           R_DMA_EN | R_FIFO_THRES | R_CH_EN |
 156                           L_DMA_EN | L_FIFO_THRES | L_CH_EN,
 157                           R_DMA_EN | R_FIFO_THRES_EMPTY | R_CH_EN |
 158                           L_DMA_EN | L_FIFO_THRES_EMPTY | L_CH_EN);
 159
 160        /* Config inputs channel */
 161        regmap_update_bits(ls1x_ac97->regmap, AC97_ICC,
 162                           M_DMA_EN | M_FIFO_THRES | M_CH_EN |
 163                           R_DMA_EN | R_FIFO_THRES | R_CH_EN |
 164                           L_DMA_EN | L_FIFO_THRES | L_CH_EN,
 165                           M_DMA_EN | M_FIFO_THRES_FULL | M_CH_EN |
 166                           R_DMA_EN | R_FIFO_THRES_EMPTY | R_CH_EN |
 167                           L_DMA_EN | L_FIFO_THRES_EMPTY | L_CH_EN);
 168
 169        if (ac97->ext_id & AC97_EI_VRA) {
 170                regmap_update_bits(ls1x_ac97->regmap, AC97_OCC0, R_VSR | L_VSR, R_VSR | L_VSR);
 171                regmap_update_bits(ls1x_ac97->regmap, AC97_ICC, M_VSR, M_VSR);
 172        }
 173}
 174
 175static struct snd_ac97_bus_ops ls1x_ac97_ops = {
 176        .reset  = ls1x_ac97_reset,
 177        .write  = ls1x_ac97_write,
 178        .read   = ls1x_ac97_read,
 179        .init   = ls1x_ac97_init,
 180};
 181
 182static int ls1x_ac97_hw_params(struct snd_pcm_substream *substream,
 183                               struct snd_pcm_hw_params *params,
 184                               struct snd_soc_dai *cpu_dai)
 185{
 186        struct ls1x_ac97 *ac97 = dev_get_drvdata(cpu_dai->dev);
 187        struct snd_dmaengine_dai_dma_data *dma_data = snd_soc_dai_get_dma_data(cpu_dai, substream);
 188
 189        switch (params_channels(params)) {
 190        case 1:
 191                dma_data->addr &= ~LS1X_AC97_DMA_STEREO;
 192                break;
 193        case 2:
 194                dma_data->addr |= LS1X_AC97_DMA_STEREO;
 195                break;
 196        default:
 197                dev_err(cpu_dai->dev, "unsupported channels! %d\n", params_channels(params));
 198                return -EINVAL;
 199        }
 200
 201        switch (params_format(params)) {
 202        case SNDRV_PCM_FORMAT_S8:
 203        case SNDRV_PCM_FORMAT_U8:
 204                if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 205                        regmap_update_bits(ac97->regmap, AC97_OCC0,
 206                                           R_SW | L_SW,
 207                                           R_SW_8_BITS | L_SW_8_BITS);
 208                else
 209                        regmap_update_bits(ac97->regmap, AC97_ICC,
 210                                           M_SW | R_SW | L_SW,
 211                                           M_SW_8_BITS | R_SW_8_BITS | L_SW_8_BITS);
 212                break;
 213        case SNDRV_PCM_FORMAT_S16_LE:
 214        case SNDRV_PCM_FORMAT_U16_LE:
 215        case SNDRV_PCM_FORMAT_S16_BE:
 216        case SNDRV_PCM_FORMAT_U16_BE:
 217                if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 218                        regmap_update_bits(ac97->regmap, AC97_OCC0,
 219                                           R_SW | L_SW,
 220                                           R_SW_16_BITS | L_SW_16_BITS);
 221                else
 222                        regmap_update_bits(ac97->regmap, AC97_ICC,
 223                                           M_SW | R_SW | L_SW,
 224                                           M_SW_16_BITS | R_SW_16_BITS | L_SW_16_BITS);
 225                break;
 226        default:
 227                dev_err(cpu_dai->dev, "unsupported format! %d\n", params_format(params));
 228                return -EINVAL;
 229        }
 230
 231        return 0;
 232}
 233
 234static int ls1x_ac97_dai_probe(struct snd_soc_dai *cpu_dai)
 235{
 236        struct ls1x_ac97 *ac97 = dev_get_drvdata(cpu_dai->dev);
 237
 238        ac97->capture_dma_data.addr = ac97->rx_dma_base & LS1X_AC97_DMA_DADDR_MASK;
 239        ac97->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
 240        ac97->capture_dma_data.fifo_size = LS1X_AC97_DMA_FIFO_SIZE;
 241
 242        ac97->playback_dma_data.addr = ac97->tx_dma_base & LS1X_AC97_DMA_DADDR_MASK;
 243        ac97->playback_dma_data.addr |= LS1X_AC97_DMA_TX_4_BYTES;
 244        ac97->playback_dma_data.addr |= LS1X_AC97_DMA_TX_EN;
 245        ac97->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
 246        ac97->playback_dma_data.fifo_size = LS1X_AC97_DMA_FIFO_SIZE;
 247
 248        snd_soc_dai_init_dma_data(cpu_dai, &ac97->playback_dma_data, &ac97->capture_dma_data);
 249        snd_soc_dai_set_drvdata(cpu_dai, ac97);
 250
 251        return 0;
 252}
 253
 254static const struct snd_soc_dai_ops ls1x_ac97_dai_ops = {
 255        .probe          = ls1x_ac97_dai_probe,
 256        .hw_params      = ls1x_ac97_hw_params,
 257};
 258
 259#define LS1X_AC97_FMTS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 |\
 260        SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |\
 261        SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_U16_BE)
 262
 263static struct snd_soc_dai_driver ls1x_ac97_dai[] = {
 264        {
 265                .name = "ls1x-ac97",
 266                .playback = {
 267                        .stream_name = "AC97 Playback",
 268                        .channels_min = 1,
 269                        .channels_max = 2,
 270                        .rates = SNDRV_PCM_RATE_8000_48000,
 271                        .formats = LS1X_AC97_FMTS,
 272                },
 273                .capture = {
 274                        .stream_name = "AC97 Capture",
 275                        .channels_min = 1,
 276                        .channels_max = 2,
 277                        .rates = SNDRV_PCM_RATE_8000_48000,
 278                        .formats = LS1X_AC97_FMTS,
 279                },
 280                .ops = &ls1x_ac97_dai_ops,
 281        },
 282};
 283
 284static const struct snd_soc_component_driver ls1x_ac97_component = {
 285        .name = KBUILD_MODNAME,
 286        .legacy_dai_naming = 1,
 287};
 288
 289static int ls1x_ac97_probe(struct platform_device *pdev)
 290{
 291        struct device *dev = &pdev->dev;
 292        struct ls1x_ac97 *ac97;
 293        struct resource *res;
 294        int ret;
 295
 296        ac97 = devm_kzalloc(dev, sizeof(struct ls1x_ac97), GFP_KERNEL);
 297        if (!ac97)
 298                return -ENOMEM;
 299        ls1x_ac97 = ac97;
 300        platform_set_drvdata(pdev, ac97);
 301
 302        ac97->reg_base = devm_platform_ioremap_resource(pdev, 0);
 303        if (IS_ERR(ac97->reg_base))
 304                return PTR_ERR(ac97->reg_base);
 305
 306        ac97->regmap = devm_regmap_init_mmio(dev, ac97->reg_base, &ls1x_ac97_regmap_config);
 307        if (IS_ERR(ac97->regmap))
 308                return dev_err_probe(dev, PTR_ERR(ac97->regmap), "devm_regmap_init_mmio failed\n");
 309
 310        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "audio-tx");
 311        if (!res)
 312                return dev_err_probe(dev, -EINVAL, "Missing 'audio-tx' in reg-names property\n");
 313
 314        ac97->tx_dma_base = dma_map_resource(dev, res->start, resource_size(res),
 315                                             DMA_TO_DEVICE, 0);
 316        if (dma_mapping_error(dev, ac97->tx_dma_base))
 317                return -ENXIO;
 318
 319        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "audio-rx");
 320        if (!res)
 321                return dev_err_probe(dev, -EINVAL, "Missing 'audio-rx' in reg-names property\n");
 322
 323        ac97->rx_dma_base = dma_map_resource(dev, res->start, resource_size(res),
 324                                             DMA_FROM_DEVICE, 0);
 325        if (dma_mapping_error(dev, ac97->rx_dma_base))
 326                return -ENXIO;
 327
 328        ret = devm_snd_dmaengine_pcm_register(dev, NULL, 0);
 329        if (ret)
 330                dev_err_probe(dev, ret, "failed to register PCM\n");
 331
 332        ret = devm_snd_soc_register_component(dev, &ls1x_ac97_component,
 333                                              ls1x_ac97_dai, ARRAY_SIZE(ls1x_ac97_dai));
 334        if (ret)
 335                dev_err_probe(dev, ret, "failed to register DAI\n");
 336
 337        return snd_soc_set_ac97_ops(&ls1x_ac97_ops);
 338}
 339
 340static void ls1x_ac97_remove(struct platform_device *pdev)
 341{
 342        ls1x_ac97 = NULL;
 343        snd_soc_set_ac97_ops(NULL);
 344}
 345
 346#ifdef CONFIG_PM_SLEEP
 347static int ls1x_ac97_suspend(struct device *dev)
 348{
 349        int val;
 350
 351        regmap_clear_bits(ls1x_ac97->regmap, AC97_OCC0, R_DMA_EN | R_CH_EN | L_DMA_EN | L_CH_EN);
 352        regmap_clear_bits(ls1x_ac97->regmap, AC97_ICC,
 353                          M_DMA_EN | M_CH_EN | R_DMA_EN | R_CH_EN | L_DMA_EN | L_CH_EN);
 354        regmap_set_bits(ls1x_ac97->regmap, AC97_CSR, CSR_RESUME);
 355
 356        return regmap_read_poll_timeout(ls1x_ac97->regmap, AC97_CSR, val,
 357                                        (val & CSR_RESUME), 0, LS1X_AC97_TIMEOUT);
 358}
 359
 360static int ls1x_ac97_resume(struct device *dev)
 361{
 362        int val;
 363
 364        regmap_set_bits(ls1x_ac97->regmap, AC97_OCC0, R_DMA_EN | R_CH_EN | L_DMA_EN | L_CH_EN);
 365        regmap_set_bits(ls1x_ac97->regmap, AC97_ICC,
 366                        M_DMA_EN | M_CH_EN | R_DMA_EN | R_CH_EN | L_DMA_EN | L_CH_EN);
 367        regmap_set_bits(ls1x_ac97->regmap, AC97_CSR, CSR_RESUME);
 368
 369        return regmap_read_poll_timeout(ls1x_ac97->regmap, AC97_CSR, val,
 370                                        !(val & CSR_RESUME), 0, LS1X_AC97_TIMEOUT);
 371}
 372#endif
 373
 374static const struct dev_pm_ops ls1x_ac97_pm_ops = {
 375        SET_SYSTEM_SLEEP_PM_OPS(ls1x_ac97_suspend, ls1x_ac97_resume)
 376};
 377
 378static const struct of_device_id ls1x_ac97_match[] = {
 379        { .compatible = "loongson,ls1b-ac97" },
 380        { /* sentinel */ }
 381};
 382MODULE_DEVICE_TABLE(of, ls1x_ac97_match);
 383
 384static struct platform_driver ls1x_ac97_driver = {
 385        .probe          = ls1x_ac97_probe,
 386        .remove         = ls1x_ac97_remove,
 387        .driver         = {
 388                .name   = KBUILD_MODNAME,
 389                .of_match_table = ls1x_ac97_match,
 390                .pm = &ls1x_ac97_pm_ops,
 391        },
 392};
 393
 394module_platform_driver(ls1x_ac97_driver);
 395
 396MODULE_AUTHOR("Keguang Zhang <keguang.zhang@gmail.com>");
 397MODULE_DESCRIPTION("Loongson-1 AC97 Controller Driver");
 398MODULE_LICENSE("GPL");
 399