rk27xx: don't reprogram the codec PLL for an unchanged rate

Every start of recording applies the sample rate again, mostly
unchanged, and set_codec_freq() reprogrammed the codec PLL each time.
That glitches MCLK and every I2S clock made of it, right before the
recording receiver is reset and starts on them. Whether it then kept
its framing came down to code timing: on the Samsung YP-CP3 the
recording screen's peak meter showed one channel empty and the other
saturated on some visits, and builds with debug code added never did.

The PLL is now left alone when it already runs the rate asked for. On a
real change the lock bit, which may still show the old lock at first,
is polled only after the 0.3 ms the datasheet gives for locking, with a
timeout of at least a full tick, and the clocks get 1 ms more to settle
before anything starts on them.

Tested on a YP-CP3, together with the codec as I2S master: the peak
meter on every one of many visits to the recording screen, with the
microphone and FM, and at 22 kHz.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: I2267e17c5bc7d8044715e762dbb6b92f45af4dcc
This commit is contained in:
Marcin Bukat 2026-10-05 22:30:01 +02:00
parent 59f15ca630
commit ce968e7da1

View file

@ -207,8 +207,17 @@ static void i2s_init(void)
*/
static void set_codec_freq(unsigned int freq)
{
static unsigned int cur_freq = HW_NUM_FREQ; /* none yet */
long timeout;
/* Reprogramming the PLL glitches MCLK, and with it every clock the
* codec makes of it: an I2S side started on them can lose its framing.
* The rate is applied again on every start of recording, mostly
* unchanged. */
if (freq == cur_freq)
return;
cur_freq = freq;
/* {CLKR, CLKF, CLKOD, CODECPLL_DIV} */
static const unsigned int pcm_freq_params[HW_NUM_FREQ][4] =
{
@ -237,13 +246,18 @@ static void set_codec_freq(unsigned int freq)
(pcm_freq_params[freq][2]<<1) ; /* CLKOD factor */
/* wait for CODEC PLL lock with 10 ms timeout
* datasheet states that pll lock should take approx. 0.3 ms
* datasheet states that pll lock should take approx. 0.3 ms; the lock bit
* may still show the old lock at first
*/
timeout = current_tick + (HZ/100);
udelay(300);
timeout = current_tick + (HZ/100) + 1;
while (!(SCU_STATUS & (1<<2)))
if (TIME_AFTER(current_tick, timeout))
break;
/* let the codec's clocks settle before an I2S side starts on them */
udelay(1000);
}
#endif