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