.. | .. |
---|
5 | 5 | */ |
---|
6 | 6 | |
---|
7 | 7 | #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" |
---|
9 | 12 | |
---|
10 | 13 | #define phase_step(_width) (360 / (1 << (_width))) |
---|
11 | 14 | |
---|
.. | .. |
---|
15 | 18 | return (struct meson_clk_phase_data *)clk->data; |
---|
16 | 19 | } |
---|
17 | 20 | |
---|
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) |
---|
19 | 22 | { |
---|
20 | 23 | return phase_step(width) * val; |
---|
21 | 24 | } |
---|
22 | | -EXPORT_SYMBOL_GPL(meson_clk_degrees_from_val); |
---|
23 | 25 | |
---|
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) |
---|
25 | 27 | { |
---|
26 | 28 | unsigned int val = DIV_ROUND_CLOSEST(degrees, phase_step(width)); |
---|
27 | 29 | |
---|
.. | .. |
---|
31 | 33 | */ |
---|
32 | 34 | return val % (1 << width); |
---|
33 | 35 | } |
---|
34 | | -EXPORT_SYMBOL_GPL(meson_clk_degrees_to_val); |
---|
35 | 36 | |
---|
36 | 37 | static int meson_clk_phase_get_phase(struct clk_hw *hw) |
---|
37 | 38 | { |
---|
.. | .. |
---|
61 | 62 | .set_phase = meson_clk_phase_set_phase, |
---|
62 | 63 | }; |
---|
63 | 64 | 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"); |
---|