static u32 rsnd_adg_calculate_brgx(unsignedlong div)
{ int i;
if (!div) return 0;
for (i = 3; i >= 0; i--) { int ratio = 2 << (i * 2); if (0 == (div % ratio)) return (u32)((i << 8) | ((div / ratio) - 1));
}
return ~0;
}
static u32 rsnd_adg_ssi_ws_timing_gen2(struct rsnd_dai_stream *io)
{ struct rsnd_mod *ssi_mod = rsnd_io_to_mod_ssi(io); int id = rsnd_mod_id(ssi_mod); int ws = id;
if (rsnd_ssi_is_pin_sharing(io)) { switch (id) { case 1: case 2: case 9:
ws = 0; break; case 4:
ws = 3; break; case 8:
ws = 7; break;
}
} else { /* * SSI8 is not connected to ADG. * Thus SSI9 is using ws = 8
*/ if (id == 9)
ws = 8;
}
/* * ADG supports BRRA/BRRB output only * this means all clkout0/1/2/3 will be same rate
*/
prop = of_find_property(np, "clock-frequency", NULL); if (!prop) goto rsnd_adg_get_clkout_end;
req_size = prop->length / sizeof(u32); if (req_size > ADG_HZ_SIZE) {
dev_err(dev, "too many clock-frequency\n"); return -EINVAL;
}
of_property_read_u32_array(np, "clock-frequency", req_rate, req_size);
req_Hz[ADG_HZ_48] = 0;
req_Hz[ADG_HZ_441] = 0; for (i = 0; i < req_size; i++) { if (0 == (req_rate[i] % 44100))
req_Hz[ADG_HZ_441] = req_rate[i]; if (0 == (req_rate[i] % 48000))
req_Hz[ADG_HZ_48] = req_rate[i];
}
/* * This driver is assuming that AUDIO_CLKA/AUDIO_CLKB/AUDIO_CLKC * have 44.1kHz or 48kHz base clocks for now. * * SSI itself can divide parent clock by 1/1 - 1/16 * see * rsnd_adg_ssi_clk_try_start() * rsnd_ssi_master_clk_start()
*/
/* * [APPROXIMATE] * * clk_i (internal clock) can't create accurate rate, it will be approximate rate. * * <Note> * * clk_i needs x2 of required maximum rate. * see * - Minimum division of BRRA/BRRB * - rsnd_ssi_clk_query() * * Sample Settings for TDM 8ch, 32bit width * * 8(ch) x 32(bit) x 44100(Hz) x 2<Note> = 22579200 * 8(ch) x 32(bit) x 48000(Hz) x 2<Note> = 24576000 * * clock-frequency = <22579200 24576000>;
*/
for_each_rsnd_clkin(clk, adg, i) {
u32 rate, div;
rate = clk_get_rate(clk);
if (0 == rate) /* not used */ continue;
/* BRGA */
if (i == CLKI) /* see [APPROXIMATE] */
rate = (clk_get_rate(clk) / req_Hz[ADG_HZ_441]) * req_Hz[ADG_HZ_441]; if (!adg->brg_rate[ADG_HZ_441] && req_Hz[ADG_HZ_441] && (0 == rate % 44100)) {
div = rate / req_Hz[ADG_HZ_441];
brgx = rsnd_adg_calculate_brgx(div); if (BRRx_MASK(brgx) == brgx) {
brga = brgx;
adg->brg_rate[ADG_HZ_441] = rate / div;
ckr |= brg_table[i] << 20; if (req_Hz[ADG_HZ_441])
parent_clk_name = __clk_get_name(clk); if (i == CLKI)
approximate = 1;
}
}
/* BRGB */
if (i == CLKI) /* see [APPROXIMATE] */
rate = (clk_get_rate(clk) / req_Hz[ADG_HZ_48]) * req_Hz[ADG_HZ_48]; if (!adg->brg_rate[ADG_HZ_48] && req_Hz[ADG_HZ_48] && (0 == rate % 48000)) {
div = rate / req_Hz[ADG_HZ_48];
brgx = rsnd_adg_calculate_brgx(div); if (BRRx_MASK(brgx) == brgx) {
brgb = brgx;
adg->brg_rate[ADG_HZ_48] = rate / div;
ckr |= brg_table[i] << 16; if (req_Hz[ADG_HZ_48])
parent_clk_name = __clk_get_name(clk); if (i == CLKI)
approximate = 1;
}
}
}
if (!(adg->brg_rate[ADG_HZ_48] && req_Hz[ADG_HZ_48]) &&
!(adg->brg_rate[ADG_HZ_441] && req_Hz[ADG_HZ_441])) goto rsnd_adg_get_clkout_end;
if (approximate)
dev_info(dev, "It uses CLK_I as approximate rate");
dbg_msg(dev, m, "BRGCKR = 0x%08x, BRRA/BRRB = 0x%x/0x%x\n",
adg->ckr, adg->brga, adg->brgb);
dbg_msg(dev, m, "BRGA (for 44100 base) = %d\n", adg->brg_rate[ADG_HZ_441]);
dbg_msg(dev, m, "BRGB (for 48000 base) = %d\n", adg->brg_rate[ADG_HZ_48]);
/* * Actual CLKOUT will be exchanged in rsnd_adg_ssi_clk_try_start() * by BRGCKR::BRGCKR_31
*/
for_each_rsnd_clkout(clk, adg, i)
dbg_msg(dev, m, "%-18s : %pa : %ld\n",
__clk_get_name(clk), clk, clk_get_rate(clk));
} #else #define rsnd_adg_clk_dbg_info(priv, m) #endif
int rsnd_adg_probe(struct rsnd_priv *priv)
{ struct rsnd_adg *adg; struct device *dev = rsnd_priv_to_dev(priv); int ret;
adg = devm_kzalloc(dev, sizeof(*adg), GFP_KERNEL); if (!adg) return -ENOMEM;
ret = rsnd_mod_init(priv, &adg->mod, &adg_ops,
NULL, 0, 0); if (ret) return ret;
priv->adg = adg;
ret = rsnd_adg_get_clkin(priv); if (ret) return ret;
ret = rsnd_adg_get_clkout(priv); if (ret) return ret;
ret = rsnd_adg_clk_enable(priv); if (ret) return ret;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.