af002128fa829486bb8cf21ad4b14ccea29c7cb9
[openwrt/staging/xback.git] /
1 From d56239568d5f3b2a5519e9b08cba847c1354ad54 Mon Sep 17 00:00:00 2001
2 From: Matthias Reichl <hias@horus.com>
3 Date: Sun, 7 May 2017 15:30:50 +0200
4 Subject: [PATCH 116/454] ASoC: bcm2835: Support left/right justified and DSP
5 modes
6
7 DSP modes and left/right justified modes can be supported
8 on bcm2835 by configuring the frame sync polarity and
9 frame sync length registers and by adjusting the
10 channel data position registers.
11
12 Clock and frame sync polarity handling in hw_params has
13 been refactored to make the interaction between logical
14 rising/falling edge frame start and physical configuration
15 (changed by normal/inverted polarity modes) clearer.
16
17 Modes where the first active data bit is transmitted immediately
18 after frame start (eg DSP mode B with slot 0 active)
19 only work reliable if bcm2835 is configured as frame master.
20 In frame slave mode channel swap (or shift, this isn't quite
21 clear yet) can occur.
22
23 Currently the driver only warns if an unstable configuration
24 is detected but doensn't prevent using them.
25
26 Signed-off-by: Matthias Reichl <hias@horus.com>
27 ---
28 sound/soc/bcm/bcm2835-i2s.c | 152 +++++++++++++++++++++++-------------
29 1 file changed, 99 insertions(+), 53 deletions(-)
30
31 --- a/sound/soc/bcm/bcm2835-i2s.c
32 +++ b/sound/soc/bcm/bcm2835-i2s.c
33 @@ -344,6 +344,9 @@ static int bcm2835_i2s_hw_params(struct
34 unsigned int rx_mask, tx_mask;
35 unsigned int rx_ch1_pos, rx_ch2_pos, tx_ch1_pos, tx_ch2_pos;
36 unsigned int mode, format;
37 + bool bit_clock_master = false;
38 + bool frame_sync_master = false;
39 + bool frame_start_falling_edge = false;
40 uint32_t csreg;
41 int ret = 0;
42
43 @@ -387,16 +390,39 @@ static int bcm2835_i2s_hw_params(struct
44 if (data_length > slot_width)
45 return -EINVAL;
46
47 - /* Clock should only be set up here if CPU is clock master */
48 + /* Check if CPU is bit clock master */
49 switch (dev->fmt & SND_SOC_DAIFMT_MASTER_MASK) {
50 case SND_SOC_DAIFMT_CBS_CFS:
51 case SND_SOC_DAIFMT_CBS_CFM:
52 - ret = clk_set_rate(dev->clk, bclk_rate);
53 - if (ret)
54 - return ret;
55 + bit_clock_master = true;
56 + break;
57 + case SND_SOC_DAIFMT_CBM_CFS:
58 + case SND_SOC_DAIFMT_CBM_CFM:
59 + bit_clock_master = false;
60 break;
61 default:
62 + return -EINVAL;
63 + }
64 +
65 + /* Check if CPU is frame sync master */
66 + switch (dev->fmt & SND_SOC_DAIFMT_MASTER_MASK) {
67 + case SND_SOC_DAIFMT_CBS_CFS:
68 + case SND_SOC_DAIFMT_CBM_CFS:
69 + frame_sync_master = true;
70 + break;
71 + case SND_SOC_DAIFMT_CBS_CFM:
72 + case SND_SOC_DAIFMT_CBM_CFM:
73 + frame_sync_master = false;
74 break;
75 + default:
76 + return -EINVAL;
77 + }
78 +
79 + /* Clock should only be set up here if CPU is clock master */
80 + if (bit_clock_master) {
81 + ret = clk_set_rate(dev->clk, bclk_rate);
82 + if (ret)
83 + return ret;
84 }
85
86 /* Setup the frame format */
87 @@ -427,13 +453,41 @@ static int bcm2835_i2s_hw_params(struct
88
89 /* Setup frame sync signal for 50% duty cycle */
90 framesync_length = frame_length / 2;
91 + frame_start_falling_edge = true;
92 + break;
93 + case SND_SOC_DAIFMT_LEFT_J:
94 + if (slots & 1)
95 + return -EINVAL;
96 +
97 + odd_slot_offset = slots >> 1;
98 + data_delay = 0;
99 + framesync_length = frame_length / 2;
100 + frame_start_falling_edge = false;
101 + break;
102 + case SND_SOC_DAIFMT_RIGHT_J:
103 + if (slots & 1)
104 + return -EINVAL;
105 +
106 + /* Odd frame lengths aren't supported */
107 + if (frame_length & 1)
108 + return -EINVAL;
109 +
110 + odd_slot_offset = slots >> 1;
111 + data_delay = slot_width - data_length;
112 + framesync_length = frame_length / 2;
113 + frame_start_falling_edge = false;
114 + break;
115 + case SND_SOC_DAIFMT_DSP_A:
116 + data_delay = 1;
117 + framesync_length = 1;
118 + frame_start_falling_edge = false;
119 + break;
120 + case SND_SOC_DAIFMT_DSP_B:
121 + data_delay = 0;
122 + framesync_length = 1;
123 + frame_start_falling_edge = false;
124 break;
125 default:
126 - /*
127 - * TODO
128 - * Others are possible but are not implemented at the moment.
129 - */
130 - dev_err(dev->dev, "%s:bad format\n", __func__);
131 return -EINVAL;
132 }
133
134 @@ -443,6 +497,15 @@ static int bcm2835_i2s_hw_params(struct
135 tx_mask, slot_width, data_delay, odd_slot_offset);
136
137 /*
138 + * Transmitting data immediately after frame start, eg
139 + * in left-justified or DSP mode A, only works stable
140 + * if bcm2835 is the frame clock master.
141 + */
142 + if ((!rx_ch1_pos || !tx_ch1_pos) && !frame_sync_master)
143 + dev_warn(dev->dev,
144 + "Unstable slave config detected, L/R may be swapped");
145 +
146 + /*
147 * Set format for both streams.
148 * We cannot set another frame length
149 * (and therefore word length) anyway,
150 @@ -472,62 +535,38 @@ static int bcm2835_i2s_hw_params(struct
151 mode |= BCM2835_I2S_FLEN(frame_length - 1);
152 mode |= BCM2835_I2S_FSLEN(framesync_length);
153
154 - /* Master or slave? */
155 - switch (dev->fmt & SND_SOC_DAIFMT_MASTER_MASK) {
156 - case SND_SOC_DAIFMT_CBS_CFS:
157 - /* CPU is master */
158 - break;
159 - case SND_SOC_DAIFMT_CBM_CFS:
160 - /*
161 - * CODEC is bit clock master
162 - * CPU is frame master
163 - */
164 + /* CLKM selects bcm2835 clock slave mode */
165 + if (!bit_clock_master)
166 mode |= BCM2835_I2S_CLKM;
167 - break;
168 - case SND_SOC_DAIFMT_CBS_CFM:
169 - /*
170 - * CODEC is frame master
171 - * CPU is bit clock master
172 - */
173 +
174 + /* FSM selects bcm2835 frame sync slave mode */
175 + if (!frame_sync_master)
176 mode |= BCM2835_I2S_FSM;
177 +
178 + /* CLKI selects normal clocking mode, sampling on rising edge */
179 + switch (dev->fmt & SND_SOC_DAIFMT_INV_MASK) {
180 + case SND_SOC_DAIFMT_NB_NF:
181 + case SND_SOC_DAIFMT_NB_IF:
182 + mode |= BCM2835_I2S_CLKI;
183 break;
184 - case SND_SOC_DAIFMT_CBM_CFM:
185 - /* CODEC is master */
186 - mode |= BCM2835_I2S_CLKM;
187 - mode |= BCM2835_I2S_FSM;
188 + case SND_SOC_DAIFMT_IB_NF:
189 + case SND_SOC_DAIFMT_IB_IF:
190 break;
191 default:
192 - dev_err(dev->dev, "%s:bad master\n", __func__);
193 return -EINVAL;
194 }
195
196 - /*
197 - * Invert clocks?
198 - *
199 - * The BCM approach seems to be inverted to the classical I2S approach.
200 - */
201 + /* FSI selects frame start on falling edge */
202 switch (dev->fmt & SND_SOC_DAIFMT_INV_MASK) {
203 case SND_SOC_DAIFMT_NB_NF:
204 - /* None. Therefore, both for BCM */
205 - mode |= BCM2835_I2S_CLKI;
206 - mode |= BCM2835_I2S_FSI;
207 - break;
208 - case SND_SOC_DAIFMT_IB_IF:
209 - /* Both. Therefore, none for BCM */
210 + case SND_SOC_DAIFMT_IB_NF:
211 + if (frame_start_falling_edge)
212 + mode |= BCM2835_I2S_FSI;
213 break;
214 case SND_SOC_DAIFMT_NB_IF:
215 - /*
216 - * Invert only frame sync. Therefore,
217 - * invert only bit clock for BCM
218 - */
219 - mode |= BCM2835_I2S_CLKI;
220 - break;
221 - case SND_SOC_DAIFMT_IB_NF:
222 - /*
223 - * Invert only bit clock. Therefore,
224 - * invert only frame sync for BCM
225 - */
226 - mode |= BCM2835_I2S_FSI;
227 + case SND_SOC_DAIFMT_IB_IF:
228 + if (!frame_start_falling_edge)
229 + mode |= BCM2835_I2S_FSI;
230 break;
231 default:
232 return -EINVAL;
233 @@ -563,6 +602,13 @@ static int bcm2835_i2s_hw_params(struct
234 dev_dbg(dev->dev, "sampling rate: %d bclk rate: %d\n",
235 params_rate(params), bclk_rate);
236
237 + dev_dbg(dev->dev, "CLKM: %d CLKI: %d FSM: %d FSI: %d frame start: %s edge\n",
238 + !!(mode & BCM2835_I2S_CLKM),
239 + !!(mode & BCM2835_I2S_CLKI),
240 + !!(mode & BCM2835_I2S_FSM),
241 + !!(mode & BCM2835_I2S_FSI),
242 + (mode & BCM2835_I2S_FSI) ? "falling" : "rising");
243 +
244 return ret;
245 }
246