1.1 --- a/pkg/devices/lib/cpm/include/cpm-common.h Fri Sep 22 21:55:54 2023 +0200
1.2 +++ b/pkg/devices/lib/cpm/include/cpm-common.h Fri Sep 22 21:56:34 2023 +0200
1.3 @@ -81,7 +81,9 @@
1.4
1.5 uint32_t get_field(Cpm_regs ®s);
1.6 void set_field(Cpm_regs ®s, uint32_t value);
1.7 +
1.8 bool is_defined() { return defined; }
1.9 + uint32_t get_limit() { return mask; }
1.10
1.11 // Undefined field object.
1.12
1.13 @@ -231,6 +233,9 @@
1.14
1.15 int pll_bypassed(Cpm_regs ®s);
1.16
1.17 + void pll_bypass(Cpm_regs ®s);
1.18 + void pll_engage(Cpm_regs ®s);
1.19 +
1.20 void wait_busy(Cpm_regs ®s);
1.21 int have_clock(Cpm_regs ®s);
1.22 void start_clock(Cpm_regs ®s);
1.23 @@ -249,6 +254,7 @@
1.24 // Output frequency.
1.25
1.26 virtual uint32_t get_frequency(Cpm_regs ®s, uint32_t source_frequency) = 0;
1.27 + virtual int set_frequency(Cpm_regs ®s, uint32_t source_frequency, uint32_t frequency) = 0;
1.28
1.29 // Other operations.
1.30
1.31 @@ -284,6 +290,7 @@
1.32 // Output frequency.
1.33
1.34 uint32_t get_frequency(Cpm_regs ®s, uint32_t source_frequency);
1.35 + int set_frequency(Cpm_regs ®s, uint32_t source_frequency, uint32_t frequency);
1.36
1.37 // Other operations.
1.38
1.39 @@ -319,6 +326,7 @@
1.40 // Output frequency.
1.41
1.42 uint32_t get_frequency(Cpm_regs ®s, uint32_t source_frequency);
1.43 + int set_frequency(Cpm_regs ®s, uint32_t source_frequency, uint32_t frequency);
1.44
1.45 // Other operations.
1.46
1.47 @@ -351,6 +359,7 @@
1.48 // Output frequency.
1.49
1.50 uint32_t get_frequency(Cpm_regs ®s, uint32_t source_frequency);
1.51 + int set_frequency(Cpm_regs ®s, uint32_t source_frequency, uint32_t frequency);
1.52
1.53 // Other operations.
1.54
1.55 @@ -493,6 +502,7 @@
1.56 // Output frequency.
1.57
1.58 uint32_t get_frequency(Cpm_regs ®s);
1.59 + virtual int set_frequency(Cpm_regs ®s, uint32_t frequency);
1.60 };
1.61
1.62
1.63 @@ -520,6 +530,7 @@
1.64 // Output frequency.
1.65
1.66 uint32_t get_frequency(Cpm_regs ®s);
1.67 + int set_frequency(Cpm_regs ®s, uint32_t frequency);
1.68 };
1.69
1.70
2.1 --- a/pkg/devices/lib/cpm/include/cpm-x1600.h Fri Sep 22 21:55:54 2023 +0200
2.2 +++ b/pkg/devices/lib/cpm/include/cpm-x1600.h Fri Sep 22 21:56:34 2023 +0200
2.3 @@ -72,7 +72,7 @@
2.4 // Output clock frequencies.
2.5
2.6 uint32_t get_frequency(enum Clock_identifiers clock);
2.7 - void set_frequency(enum Clock_identifiers clock, uint32_t frequency);
2.8 + int set_frequency(enum Clock_identifiers clock, uint32_t frequency);
2.9 };
2.10
2.11 #endif /* __cplusplus */
2.12 @@ -104,6 +104,6 @@
2.13 uint32_t x1600_cpm_get_source_frequency(void *cpm, enum Clock_identifiers clock);
2.14
2.15 uint32_t x1600_cpm_get_frequency(void *cpm, enum Clock_identifiers clock);
2.16 -void x1600_cpm_set_frequency(void *cpm, enum Clock_identifiers clock, uint32_t frequency);
2.17 +int x1600_cpm_set_frequency(void *cpm, enum Clock_identifiers clock, uint32_t frequency);
2.18
2.19 EXTERN_C_END
3.1 --- a/pkg/devices/lib/cpm/src/common.cc Fri Sep 22 21:55:54 2023 +0200
3.2 +++ b/pkg/devices/lib/cpm/src/common.cc Fri Sep 22 21:56:34 2023 +0200
3.3 @@ -226,6 +226,18 @@
3.4 return _bypass.get_field(regs);
3.5 }
3.6
3.7 +void
3.8 +Control_pll::pll_bypass(Cpm_regs ®s)
3.9 +{
3.10 + _bypass.set_field(regs, 1);
3.11 +}
3.12 +
3.13 +void
3.14 +Control_pll::pll_engage(Cpm_regs ®s)
3.15 +{
3.16 + _bypass.set_field(regs, 0);
3.17 +}
3.18 +
3.19 // Clock control.
3.20
3.21 int
3.22 @@ -290,6 +302,13 @@
3.23 }
3.24
3.25 int
3.26 +Divider::set_frequency(Cpm_regs ®s, uint32_t source_frequency, uint32_t frequency)
3.27 +{
3.28 + set_divider(regs, (uint32_t) round((double) source_frequency / (double) frequency));
3.29 + return 1;
3.30 +}
3.31 +
3.32 +int
3.33 Divider::get_parameters(Cpm_regs ®s, uint32_t parameters[])
3.34 {
3.35 parameters[0] = get_divider(regs);
3.36 @@ -310,41 +329,97 @@
3.37
3.38
3.39
3.40 -// Feedback (13-bit) multiplier.
3.41 +// Common divider functionality.
3.42 +
3.43 +static int is_integer(double x)
3.44 +{
3.45 + double target = round(x) * 1000;
3.46 + double rounded = floor(x * 1000);
3.47 +
3.48 + return (target - 100 < rounded) && (rounded < target + 100);
3.49 +}
3.50 +
3.51 +static double getscale_part(double x)
3.52 +{
3.53 + double part = x - floor(x);
3.54 +
3.55 + if (part > 0.5)
3.56 + return 1 / (1 - part);
3.57 + else if (part > 0)
3.58 + return 1 / part;
3.59 + else
3.60 + return 1;
3.61 +}
3.62 +
3.63 +static double getscale(double x)
3.64 +{
3.65 + double scale = getscale_part(x);
3.66 +
3.67 + if (is_integer(scale))
3.68 + return scale;
3.69 + else
3.70 + return scale * getscale(scale);
3.71 +}
3.72 +
3.73 +static void get_divider_operands(double frequency, double source_frequency,
3.74 + double *multiplier, double *divider)
3.75 +{
3.76 + double ratio = frequency / source_frequency;
3.77 + double scale = getscale(ratio);
3.78 +
3.79 + *multiplier = scale * ratio;
3.80 + *divider = scale;
3.81 +}
3.82 +
3.83 +static void reduce_divider_operands(uint32_t *m, uint32_t *n, uint32_t m_limit,
3.84 + uint32_t n_limit)
3.85 +{
3.86 + while ((*m > m_limit) && (*n > n_limit) && (*m > 1) && (*n > 1))
3.87 + {
3.88 + *m >>= 1;
3.89 + *n >>= 1;
3.90 + }
3.91 +}
3.92 +
3.93 +#define zero_as_one(X) ((X) ? (X) : 1)
3.94 +
3.95 +
3.96 +
3.97 +// Feedback multiplier.
3.98
3.99 uint32_t
3.100 Divider_pll::get_multiplier(Cpm_regs ®s)
3.101 {
3.102 - return _multiplier.get_field(regs) + 1;
3.103 + return zero_as_one(_multiplier.get_field(regs));
3.104 }
3.105
3.106 void
3.107 Divider_pll::set_multiplier(Cpm_regs ®s, uint32_t multiplier)
3.108 {
3.109 - _multiplier.set_field(regs, multiplier - 1);
3.110 + _multiplier.set_field(regs, multiplier);
3.111 }
3.112
3.113 -// Input (6-bit) divider.
3.114 +// Input divider.
3.115
3.116 uint32_t
3.117 Divider_pll::get_input_divider(Cpm_regs ®s)
3.118 {
3.119 - return _input_divider.get_field(regs) + 1;
3.120 + return zero_as_one(_input_divider.get_field(regs));
3.121 }
3.122
3.123 void
3.124 Divider_pll::set_input_divider(Cpm_regs ®s, uint32_t divider)
3.125 {
3.126 - _input_divider.set_field(regs, divider - 1);
3.127 + _input_divider.set_field(regs, divider);
3.128 }
3.129
3.130 -// Output (dual 3-bit) dividers.
3.131 +// Output dividers.
3.132
3.133 uint32_t
3.134 Divider_pll::get_output_divider(Cpm_regs ®s)
3.135 {
3.136 - uint32_t d0 = _output_divider0.get_field(regs);
3.137 - uint32_t d1 = _output_divider1.get_field(regs);
3.138 + uint32_t d0 = zero_as_one(_output_divider0.get_field(regs));
3.139 + uint32_t d1 = zero_as_one(_output_divider1.get_field(regs));
3.140
3.141 return d0 * d1;
3.142 }
3.143 @@ -355,8 +430,21 @@
3.144 // Assert 1 as a minimum.
3.145 // Divider 0 must be less than or equal to divider 1.
3.146
3.147 - uint32_t d0 = (uint32_t) floor(sqrt(divider ? divider : 1));
3.148 - uint32_t d1 = divider / d0;
3.149 + uint32_t d0, d1;
3.150 +
3.151 + if (!divider)
3.152 + divider = 1;
3.153 +
3.154 + if (divider < _output_divider1.get_limit())
3.155 + {
3.156 + d0 = 1;
3.157 + d1 = divider;
3.158 + }
3.159 + else
3.160 + {
3.161 + d0 = (uint32_t) floor(sqrt(divider));
3.162 + d1 = divider / d0;
3.163 + }
3.164
3.165 _output_divider0.set_field(regs, d0);
3.166 _output_divider1.set_field(regs, d1);
3.167 @@ -370,6 +458,66 @@
3.168 }
3.169
3.170 int
3.171 +Divider_pll::set_frequency(Cpm_regs ®s, uint32_t source_frequency, uint32_t frequency)
3.172 +{
3.173 + double intermediate_min = 600000000, intermediate_max = 2400000000;
3.174 + double intermediate_multiplier, intermediate_input_divider;
3.175 + uint32_t output_min, output_max, output0, output1;
3.176 + uint32_t multiplier, input_divider, output_divider;
3.177 +
3.178 + // Distribute the divider across the input and output divider. For the X1600,
3.179 + // the multiplier and input divider should collectively deliver a frequency in
3.180 + // the range 600-2400 MHz.
3.181 +
3.182 + output_min = (uint32_t) ceil(intermediate_min / frequency);
3.183 + output_max = (uint32_t) floor(intermediate_max / frequency);
3.184 +
3.185 + output_divider = output_min;
3.186 +
3.187 + while (output_divider <= output_max)
3.188 + {
3.189 + // Test divider constraints.
3.190 +
3.191 + output0 = (uint32_t) floor(sqrt(output_divider));
3.192 + output1 = (uint32_t) floor(output_divider / output0);
3.193 +
3.194 + if ((output0 * output1 == output_divider) &&
3.195 + (output0 <= _output_divider0.get_limit()) &&
3.196 + (output1 <= _output_divider1.get_limit()))
3.197 + {
3.198 + // Calculate the other parameters.
3.199 +
3.200 + uint32_t intermediate_frequency = frequency * output_divider;
3.201 +
3.202 + get_divider_operands(intermediate_frequency, source_frequency,
3.203 + &intermediate_multiplier, &intermediate_input_divider);
3.204 +
3.205 + multiplier = (uint32_t) round(intermediate_multiplier);
3.206 + input_divider = (uint32_t) round(intermediate_input_divider);
3.207 +
3.208 + uint32_t multiplier_limit = _multiplier.get_limit();
3.209 + uint32_t input_divider_limit = _input_divider.get_limit();
3.210 +
3.211 + reduce_divider_operands(&multiplier, &input_divider,
3.212 + multiplier_limit, input_divider_limit);
3.213 +
3.214 + if ((multiplier <= multiplier_limit) && (input_divider <= input_divider_limit))
3.215 + {
3.216 + set_multiplier(regs, multiplier);
3.217 + set_input_divider(regs, input_divider);
3.218 + set_output_divider(regs, output_divider);
3.219 +
3.220 + return 1;
3.221 + }
3.222 + }
3.223 +
3.224 + output_divider++;
3.225 + }
3.226 +
3.227 + return 0;
3.228 +}
3.229 +
3.230 +int
3.231 Divider_pll::get_parameters(Cpm_regs ®s, uint32_t parameters[])
3.232 {
3.233 parameters[0] = get_multiplier(regs);
3.234 @@ -418,11 +566,46 @@
3.235 uint32_t
3.236 Divider_i2s::get_frequency(Cpm_regs ®s, uint32_t source_frequency)
3.237 {
3.238 + /* NOTE: Assuming that this is the formula, given that the manual does not
3.239 + really describe how D is used. */
3.240 +
3.241 return (source_frequency * get_multiplier(regs)) /
3.242 (get_divider_N(regs) * get_divider_D(regs));
3.243 }
3.244
3.245 int
3.246 +Divider_i2s::set_frequency(Cpm_regs ®s, uint32_t source_frequency, uint32_t frequency)
3.247 +{
3.248 + double m, n;
3.249 +
3.250 + get_divider_operands(frequency, source_frequency, &m, &n);
3.251 +
3.252 + uint32_t multiplier = (uint32_t) round(m);
3.253 + uint32_t divider = (uint32_t) round(n);
3.254 +
3.255 + reduce_divider_operands(&multiplier, ÷r,
3.256 + _multiplier.get_limit(),
3.257 + _divider_N.get_limit());
3.258 +
3.259 + // Test for operand within limits and the N >= 2M constraint.
3.260 +
3.261 + if ((multiplier <= _multiplier.get_limit()) && (divider <= _divider_N.get_limit()) &&
3.262 + (divider >= 2 * multiplier))
3.263 + {
3.264 + /* NOTE: Setting D to 1. Even though it seems that D might also be used,
3.265 + it does not seem necessary in practice, and the documentation is
3.266 + unclear about its use. */
3.267 +
3.268 + uint32_t parameters[] = {multiplier, divider, 1};
3.269 +
3.270 + set_parameters(regs, 3, parameters);
3.271 + return 1;
3.272 + }
3.273 +
3.274 + return 0;
3.275 +}
3.276 +
3.277 +int
3.278 Divider_i2s::get_parameters(Cpm_regs ®s, uint32_t parameters[])
3.279 {
3.280 parameters[0] = get_multiplier(regs);
3.281 @@ -446,7 +629,7 @@
3.282 }
3.283 else if (num_parameters > 1)
3.284 {
3.285 - // Test for N < 2M.
3.286 + // Require N >= 2M, returning otherwise.
3.287
3.288 if (parameters[1] < 2 * parameters[0])
3.289 return 0;
3.290 @@ -635,6 +818,17 @@
3.291 }
3.292
3.293 int
3.294 +Clock_divided_base::set_frequency(Cpm_regs ®s, uint32_t frequency)
3.295 +{
3.296 + _get_control().change_enable(regs);
3.297 + int result = _get_divider().set_frequency(regs, get_source_frequency(regs), frequency);
3.298 + _get_control().wait_busy(regs);
3.299 + _get_control().change_disable(regs);
3.300 +
3.301 + return result;
3.302 +}
3.303 +
3.304 +int
3.305 Clock_divided_base::get_parameters(Cpm_regs ®s, uint32_t parameters[])
3.306 {
3.307 return _get_divider().get_parameters(regs, parameters);
3.308 @@ -667,3 +861,12 @@
3.309 else
3.310 return get_source_frequency(regs);
3.311 }
3.312 +
3.313 +int
3.314 +Pll::set_frequency(Cpm_regs ®s, uint32_t frequency)
3.315 +{
3.316 + int result = Clock_divided_base::set_frequency(regs, frequency);
3.317 + _control.pll_engage(regs);
3.318 +
3.319 + return result;
3.320 +}
4.1 --- a/pkg/devices/lib/cpm/src/x1600.cc Fri Sep 22 21:55:54 2023 +0200
4.2 +++ b/pkg/devices/lib/cpm/src/x1600.cc Fri Sep 22 21:56:34 2023 +0200
4.3 @@ -219,9 +219,9 @@
4.4 Pll_bypass_E (Pll_control_E, 1, 26), // EPLL_BP
4.5 Pll_bypass_M (Pll_control_M, 1, 28), // MPLL_BP
4.6
4.7 - Pll_multiplier_A (Pll_control_A, 0x1fff, 20), // APLLM
4.8 - Pll_multiplier_E (Pll_control_E, 0x1fff, 20), // EPLLM
4.9 - Pll_multiplier_M (Pll_control_M, 0x1fff, 20), // MPLLM
4.10 + Pll_multiplier_A (Pll_control_A, 0xfff, 20), // APLLM
4.11 + Pll_multiplier_E (Pll_control_E, 0x3f, 20), // EPLLM (observed)
4.12 + Pll_multiplier_M (Pll_control_M, 0xfff, 20), // MPLLM
4.13
4.14 Pll_input_division_A (Pll_control_A, 0x3f, 14), // APLLN
4.15 Pll_input_division_E (Pll_control_E, 0x3f, 14), // EPLLN
4.16 @@ -564,36 +564,15 @@
4.17 return clocks[clock]->get_frequency(_cpm_regs);
4.18 }
4.19
4.20 -void
4.21 +int
4.22 Cpm_x1600_chip::set_frequency(enum Clock_identifiers clock, uint32_t frequency)
4.23 {
4.24 - switch (clock)
4.25 - {
4.26 - // The pixel frequency is based on the selected clock source (SCLK_A, MPLL or
4.27 - // EPLL).
4.28 -
4.29 - case Clock_lcd_pixel:
4.30 - {
4.31 -
4.32 - // Switch to the MPLL and attempt to set the divider.
4.33 -
4.34 - Clock_divided_base *lcd = dynamic_cast<Clock_divided_base *>(clocks[Clock_lcd_pixel]);
4.35 - Clock_base *pll = clocks[Clock_pll_M];
4.36 + Clock_divided_base *clk = dynamic_cast<Clock_divided_base *>(clocks[clock]);
4.37
4.38 - if (lcd != NULL)
4.39 - {
4.40 - uint32_t parameters[] = {lcd->get_source_frequency(_cpm_regs) / frequency};
4.41 -
4.42 - lcd->set_source(_cpm_regs, Source_mME_pll_M);
4.43 - pll->start_clock(_cpm_regs);
4.44 - lcd->set_parameters(_cpm_regs, 1, parameters);
4.45 - }
4.46 - break;
4.47 - }
4.48 -
4.49 - default:
4.50 - break;
4.51 - }
4.52 + if (clk != NULL)
4.53 + return clk->set_frequency(_cpm_regs, frequency);
4.54 + else
4.55 + return 0;
4.56 }
4.57
4.58
4.59 @@ -681,8 +660,8 @@
4.60 return static_cast<Cpm_x1600_chip *>(cpm)->get_frequency(clock);
4.61 }
4.62
4.63 -void
4.64 +int
4.65 x1600_cpm_set_frequency(void *cpm, enum Clock_identifiers clock, uint32_t frequency)
4.66 {
4.67 - static_cast<Cpm_x1600_chip *>(cpm)->set_frequency(clock, frequency);
4.68 + return static_cast<Cpm_x1600_chip *>(cpm)->set_frequency(clock, frequency);
4.69 }