Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/soc/meson/aiu-fifo-i2s.c
26436 views
1
// SPDX-License-Identifier: GPL-2.0
2
//
3
// Copyright (c) 2020 BayLibre, SAS.
4
// Author: Jerome Brunet <[email protected]>
5
6
#include <linux/bitfield.h>
7
#include <linux/clk.h>
8
#include <sound/pcm_params.h>
9
#include <sound/soc.h>
10
#include <sound/soc-dai.h>
11
12
#include "aiu.h"
13
#include "aiu-fifo.h"
14
15
#define AIU_I2S_SOURCE_DESC_MODE_8CH BIT(0)
16
#define AIU_I2S_SOURCE_DESC_MODE_24BIT BIT(5)
17
#define AIU_I2S_SOURCE_DESC_MODE_32BIT BIT(9)
18
#define AIU_I2S_SOURCE_DESC_MODE_SPLIT BIT(11)
19
#define AIU_MEM_I2S_MASKS_IRQ_BLOCK GENMASK(31, 16)
20
#define AIU_MEM_I2S_CONTROL_MODE_16BIT BIT(6)
21
#define AIU_MEM_I2S_BUF_CNTL_INIT BIT(0)
22
#define AIU_RST_SOFT_I2S_FAST BIT(0)
23
#define AIU_I2S_MISC_HOLD_EN BIT(2)
24
#define AIU_I2S_MISC_FORCE_LEFT_RIGHT BIT(4)
25
26
#define AIU_FIFO_I2S_BLOCK 256
27
28
static const struct snd_pcm_hardware fifo_i2s_pcm = {
29
.info = (SNDRV_PCM_INFO_INTERLEAVED |
30
SNDRV_PCM_INFO_MMAP |
31
SNDRV_PCM_INFO_MMAP_VALID |
32
SNDRV_PCM_INFO_PAUSE),
33
.formats = AIU_FORMATS,
34
.rate_min = 5512,
35
.rate_max = 192000,
36
.channels_min = 2,
37
.channels_max = 8,
38
.period_bytes_min = AIU_FIFO_I2S_BLOCK,
39
.period_bytes_max = AIU_FIFO_I2S_BLOCK * USHRT_MAX,
40
.periods_min = 2,
41
.periods_max = UINT_MAX,
42
43
/* No real justification for this */
44
.buffer_bytes_max = 1 * 1024 * 1024,
45
};
46
47
static int aiu_fifo_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
48
struct snd_soc_dai *dai)
49
{
50
struct snd_soc_component *component = dai->component;
51
52
switch (cmd) {
53
case SNDRV_PCM_TRIGGER_START:
54
case SNDRV_PCM_TRIGGER_RESUME:
55
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
56
snd_soc_component_write(component, AIU_RST_SOFT,
57
AIU_RST_SOFT_I2S_FAST);
58
snd_soc_component_read(component, AIU_I2S_SYNC);
59
break;
60
}
61
62
return aiu_fifo_trigger(substream, cmd, dai);
63
}
64
65
static int aiu_fifo_i2s_prepare(struct snd_pcm_substream *substream,
66
struct snd_soc_dai *dai)
67
{
68
struct snd_soc_component *component = dai->component;
69
int ret;
70
71
ret = aiu_fifo_prepare(substream, dai);
72
if (ret)
73
return ret;
74
75
snd_soc_component_update_bits(component,
76
AIU_MEM_I2S_BUF_CNTL,
77
AIU_MEM_I2S_BUF_CNTL_INIT,
78
AIU_MEM_I2S_BUF_CNTL_INIT);
79
snd_soc_component_update_bits(component,
80
AIU_MEM_I2S_BUF_CNTL,
81
AIU_MEM_I2S_BUF_CNTL_INIT, 0);
82
83
return 0;
84
}
85
86
static int aiu_fifo_i2s_hw_params(struct snd_pcm_substream *substream,
87
struct snd_pcm_hw_params *params,
88
struct snd_soc_dai *dai)
89
{
90
struct snd_soc_component *component = dai->component;
91
struct aiu_fifo *fifo = snd_soc_dai_dma_data_get_playback(dai);
92
unsigned int val;
93
int ret;
94
95
snd_soc_component_update_bits(component, AIU_I2S_MISC,
96
AIU_I2S_MISC_HOLD_EN,
97
AIU_I2S_MISC_HOLD_EN);
98
99
ret = aiu_fifo_hw_params(substream, params, dai);
100
if (ret)
101
return ret;
102
103
switch (params_physical_width(params)) {
104
case 16:
105
val = AIU_MEM_I2S_CONTROL_MODE_16BIT;
106
break;
107
case 32:
108
val = 0;
109
break;
110
default:
111
dev_err(dai->dev, "Unsupported physical width %u\n",
112
params_physical_width(params));
113
return -EINVAL;
114
}
115
116
snd_soc_component_update_bits(component, AIU_MEM_I2S_CONTROL,
117
AIU_MEM_I2S_CONTROL_MODE_16BIT,
118
val);
119
120
/* Setup the irq periodicity */
121
val = params_period_bytes(params) / fifo->fifo_block;
122
val = FIELD_PREP(AIU_MEM_I2S_MASKS_IRQ_BLOCK, val);
123
snd_soc_component_update_bits(component, AIU_MEM_I2S_MASKS,
124
AIU_MEM_I2S_MASKS_IRQ_BLOCK, val);
125
126
/*
127
* Most (all?) supported SoCs have this bit set by default. The vendor
128
* driver however sets it manually (depending on the version either
129
* while un-setting AIU_I2S_MISC_HOLD_EN or right before that). Follow
130
* the same approach for consistency with the vendor driver.
131
*/
132
snd_soc_component_update_bits(component, AIU_I2S_MISC,
133
AIU_I2S_MISC_FORCE_LEFT_RIGHT,
134
AIU_I2S_MISC_FORCE_LEFT_RIGHT);
135
136
snd_soc_component_update_bits(component, AIU_I2S_MISC,
137
AIU_I2S_MISC_HOLD_EN, 0);
138
139
return 0;
140
}
141
142
const struct snd_soc_dai_ops aiu_fifo_i2s_dai_ops = {
143
.pcm_new = aiu_fifo_pcm_new,
144
.probe = aiu_fifo_i2s_dai_probe,
145
.remove = aiu_fifo_dai_remove,
146
.trigger = aiu_fifo_i2s_trigger,
147
.prepare = aiu_fifo_i2s_prepare,
148
.hw_params = aiu_fifo_i2s_hw_params,
149
.startup = aiu_fifo_startup,
150
.shutdown = aiu_fifo_shutdown,
151
};
152
153
int aiu_fifo_i2s_dai_probe(struct snd_soc_dai *dai)
154
{
155
struct snd_soc_component *component = dai->component;
156
struct aiu *aiu = snd_soc_component_get_drvdata(component);
157
struct aiu_fifo *fifo;
158
int ret;
159
160
ret = aiu_fifo_dai_probe(dai);
161
if (ret)
162
return ret;
163
164
fifo = snd_soc_dai_dma_data_get_playback(dai);
165
166
fifo->pcm = &fifo_i2s_pcm;
167
fifo->mem_offset = AIU_MEM_I2S_START;
168
fifo->fifo_block = AIU_FIFO_I2S_BLOCK;
169
fifo->pclk = aiu->i2s.clks[PCLK].clk;
170
fifo->irq = aiu->i2s.irq;
171
172
return 0;
173
}
174
175