uboot/drivers/sound/rockchip_i2s.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright 2018 Google LLC
   4 * Copyright 2014 Rockchip Electronics Co., Ltd.
   5 * Taken from dc i2s/rockchip.c
   6 */
   7
   8#define LOG_CATEGORY UCLASS_I2S
   9
  10#include <common.h>
  11#include <dm.h>
  12#include <i2s.h>
  13#include <log.h>
  14#include <sound.h>
  15#include <asm/io.h>
  16#include <linux/bitops.h>
  17
  18struct rk_i2s_regs {
  19        u32 txcr;               /* I2S_TXCR, 0x00 */
  20        u32 rxcr;               /* I2S_RXCR, 0x04 */
  21        u32 ckr;                /* I2S_CKR, 0x08 */
  22        u32 fifolr;             /* I2S_FIFOLR, 0x0C */
  23        u32 dmacr;              /* I2S_DMACR, 0x10 */
  24        u32 intcr;              /* I2S_INTCR, 0x14 */
  25        u32 intsr;              /* I2S_INTSR, 0x18 */
  26        u32 xfer;               /* I2S_XFER, 0x1C */
  27        u32 clr;                /* I2S_CLR, 0x20 */
  28        u32 txdr;               /* I2S_TXDR, 0x24 */
  29        u32 rxdr;               /* I2S_RXDR, 0x28 */
  30};
  31
  32enum {
  33        /* I2S_XFER */
  34        I2S_RX_TRAN_BIT         = BIT(1),
  35        I2S_TX_TRAN_BIT         = BIT(0),
  36        I2S_TRAN_MASK           = 3 << 0,
  37
  38        /* I2S_TXCKR */
  39        I2S_MCLK_DIV_SHIFT      = 16,
  40        I2S_MCLK_DIV_MASK       = (0xff << I2S_MCLK_DIV_SHIFT),
  41
  42        I2S_RX_SCLK_DIV_SHIFT   = 8,
  43        I2S_RX_SCLK_DIV_MASK    = 0xff << I2S_RX_SCLK_DIV_SHIFT,
  44        I2S_TX_SCLK_DIV_SHIFT   = 0,
  45        I2S_TX_SCLK_DIV_MASK    = 0xff << I2S_TX_SCLK_DIV_SHIFT,
  46
  47        I2S_DATA_WIDTH_SHIFT    = 0,
  48        I2S_DATA_WIDTH_MASK     = 0x1f << I2S_DATA_WIDTH_SHIFT,
  49};
  50
  51static int rockchip_i2s_init(struct i2s_uc_priv *priv)
  52{
  53        struct rk_i2s_regs *regs = (struct rk_i2s_regs *)priv->base_address;
  54        u32 bps = priv->bitspersample;
  55        u32 lrf = priv->rfs;
  56        u32 chn = priv->channels;
  57        u32 mode = 0;
  58
  59        clrbits_le32(&regs->xfer, I2S_TX_TRAN_BIT);
  60        mode = readl(&regs->txcr) & ~0x1f;
  61        switch (priv->bitspersample) {
  62        case 16:
  63        case 24:
  64                mode |= (priv->bitspersample - 1) << I2S_DATA_WIDTH_SHIFT;
  65                break;
  66        default:
  67                log_err("Invalid sample size input %d\n", priv->bitspersample);
  68                return -EINVAL;
  69        }
  70        writel(mode, &regs->txcr);
  71
  72        mode = readl(&regs->ckr) & ~I2S_MCLK_DIV_MASK;
  73        mode |= (lrf / (bps * chn) - 1) << I2S_MCLK_DIV_SHIFT;
  74
  75        mode &= ~I2S_TX_SCLK_DIV_MASK;
  76        mode |= (priv->bitspersample * priv->channels - 1) <<
  77                         I2S_TX_SCLK_DIV_SHIFT;
  78        writel(mode, &regs->ckr);
  79
  80        return 0;
  81}
  82
  83static int i2s_send_data(struct rk_i2s_regs *regs, u32 *data, uint length)
  84{
  85        for (int i = 0; i < min(32u, length); i++)
  86                writel(*data++, &regs->txdr);
  87
  88        length -= min(32u, length);
  89
  90        /* enable both tx and rx */
  91        setbits_le32(&regs->xfer, I2S_TRAN_MASK);
  92        while (length) {
  93                if ((readl(&regs->fifolr) & 0x3f) < 0x20) {
  94                        writel(*data++, &regs->txdr);
  95                        length--;
  96                }
  97        }
  98        while (readl(&regs->fifolr) & 0x3f)
  99                /* wait until FIFO empty */;
 100        clrbits_le32(&regs->xfer, I2S_TRAN_MASK);
 101        writel(0, &regs->clr);
 102
 103        return 0;
 104}
 105
 106static int rockchip_i2s_tx_data(struct udevice *dev, void *data, uint data_size)
 107{
 108        struct i2s_uc_priv *priv = dev_get_uclass_priv(dev);
 109        struct rk_i2s_regs *regs = (struct rk_i2s_regs *)priv->base_address;
 110
 111        return i2s_send_data(regs, data, data_size / sizeof(u32));
 112}
 113
 114static int rockchip_i2s_probe(struct udevice *dev)
 115{
 116        struct i2s_uc_priv *priv = dev_get_uclass_priv(dev);
 117        ulong base;
 118
 119        base = dev_read_addr(dev);
 120        if (base == FDT_ADDR_T_NONE) {
 121                log_debug("Missing i2s base\n");
 122                return -EINVAL;
 123        }
 124        priv->base_address = base;
 125        priv->id = 1;
 126        priv->audio_pll_clk = 4800000;
 127        priv->samplingrate = 48000;
 128        priv->bitspersample = 16;
 129        priv->channels = 2;
 130        priv->rfs = 256;
 131        priv->bfs = 32;
 132
 133        return rockchip_i2s_init(priv);
 134}
 135
 136static const struct i2s_ops rockchip_i2s_ops = {
 137        .tx_data        = rockchip_i2s_tx_data,
 138};
 139
 140static const struct udevice_id rockchip_i2s_ids[] = {
 141        { .compatible = "rockchip,rk3288-i2s" },
 142        { }
 143};
 144
 145U_BOOT_DRIVER(rockchip_i2s) = {
 146        .name           = "rockchip_i2s",
 147        .id             = UCLASS_I2S,
 148        .of_match       = rockchip_i2s_ids,
 149        .probe          = rockchip_i2s_probe,
 150        .ops            = &rockchip_i2s_ops,
 151};
 152