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 
tegra_i2s_set_cif_tx_ctrl(struct udevice * dev,u32 value)19 int 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 
tegra_i2s_transmit_enable(struct i2s_ctlr * regs,int on)29 static 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 
i2s_tx_init(struct i2s_uc_priv * pi2s_tx)35 static 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 
tegra_i2s_tx_data(struct udevice * dev,void * data,uint data_size)70 static 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 
tegra_i2s_probe(struct udevice * dev)87 static 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 
109 static const struct i2s_ops tegra_i2s_ops = {
110 	.tx_data	= tegra_i2s_tx_data,
111 };
112 
113 static const struct udevice_id tegra_i2s_ids[] = {
114 	{ .compatible = "nvidia,tegra124-i2s" },
115 	{ }
116 };
117 
118 U_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