uboot/drivers/sound/tegra_i2s.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright 2018 Google LLC
   4 * Written by Simon Glass <sjg@chromium.org>
   5 */
   6#define LOG_CATEGORY UCLASS_I2S
   7#define LOG_DEBUG
   8
   9#include <common.h>
  10#include <dm.h>
  11#include <i2s.h>
  12#include <log.h>
  13#include <misc.h>
  14#include <sound.h>
  15#include <asm/io.h>
  16#include <asm/arch-tegra/tegra_i2s.h>
  17#include "tegra_i2s_priv.h"
  18
  19int tegra_i2s_set_cif_tx_ctrl(struct udevice *dev, u32 value)
  20{
  21        struct i2s_uc_priv *priv = dev_get_uclass_priv(dev);
  22        struct i2s_ctlr *regs = (struct i2s_ctlr *)priv->base_address;
  23
  24        writel(value, &regs->cif_tx_ctrl);
  25
  26        return 0;
  27}
  28
  29static void tegra_i2s_transmit_enable(struct i2s_ctlr *regs, int on)
  30{
  31        clrsetbits_le32(&regs->ctrl, I2S_CTRL_XFER_EN_TX,
  32                        on ? I2S_CTRL_XFER_EN_TX : 0);
  33}
  34
  35static int i2s_tx_init(struct i2s_uc_priv *pi2s_tx)
  36{
  37        struct i2s_ctlr *regs = (struct i2s_ctlr *)pi2s_tx->base_address;
  38        u32 audio_bits = (pi2s_tx->bitspersample >> 2) - 1;
  39        u32 ctrl = readl(&regs->ctrl);
  40
  41        /* Set format to LRCK / Left Low */
  42        ctrl &= ~(I2S_CTRL_FRAME_FORMAT_MASK | I2S_CTRL_LRCK_MASK);
  43        ctrl |= I2S_CTRL_FRAME_FORMAT_LRCK;
  44        ctrl |= I2S_CTRL_LRCK_L_LOW;
  45
  46        /* Disable all transmission until we are ready to transfer */
  47        ctrl &= ~(I2S_CTRL_XFER_EN_TX | I2S_CTRL_XFER_EN_RX);
  48
  49        /* Serve as master */
  50        ctrl |= I2S_CTRL_MASTER_ENABLE;
  51
  52        /* Configure audio bits size */
  53        ctrl &= ~I2S_CTRL_BIT_SIZE_MASK;
  54        ctrl |= audio_bits << I2S_CTRL_BIT_SIZE_SHIFT;
  55        writel(ctrl, &regs->ctrl);
  56
  57        /* Timing in LRCK mode: */
  58        writel(pi2s_tx->bitspersample, &regs->timing);
  59
  60        /* I2S mode has [TX/RX]_DATA_OFFSET both set to 1 */
  61        writel(((1 << I2S_OFFSET_RX_DATA_OFFSET_SHIFT) |
  62                (1 << I2S_OFFSET_TX_DATA_OFFSET_SHIFT)), &regs->offset);
  63
  64        /* FSYNC_WIDTH = 2 clocks wide, TOTAL_SLOTS = 2 slots per fsync */
  65        writel((2 - 1) << I2S_CH_CTRL_FSYNC_WIDTH_SHIFT, &regs->ch_ctrl);
  66
  67        return 0;
  68}
  69
  70static int tegra_i2s_tx_data(struct udevice *dev, void *data, uint data_size)
  71{
  72        struct i2s_uc_priv *priv = dev_get_uclass_priv(dev);
  73        struct i2s_ctlr *regs = (struct i2s_ctlr *)priv->base_address;
  74        int ret;
  75
  76        tegra_i2s_transmit_enable(regs, 1);
  77        ret = misc_write(dev_get_parent(dev), 0, data, data_size);
  78        tegra_i2s_transmit_enable(regs, 0);
  79        if (ret < 0)
  80                return ret;
  81        else if (ret < data_size)
  82                return -EIO;
  83
  84        return 0;
  85}
  86
  87static int tegra_i2s_probe(struct udevice *dev)
  88{
  89        struct i2s_uc_priv *priv = dev_get_uclass_priv(dev);
  90        ulong base;
  91
  92        base = dev_read_addr(dev);
  93        if (base == FDT_ADDR_T_NONE) {
  94                debug("%s: Missing i2s base\n", __func__);
  95                return -EINVAL;
  96        }
  97        priv->base_address = base;
  98        priv->id = 1;
  99        priv->audio_pll_clk = 4800000;
 100        priv->samplingrate = 48000;
 101        priv->bitspersample = 16;
 102        priv->channels = 2;
 103        priv->rfs = 256;
 104        priv->bfs = 32;
 105
 106        return i2s_tx_init(priv);
 107}
 108
 109static const struct i2s_ops tegra_i2s_ops = {
 110        .tx_data        = tegra_i2s_tx_data,
 111};
 112
 113static const struct udevice_id tegra_i2s_ids[] = {
 114        { .compatible = "nvidia,tegra124-i2s" },
 115        { }
 116};
 117
 118U_BOOT_DRIVER(tegra_i2s) = {
 119        .name           = "tegra_i2s",
 120        .id             = UCLASS_I2S,
 121        .of_match       = tegra_i2s_ids,
 122        .probe          = tegra_i2s_probe,
 123        .ops            = &tegra_i2s_ops,
 124};
 125