1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
|
From b97a67a14e5c5904b84e52150e0d13da794fc9d7 Mon Sep 17 00:00:00 2001
From: gtrainavicius <gtrainavicius@users.noreply.github.com>
Date: Tue, 10 Jan 2017 21:59:39 +0200
Subject: [PATCH] bcm2835-i2s: Changes for allowing asymmetric sample formats.
(#1783)
This is achieved by making changes only to the requested
stream direction format, keeping the other stream direction
configuration intact.
Signed-off-by: Giedrius Trainavicius <giedrius@blokas.io>
---
sound/soc/bcm/bcm2835-i2s.c | 54 +++++++++++++++++++++++++++++++--------------
1 file changed, 38 insertions(+), 16 deletions(-)
--- a/sound/soc/bcm/bcm2835-i2s.c
+++ b/sound/soc/bcm/bcm2835-i2s.c
@@ -310,6 +310,7 @@ static int bcm2835_i2s_hw_params(struct
unsigned int sampling_rate = params_rate(params);
unsigned int data_length, data_delay, bclk_ratio;
unsigned int ch1pos, ch2pos, mode, format;
+ unsigned int previous_ftxp, previous_frxp;
unsigned int mash = BCM2835_CLK_MASH_1;
unsigned int divi, divf, target_frequency;
int clk_src = -1;
@@ -320,6 +321,7 @@ static int bcm2835_i2s_hw_params(struct
bool frame_master = (master == SND_SOC_DAIFMT_CBS_CFS
|| master == SND_SOC_DAIFMT_CBM_CFS);
uint32_t csreg;
+ bool packed;
/*
* If a stream is already enabled,
@@ -465,26 +467,46 @@ static int bcm2835_i2s_hw_params(struct
return -EINVAL;
}
- /*
- * Set format for both streams.
- * We cannot set another frame length
- * (and therefore word length) anyway,
- * so the format will be the same.
- */
- regmap_write(dev->i2s_regmap, BCM2835_I2S_RXC_A_REG, format);
- regmap_write(dev->i2s_regmap, BCM2835_I2S_TXC_A_REG, format);
+ /* Set the format for the matching stream direction. */
+ switch (substream->stream) {
+ case SNDRV_PCM_STREAM_PLAYBACK:
+ regmap_write(dev->i2s_regmap, BCM2835_I2S_TXC_A_REG, format);
+ break;
+ case SNDRV_PCM_STREAM_CAPTURE:
+ regmap_write(dev->i2s_regmap, BCM2835_I2S_RXC_A_REG, format);
+ break;
+ default:
+ return -EINVAL;
+ }
/* Setup the I2S mode */
+ /* Keep existing FTXP and FRXP values. */
+ regmap_read(dev->i2s_regmap, BCM2835_I2S_MODE_A_REG, &mode);
+
+ previous_ftxp = mode & BCM2835_I2S_FTXP;
+ previous_frxp = mode & BCM2835_I2S_FRXP;
+
mode = 0;
- if (data_length <= 16) {
- /*
- * Use frame packed mode (2 channels per 32 bit word)
- * We cannot set another frame length in the second stream
- * (and therefore word length) anyway,
- * so the format will be the same.
- */
- mode |= BCM2835_I2S_FTXP | BCM2835_I2S_FRXP;
+ /*
+ * Retain the frame packed mode (2 channels per 32 bit word)
+ * of the other direction stream intact. The formats of each
+ * direction can be different as long as the frame length is
+ * shared for both.
+ */
+ packed = data_length <= 16;
+
+ switch (substream->stream) {
+ case SNDRV_PCM_STREAM_PLAYBACK:
+ mode |= previous_frxp;
+ mode |= packed ? BCM2835_I2S_FTXP : 0;
+ break;
+ case SNDRV_PCM_STREAM_CAPTURE:
+ mode |= previous_ftxp;
+ mode |= packed ? BCM2835_I2S_FRXP : 0;
+ break;
+ default:
+ return -EINVAL;
}
mode |= BCM2835_I2S_FLEN(bclk_ratio - 1);
|