forked from ~ljy/RK356X_SDK_RELEASE

hc
2024-05-10 61598093bbdd283a7edc367d900f223070ead8d2
kernel/drivers/clk/meson/clk-phase.c
....@@ -5,7 +5,10 @@
55 */
66
77 #include <linux/clk-provider.h>
8
-#include "clkc.h"
8
+#include <linux/module.h>
9
+
10
+#include "clk-regmap.h"
11
+#include "clk-phase.h"
912
1013 #define phase_step(_width) (360 / (1 << (_width)))
1114
....@@ -15,13 +18,12 @@
1518 return (struct meson_clk_phase_data *)clk->data;
1619 }
1720
18
-int meson_clk_degrees_from_val(unsigned int val, unsigned int width)
21
+static int meson_clk_degrees_from_val(unsigned int val, unsigned int width)
1922 {
2023 return phase_step(width) * val;
2124 }
22
-EXPORT_SYMBOL_GPL(meson_clk_degrees_from_val);
2325
24
-unsigned int meson_clk_degrees_to_val(int degrees, unsigned int width)
26
+static unsigned int meson_clk_degrees_to_val(int degrees, unsigned int width)
2527 {
2628 unsigned int val = DIV_ROUND_CLOSEST(degrees, phase_step(width));
2729
....@@ -31,7 +33,6 @@
3133 */
3234 return val % (1 << width);
3335 }
34
-EXPORT_SYMBOL_GPL(meson_clk_degrees_to_val);
3536
3637 static int meson_clk_phase_get_phase(struct clk_hw *hw)
3738 {
....@@ -61,3 +62,125 @@
6162 .set_phase = meson_clk_phase_set_phase,
6263 };
6364 EXPORT_SYMBOL_GPL(meson_clk_phase_ops);
65
+
66
+/*
67
+ * This is a special clock for the audio controller.
68
+ * The phase of mst_sclk clock output can be controlled independently
69
+ * for the outside world (ph0), the tdmout (ph1) and tdmin (ph2).
70
+ * Controlling these 3 phases as just one makes things simpler and
71
+ * give the same clock view to all the element on the i2s bus.
72
+ * If necessary, we can still control the phase in the tdm block
73
+ * which makes these independent control redundant.
74
+ */
75
+static inline struct meson_clk_triphase_data *
76
+meson_clk_triphase_data(struct clk_regmap *clk)
77
+{
78
+ return (struct meson_clk_triphase_data *)clk->data;
79
+}
80
+
81
+static int meson_clk_triphase_sync(struct clk_hw *hw)
82
+{
83
+ struct clk_regmap *clk = to_clk_regmap(hw);
84
+ struct meson_clk_triphase_data *tph = meson_clk_triphase_data(clk);
85
+ unsigned int val;
86
+
87
+ /* Get phase 0 and sync it to phase 1 and 2 */
88
+ val = meson_parm_read(clk->map, &tph->ph0);
89
+ meson_parm_write(clk->map, &tph->ph1, val);
90
+ meson_parm_write(clk->map, &tph->ph2, val);
91
+
92
+ return 0;
93
+}
94
+
95
+static int meson_clk_triphase_get_phase(struct clk_hw *hw)
96
+{
97
+ struct clk_regmap *clk = to_clk_regmap(hw);
98
+ struct meson_clk_triphase_data *tph = meson_clk_triphase_data(clk);
99
+ unsigned int val;
100
+
101
+ /* Phase are in sync, reading phase 0 is enough */
102
+ val = meson_parm_read(clk->map, &tph->ph0);
103
+
104
+ return meson_clk_degrees_from_val(val, tph->ph0.width);
105
+}
106
+
107
+static int meson_clk_triphase_set_phase(struct clk_hw *hw, int degrees)
108
+{
109
+ struct clk_regmap *clk = to_clk_regmap(hw);
110
+ struct meson_clk_triphase_data *tph = meson_clk_triphase_data(clk);
111
+ unsigned int val;
112
+
113
+ val = meson_clk_degrees_to_val(degrees, tph->ph0.width);
114
+ meson_parm_write(clk->map, &tph->ph0, val);
115
+ meson_parm_write(clk->map, &tph->ph1, val);
116
+ meson_parm_write(clk->map, &tph->ph2, val);
117
+
118
+ return 0;
119
+}
120
+
121
+const struct clk_ops meson_clk_triphase_ops = {
122
+ .init = meson_clk_triphase_sync,
123
+ .get_phase = meson_clk_triphase_get_phase,
124
+ .set_phase = meson_clk_triphase_set_phase,
125
+};
126
+EXPORT_SYMBOL_GPL(meson_clk_triphase_ops);
127
+
128
+/*
129
+ * This is a special clock for the audio controller.
130
+ * This drive a bit clock inverter for which the
131
+ * opposite value of the inverter bit needs to be manually
132
+ * set into another bit
133
+ */
134
+static inline struct meson_sclk_ws_inv_data *
135
+meson_sclk_ws_inv_data(struct clk_regmap *clk)
136
+{
137
+ return (struct meson_sclk_ws_inv_data *)clk->data;
138
+}
139
+
140
+static int meson_sclk_ws_inv_sync(struct clk_hw *hw)
141
+{
142
+ struct clk_regmap *clk = to_clk_regmap(hw);
143
+ struct meson_sclk_ws_inv_data *tph = meson_sclk_ws_inv_data(clk);
144
+ unsigned int val;
145
+
146
+ /* Get phase and sync the inverted value to ws */
147
+ val = meson_parm_read(clk->map, &tph->ph);
148
+ meson_parm_write(clk->map, &tph->ws, val ? 0 : 1);
149
+
150
+ return 0;
151
+}
152
+
153
+static int meson_sclk_ws_inv_get_phase(struct clk_hw *hw)
154
+{
155
+ struct clk_regmap *clk = to_clk_regmap(hw);
156
+ struct meson_sclk_ws_inv_data *tph = meson_sclk_ws_inv_data(clk);
157
+ unsigned int val;
158
+
159
+ val = meson_parm_read(clk->map, &tph->ph);
160
+
161
+ return meson_clk_degrees_from_val(val, tph->ph.width);
162
+}
163
+
164
+static int meson_sclk_ws_inv_set_phase(struct clk_hw *hw, int degrees)
165
+{
166
+ struct clk_regmap *clk = to_clk_regmap(hw);
167
+ struct meson_sclk_ws_inv_data *tph = meson_sclk_ws_inv_data(clk);
168
+ unsigned int val;
169
+
170
+ val = meson_clk_degrees_to_val(degrees, tph->ph.width);
171
+ meson_parm_write(clk->map, &tph->ph, val);
172
+ meson_parm_write(clk->map, &tph->ws, val ? 0 : 1);
173
+ return 0;
174
+}
175
+
176
+const struct clk_ops meson_sclk_ws_inv_ops = {
177
+ .init = meson_sclk_ws_inv_sync,
178
+ .get_phase = meson_sclk_ws_inv_get_phase,
179
+ .set_phase = meson_sclk_ws_inv_set_phase,
180
+};
181
+EXPORT_SYMBOL_GPL(meson_sclk_ws_inv_ops);
182
+
183
+
184
+MODULE_DESCRIPTION("Amlogic phase driver");
185
+MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
186
+MODULE_LICENSE("GPL v2");