18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci// 38c2ecf20Sopenharmony_ci// Helper routines for R-Car sound ADG. 48c2ecf20Sopenharmony_ci// 58c2ecf20Sopenharmony_ci// Copyright (C) 2013 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/clk-provider.h> 88c2ecf20Sopenharmony_ci#include "rsnd.h" 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#define CLKA 0 118c2ecf20Sopenharmony_ci#define CLKB 1 128c2ecf20Sopenharmony_ci#define CLKC 2 138c2ecf20Sopenharmony_ci#define CLKI 3 148c2ecf20Sopenharmony_ci#define CLKMAX 4 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#define CLKOUT 0 178c2ecf20Sopenharmony_ci#define CLKOUT1 1 188c2ecf20Sopenharmony_ci#define CLKOUT2 2 198c2ecf20Sopenharmony_ci#define CLKOUT3 3 208c2ecf20Sopenharmony_ci#define CLKOUTMAX 4 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#define BRRx_MASK(x) (0x3FF & x) 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistatic struct rsnd_mod_ops adg_ops = { 258c2ecf20Sopenharmony_ci .name = "adg", 268c2ecf20Sopenharmony_ci}; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistruct rsnd_adg { 298c2ecf20Sopenharmony_ci struct clk *clk[CLKMAX]; 308c2ecf20Sopenharmony_ci struct clk *clkout[CLKOUTMAX]; 318c2ecf20Sopenharmony_ci struct clk_onecell_data onecell; 328c2ecf20Sopenharmony_ci struct rsnd_mod mod; 338c2ecf20Sopenharmony_ci int clk_rate[CLKMAX]; 348c2ecf20Sopenharmony_ci u32 flags; 358c2ecf20Sopenharmony_ci u32 ckr; 368c2ecf20Sopenharmony_ci u32 rbga; 378c2ecf20Sopenharmony_ci u32 rbgb; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci int rbga_rate_for_441khz; /* RBGA */ 408c2ecf20Sopenharmony_ci int rbgb_rate_for_48khz; /* RBGB */ 418c2ecf20Sopenharmony_ci}; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci#define LRCLK_ASYNC (1 << 0) 448c2ecf20Sopenharmony_ci#define AUDIO_OUT_48 (1 << 1) 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci#define for_each_rsnd_clk(pos, adg, i) \ 478c2ecf20Sopenharmony_ci for (i = 0; \ 488c2ecf20Sopenharmony_ci (i < CLKMAX) && \ 498c2ecf20Sopenharmony_ci ((pos) = adg->clk[i]); \ 508c2ecf20Sopenharmony_ci i++) 518c2ecf20Sopenharmony_ci#define for_each_rsnd_clkout(pos, adg, i) \ 528c2ecf20Sopenharmony_ci for (i = 0; \ 538c2ecf20Sopenharmony_ci (i < CLKOUTMAX) && \ 548c2ecf20Sopenharmony_ci ((pos) = adg->clkout[i]); \ 558c2ecf20Sopenharmony_ci i++) 568c2ecf20Sopenharmony_ci#define rsnd_priv_to_adg(priv) ((struct rsnd_adg *)(priv)->adg) 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_cistatic const char * const clk_name[] = { 598c2ecf20Sopenharmony_ci [CLKA] = "clk_a", 608c2ecf20Sopenharmony_ci [CLKB] = "clk_b", 618c2ecf20Sopenharmony_ci [CLKC] = "clk_c", 628c2ecf20Sopenharmony_ci [CLKI] = "clk_i", 638c2ecf20Sopenharmony_ci}; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic u32 rsnd_adg_calculate_rbgx(unsigned long div) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci int i, ratio; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci if (!div) 708c2ecf20Sopenharmony_ci return 0; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci for (i = 3; i >= 0; i--) { 738c2ecf20Sopenharmony_ci ratio = 2 << (i * 2); 748c2ecf20Sopenharmony_ci if (0 == (div % ratio)) 758c2ecf20Sopenharmony_ci return (u32)((i << 8) | ((div / ratio) - 1)); 768c2ecf20Sopenharmony_ci } 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci return ~0; 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_cistatic u32 rsnd_adg_ssi_ws_timing_gen2(struct rsnd_dai_stream *io) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci struct rsnd_mod *ssi_mod = rsnd_io_to_mod_ssi(io); 848c2ecf20Sopenharmony_ci int id = rsnd_mod_id(ssi_mod); 858c2ecf20Sopenharmony_ci int ws = id; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci if (rsnd_ssi_is_pin_sharing(io)) { 888c2ecf20Sopenharmony_ci switch (id) { 898c2ecf20Sopenharmony_ci case 1: 908c2ecf20Sopenharmony_ci case 2: 918c2ecf20Sopenharmony_ci case 9: 928c2ecf20Sopenharmony_ci ws = 0; 938c2ecf20Sopenharmony_ci break; 948c2ecf20Sopenharmony_ci case 4: 958c2ecf20Sopenharmony_ci ws = 3; 968c2ecf20Sopenharmony_ci break; 978c2ecf20Sopenharmony_ci case 8: 988c2ecf20Sopenharmony_ci ws = 7; 998c2ecf20Sopenharmony_ci break; 1008c2ecf20Sopenharmony_ci } 1018c2ecf20Sopenharmony_ci } 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci return (0x6 + ws) << 8; 1048c2ecf20Sopenharmony_ci} 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_cistatic void __rsnd_adg_get_timesel_ratio(struct rsnd_priv *priv, 1078c2ecf20Sopenharmony_ci struct rsnd_dai_stream *io, 1088c2ecf20Sopenharmony_ci unsigned int target_rate, 1098c2ecf20Sopenharmony_ci unsigned int *target_val, 1108c2ecf20Sopenharmony_ci unsigned int *target_en) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci struct rsnd_adg *adg = rsnd_priv_to_adg(priv); 1138c2ecf20Sopenharmony_ci struct device *dev = rsnd_priv_to_dev(priv); 1148c2ecf20Sopenharmony_ci int idx, sel, div, step; 1158c2ecf20Sopenharmony_ci unsigned int val, en; 1168c2ecf20Sopenharmony_ci unsigned int min, diff; 1178c2ecf20Sopenharmony_ci unsigned int sel_rate[] = { 1188c2ecf20Sopenharmony_ci adg->clk_rate[CLKA], /* 0000: CLKA */ 1198c2ecf20Sopenharmony_ci adg->clk_rate[CLKB], /* 0001: CLKB */ 1208c2ecf20Sopenharmony_ci adg->clk_rate[CLKC], /* 0010: CLKC */ 1218c2ecf20Sopenharmony_ci adg->rbga_rate_for_441khz, /* 0011: RBGA */ 1228c2ecf20Sopenharmony_ci adg->rbgb_rate_for_48khz, /* 0100: RBGB */ 1238c2ecf20Sopenharmony_ci }; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci min = ~0; 1268c2ecf20Sopenharmony_ci val = 0; 1278c2ecf20Sopenharmony_ci en = 0; 1288c2ecf20Sopenharmony_ci for (sel = 0; sel < ARRAY_SIZE(sel_rate); sel++) { 1298c2ecf20Sopenharmony_ci idx = 0; 1308c2ecf20Sopenharmony_ci step = 2; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci if (!sel_rate[sel]) 1338c2ecf20Sopenharmony_ci continue; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci for (div = 2; div <= 98304; div += step) { 1368c2ecf20Sopenharmony_ci diff = abs(target_rate - sel_rate[sel] / div); 1378c2ecf20Sopenharmony_ci if (min > diff) { 1388c2ecf20Sopenharmony_ci val = (sel << 8) | idx; 1398c2ecf20Sopenharmony_ci min = diff; 1408c2ecf20Sopenharmony_ci en = 1 << (sel + 1); /* fixme */ 1418c2ecf20Sopenharmony_ci } 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci /* 1448c2ecf20Sopenharmony_ci * step of 0_0000 / 0_0001 / 0_1101 1458c2ecf20Sopenharmony_ci * are out of order 1468c2ecf20Sopenharmony_ci */ 1478c2ecf20Sopenharmony_ci if ((idx > 2) && (idx % 2)) 1488c2ecf20Sopenharmony_ci step *= 2; 1498c2ecf20Sopenharmony_ci if (idx == 0x1c) { 1508c2ecf20Sopenharmony_ci div += step; 1518c2ecf20Sopenharmony_ci step *= 2; 1528c2ecf20Sopenharmony_ci } 1538c2ecf20Sopenharmony_ci idx++; 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci } 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci if (min == ~0) { 1588c2ecf20Sopenharmony_ci dev_err(dev, "no Input clock\n"); 1598c2ecf20Sopenharmony_ci return; 1608c2ecf20Sopenharmony_ci } 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci *target_val = val; 1638c2ecf20Sopenharmony_ci if (target_en) 1648c2ecf20Sopenharmony_ci *target_en = en; 1658c2ecf20Sopenharmony_ci} 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_cistatic void rsnd_adg_get_timesel_ratio(struct rsnd_priv *priv, 1688c2ecf20Sopenharmony_ci struct rsnd_dai_stream *io, 1698c2ecf20Sopenharmony_ci unsigned int in_rate, 1708c2ecf20Sopenharmony_ci unsigned int out_rate, 1718c2ecf20Sopenharmony_ci u32 *in, u32 *out, u32 *en) 1728c2ecf20Sopenharmony_ci{ 1738c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); 1748c2ecf20Sopenharmony_ci unsigned int target_rate; 1758c2ecf20Sopenharmony_ci u32 *target_val; 1768c2ecf20Sopenharmony_ci u32 _in; 1778c2ecf20Sopenharmony_ci u32 _out; 1788c2ecf20Sopenharmony_ci u32 _en; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci /* default = SSI WS */ 1818c2ecf20Sopenharmony_ci _in = 1828c2ecf20Sopenharmony_ci _out = rsnd_adg_ssi_ws_timing_gen2(io); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci target_rate = 0; 1858c2ecf20Sopenharmony_ci target_val = NULL; 1868c2ecf20Sopenharmony_ci _en = 0; 1878c2ecf20Sopenharmony_ci if (runtime->rate != in_rate) { 1888c2ecf20Sopenharmony_ci target_rate = out_rate; 1898c2ecf20Sopenharmony_ci target_val = &_out; 1908c2ecf20Sopenharmony_ci } else if (runtime->rate != out_rate) { 1918c2ecf20Sopenharmony_ci target_rate = in_rate; 1928c2ecf20Sopenharmony_ci target_val = &_in; 1938c2ecf20Sopenharmony_ci } 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci if (target_rate) 1968c2ecf20Sopenharmony_ci __rsnd_adg_get_timesel_ratio(priv, io, 1978c2ecf20Sopenharmony_ci target_rate, 1988c2ecf20Sopenharmony_ci target_val, &_en); 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci if (in) 2018c2ecf20Sopenharmony_ci *in = _in; 2028c2ecf20Sopenharmony_ci if (out) 2038c2ecf20Sopenharmony_ci *out = _out; 2048c2ecf20Sopenharmony_ci if (en) 2058c2ecf20Sopenharmony_ci *en = _en; 2068c2ecf20Sopenharmony_ci} 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ciint rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *cmd_mod, 2098c2ecf20Sopenharmony_ci struct rsnd_dai_stream *io) 2108c2ecf20Sopenharmony_ci{ 2118c2ecf20Sopenharmony_ci struct rsnd_priv *priv = rsnd_mod_to_priv(cmd_mod); 2128c2ecf20Sopenharmony_ci struct rsnd_adg *adg = rsnd_priv_to_adg(priv); 2138c2ecf20Sopenharmony_ci struct rsnd_mod *adg_mod = rsnd_mod_get(adg); 2148c2ecf20Sopenharmony_ci int id = rsnd_mod_id(cmd_mod); 2158c2ecf20Sopenharmony_ci int shift = (id % 2) ? 16 : 0; 2168c2ecf20Sopenharmony_ci u32 mask, val; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci rsnd_adg_get_timesel_ratio(priv, io, 2198c2ecf20Sopenharmony_ci rsnd_src_get_in_rate(priv, io), 2208c2ecf20Sopenharmony_ci rsnd_src_get_out_rate(priv, io), 2218c2ecf20Sopenharmony_ci NULL, &val, NULL); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci val = val << shift; 2248c2ecf20Sopenharmony_ci mask = 0x0f1f << shift; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci rsnd_mod_bset(adg_mod, CMDOUT_TIMSEL, mask, val); 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci return 0; 2298c2ecf20Sopenharmony_ci} 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ciint rsnd_adg_set_src_timesel_gen2(struct rsnd_mod *src_mod, 2328c2ecf20Sopenharmony_ci struct rsnd_dai_stream *io, 2338c2ecf20Sopenharmony_ci unsigned int in_rate, 2348c2ecf20Sopenharmony_ci unsigned int out_rate) 2358c2ecf20Sopenharmony_ci{ 2368c2ecf20Sopenharmony_ci struct rsnd_priv *priv = rsnd_mod_to_priv(src_mod); 2378c2ecf20Sopenharmony_ci struct rsnd_adg *adg = rsnd_priv_to_adg(priv); 2388c2ecf20Sopenharmony_ci struct rsnd_mod *adg_mod = rsnd_mod_get(adg); 2398c2ecf20Sopenharmony_ci u32 in, out; 2408c2ecf20Sopenharmony_ci u32 mask, en; 2418c2ecf20Sopenharmony_ci int id = rsnd_mod_id(src_mod); 2428c2ecf20Sopenharmony_ci int shift = (id % 2) ? 16 : 0; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci rsnd_mod_confirm_src(src_mod); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci rsnd_adg_get_timesel_ratio(priv, io, 2478c2ecf20Sopenharmony_ci in_rate, out_rate, 2488c2ecf20Sopenharmony_ci &in, &out, &en); 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci in = in << shift; 2518c2ecf20Sopenharmony_ci out = out << shift; 2528c2ecf20Sopenharmony_ci mask = 0x0f1f << shift; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci rsnd_mod_bset(adg_mod, SRCIN_TIMSEL(id / 2), mask, in); 2558c2ecf20Sopenharmony_ci rsnd_mod_bset(adg_mod, SRCOUT_TIMSEL(id / 2), mask, out); 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci if (en) 2588c2ecf20Sopenharmony_ci rsnd_mod_bset(adg_mod, DIV_EN, en, en); 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci return 0; 2618c2ecf20Sopenharmony_ci} 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_cistatic void rsnd_adg_set_ssi_clk(struct rsnd_mod *ssi_mod, u32 val) 2648c2ecf20Sopenharmony_ci{ 2658c2ecf20Sopenharmony_ci struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod); 2668c2ecf20Sopenharmony_ci struct rsnd_adg *adg = rsnd_priv_to_adg(priv); 2678c2ecf20Sopenharmony_ci struct rsnd_mod *adg_mod = rsnd_mod_get(adg); 2688c2ecf20Sopenharmony_ci struct device *dev = rsnd_priv_to_dev(priv); 2698c2ecf20Sopenharmony_ci int id = rsnd_mod_id(ssi_mod); 2708c2ecf20Sopenharmony_ci int shift = (id % 4) * 8; 2718c2ecf20Sopenharmony_ci u32 mask = 0xFF << shift; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci rsnd_mod_confirm_ssi(ssi_mod); 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci val = val << shift; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci /* 2788c2ecf20Sopenharmony_ci * SSI 8 is not connected to ADG. 2798c2ecf20Sopenharmony_ci * it works with SSI 7 2808c2ecf20Sopenharmony_ci */ 2818c2ecf20Sopenharmony_ci if (id == 8) 2828c2ecf20Sopenharmony_ci return; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL(id / 4), mask, val); 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci dev_dbg(dev, "AUDIO_CLK_SEL is 0x%x\n", val); 2878c2ecf20Sopenharmony_ci} 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ciint rsnd_adg_clk_query(struct rsnd_priv *priv, unsigned int rate) 2908c2ecf20Sopenharmony_ci{ 2918c2ecf20Sopenharmony_ci struct rsnd_adg *adg = rsnd_priv_to_adg(priv); 2928c2ecf20Sopenharmony_ci int i; 2938c2ecf20Sopenharmony_ci int sel_table[] = { 2948c2ecf20Sopenharmony_ci [CLKA] = 0x1, 2958c2ecf20Sopenharmony_ci [CLKB] = 0x2, 2968c2ecf20Sopenharmony_ci [CLKC] = 0x3, 2978c2ecf20Sopenharmony_ci [CLKI] = 0x0, 2988c2ecf20Sopenharmony_ci }; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci /* 3018c2ecf20Sopenharmony_ci * find suitable clock from 3028c2ecf20Sopenharmony_ci * AUDIO_CLKA/AUDIO_CLKB/AUDIO_CLKC/AUDIO_CLKI. 3038c2ecf20Sopenharmony_ci */ 3048c2ecf20Sopenharmony_ci for (i = 0; i < CLKMAX; i++) 3058c2ecf20Sopenharmony_ci if (rate == adg->clk_rate[i]) 3068c2ecf20Sopenharmony_ci return sel_table[i]; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci /* 3098c2ecf20Sopenharmony_ci * find divided clock from BRGA/BRGB 3108c2ecf20Sopenharmony_ci */ 3118c2ecf20Sopenharmony_ci if (rate == adg->rbga_rate_for_441khz) 3128c2ecf20Sopenharmony_ci return 0x10; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci if (rate == adg->rbgb_rate_for_48khz) 3158c2ecf20Sopenharmony_ci return 0x20; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci return -EIO; 3188c2ecf20Sopenharmony_ci} 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ciint rsnd_adg_ssi_clk_stop(struct rsnd_mod *ssi_mod) 3218c2ecf20Sopenharmony_ci{ 3228c2ecf20Sopenharmony_ci rsnd_adg_set_ssi_clk(ssi_mod, 0); 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci return 0; 3258c2ecf20Sopenharmony_ci} 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ciint rsnd_adg_ssi_clk_try_start(struct rsnd_mod *ssi_mod, unsigned int rate) 3288c2ecf20Sopenharmony_ci{ 3298c2ecf20Sopenharmony_ci struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod); 3308c2ecf20Sopenharmony_ci struct rsnd_adg *adg = rsnd_priv_to_adg(priv); 3318c2ecf20Sopenharmony_ci struct device *dev = rsnd_priv_to_dev(priv); 3328c2ecf20Sopenharmony_ci struct rsnd_mod *adg_mod = rsnd_mod_get(adg); 3338c2ecf20Sopenharmony_ci int data; 3348c2ecf20Sopenharmony_ci u32 ckr = 0; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci data = rsnd_adg_clk_query(priv, rate); 3378c2ecf20Sopenharmony_ci if (data < 0) 3388c2ecf20Sopenharmony_ci return data; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci rsnd_adg_set_ssi_clk(ssi_mod, data); 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci if (rsnd_flags_has(adg, LRCLK_ASYNC)) { 3438c2ecf20Sopenharmony_ci if (rsnd_flags_has(adg, AUDIO_OUT_48)) 3448c2ecf20Sopenharmony_ci ckr = 0x80000000; 3458c2ecf20Sopenharmony_ci } else { 3468c2ecf20Sopenharmony_ci if (0 == (rate % 8000)) 3478c2ecf20Sopenharmony_ci ckr = 0x80000000; 3488c2ecf20Sopenharmony_ci } 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci rsnd_mod_bset(adg_mod, BRGCKR, 0x80770000, adg->ckr | ckr); 3518c2ecf20Sopenharmony_ci rsnd_mod_write(adg_mod, BRRA, adg->rbga); 3528c2ecf20Sopenharmony_ci rsnd_mod_write(adg_mod, BRRB, adg->rbgb); 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci dev_dbg(dev, "CLKOUT is based on BRG%c (= %dHz)\n", 3558c2ecf20Sopenharmony_ci (ckr) ? 'B' : 'A', 3568c2ecf20Sopenharmony_ci (ckr) ? adg->rbgb_rate_for_48khz : 3578c2ecf20Sopenharmony_ci adg->rbga_rate_for_441khz); 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci return 0; 3608c2ecf20Sopenharmony_ci} 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_civoid rsnd_adg_clk_control(struct rsnd_priv *priv, int enable) 3638c2ecf20Sopenharmony_ci{ 3648c2ecf20Sopenharmony_ci struct rsnd_adg *adg = rsnd_priv_to_adg(priv); 3658c2ecf20Sopenharmony_ci struct device *dev = rsnd_priv_to_dev(priv); 3668c2ecf20Sopenharmony_ci struct clk *clk; 3678c2ecf20Sopenharmony_ci int i, ret; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci for_each_rsnd_clk(clk, adg, i) { 3708c2ecf20Sopenharmony_ci ret = 0; 3718c2ecf20Sopenharmony_ci if (enable) { 3728c2ecf20Sopenharmony_ci ret = clk_prepare_enable(clk); 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci /* 3758c2ecf20Sopenharmony_ci * We shouldn't use clk_get_rate() under 3768c2ecf20Sopenharmony_ci * atomic context. Let's keep it when 3778c2ecf20Sopenharmony_ci * rsnd_adg_clk_enable() was called 3788c2ecf20Sopenharmony_ci */ 3798c2ecf20Sopenharmony_ci adg->clk_rate[i] = clk_get_rate(adg->clk[i]); 3808c2ecf20Sopenharmony_ci } else { 3818c2ecf20Sopenharmony_ci clk_disable_unprepare(clk); 3828c2ecf20Sopenharmony_ci } 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci if (ret < 0) 3858c2ecf20Sopenharmony_ci dev_warn(dev, "can't use clk %d\n", i); 3868c2ecf20Sopenharmony_ci } 3878c2ecf20Sopenharmony_ci} 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_cistatic void rsnd_adg_get_clkin(struct rsnd_priv *priv, 3908c2ecf20Sopenharmony_ci struct rsnd_adg *adg) 3918c2ecf20Sopenharmony_ci{ 3928c2ecf20Sopenharmony_ci struct device *dev = rsnd_priv_to_dev(priv); 3938c2ecf20Sopenharmony_ci struct clk *clk; 3948c2ecf20Sopenharmony_ci int i; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci for (i = 0; i < CLKMAX; i++) { 3978c2ecf20Sopenharmony_ci clk = devm_clk_get(dev, clk_name[i]); 3988c2ecf20Sopenharmony_ci adg->clk[i] = IS_ERR(clk) ? NULL : clk; 3998c2ecf20Sopenharmony_ci } 4008c2ecf20Sopenharmony_ci} 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_cistatic void rsnd_adg_get_clkout(struct rsnd_priv *priv, 4038c2ecf20Sopenharmony_ci struct rsnd_adg *adg) 4048c2ecf20Sopenharmony_ci{ 4058c2ecf20Sopenharmony_ci struct clk *clk; 4068c2ecf20Sopenharmony_ci struct device *dev = rsnd_priv_to_dev(priv); 4078c2ecf20Sopenharmony_ci struct device_node *np = dev->of_node; 4088c2ecf20Sopenharmony_ci struct property *prop; 4098c2ecf20Sopenharmony_ci u32 ckr, rbgx, rbga, rbgb; 4108c2ecf20Sopenharmony_ci u32 rate, div; 4118c2ecf20Sopenharmony_ci#define REQ_SIZE 2 4128c2ecf20Sopenharmony_ci u32 req_rate[REQ_SIZE] = {}; 4138c2ecf20Sopenharmony_ci uint32_t count = 0; 4148c2ecf20Sopenharmony_ci unsigned long req_48kHz_rate, req_441kHz_rate; 4158c2ecf20Sopenharmony_ci int i, req_size; 4168c2ecf20Sopenharmony_ci const char *parent_clk_name = NULL; 4178c2ecf20Sopenharmony_ci static const char * const clkout_name[] = { 4188c2ecf20Sopenharmony_ci [CLKOUT] = "audio_clkout", 4198c2ecf20Sopenharmony_ci [CLKOUT1] = "audio_clkout1", 4208c2ecf20Sopenharmony_ci [CLKOUT2] = "audio_clkout2", 4218c2ecf20Sopenharmony_ci [CLKOUT3] = "audio_clkout3", 4228c2ecf20Sopenharmony_ci }; 4238c2ecf20Sopenharmony_ci int brg_table[] = { 4248c2ecf20Sopenharmony_ci [CLKA] = 0x0, 4258c2ecf20Sopenharmony_ci [CLKB] = 0x1, 4268c2ecf20Sopenharmony_ci [CLKC] = 0x4, 4278c2ecf20Sopenharmony_ci [CLKI] = 0x2, 4288c2ecf20Sopenharmony_ci }; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci ckr = 0; 4318c2ecf20Sopenharmony_ci rbga = 2; /* default 1/6 */ 4328c2ecf20Sopenharmony_ci rbgb = 2; /* default 1/6 */ 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci /* 4358c2ecf20Sopenharmony_ci * ADG supports BRRA/BRRB output only 4368c2ecf20Sopenharmony_ci * this means all clkout0/1/2/3 will be same rate 4378c2ecf20Sopenharmony_ci */ 4388c2ecf20Sopenharmony_ci prop = of_find_property(np, "clock-frequency", NULL); 4398c2ecf20Sopenharmony_ci if (!prop) 4408c2ecf20Sopenharmony_ci goto rsnd_adg_get_clkout_end; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci req_size = prop->length / sizeof(u32); 4438c2ecf20Sopenharmony_ci if (req_size > REQ_SIZE) { 4448c2ecf20Sopenharmony_ci dev_err(dev, 4458c2ecf20Sopenharmony_ci "too many clock-frequency, use top %d\n", REQ_SIZE); 4468c2ecf20Sopenharmony_ci req_size = REQ_SIZE; 4478c2ecf20Sopenharmony_ci } 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci of_property_read_u32_array(np, "clock-frequency", req_rate, req_size); 4508c2ecf20Sopenharmony_ci req_48kHz_rate = 0; 4518c2ecf20Sopenharmony_ci req_441kHz_rate = 0; 4528c2ecf20Sopenharmony_ci for (i = 0; i < req_size; i++) { 4538c2ecf20Sopenharmony_ci if (0 == (req_rate[i] % 44100)) 4548c2ecf20Sopenharmony_ci req_441kHz_rate = req_rate[i]; 4558c2ecf20Sopenharmony_ci if (0 == (req_rate[i] % 48000)) 4568c2ecf20Sopenharmony_ci req_48kHz_rate = req_rate[i]; 4578c2ecf20Sopenharmony_ci } 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci if (req_rate[0] % 48000 == 0) 4608c2ecf20Sopenharmony_ci rsnd_flags_set(adg, AUDIO_OUT_48); 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci if (of_get_property(np, "clkout-lr-asynchronous", NULL)) 4638c2ecf20Sopenharmony_ci rsnd_flags_set(adg, LRCLK_ASYNC); 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci /* 4668c2ecf20Sopenharmony_ci * This driver is assuming that AUDIO_CLKA/AUDIO_CLKB/AUDIO_CLKC 4678c2ecf20Sopenharmony_ci * have 44.1kHz or 48kHz base clocks for now. 4688c2ecf20Sopenharmony_ci * 4698c2ecf20Sopenharmony_ci * SSI itself can divide parent clock by 1/1 - 1/16 4708c2ecf20Sopenharmony_ci * see 4718c2ecf20Sopenharmony_ci * rsnd_adg_ssi_clk_try_start() 4728c2ecf20Sopenharmony_ci * rsnd_ssi_master_clk_start() 4738c2ecf20Sopenharmony_ci */ 4748c2ecf20Sopenharmony_ci adg->rbga_rate_for_441khz = 0; 4758c2ecf20Sopenharmony_ci adg->rbgb_rate_for_48khz = 0; 4768c2ecf20Sopenharmony_ci for_each_rsnd_clk(clk, adg, i) { 4778c2ecf20Sopenharmony_ci rate = clk_get_rate(clk); 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci if (0 == rate) /* not used */ 4808c2ecf20Sopenharmony_ci continue; 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci /* RBGA */ 4838c2ecf20Sopenharmony_ci if (!adg->rbga_rate_for_441khz && (0 == rate % 44100)) { 4848c2ecf20Sopenharmony_ci div = 6; 4858c2ecf20Sopenharmony_ci if (req_441kHz_rate) 4868c2ecf20Sopenharmony_ci div = rate / req_441kHz_rate; 4878c2ecf20Sopenharmony_ci rbgx = rsnd_adg_calculate_rbgx(div); 4888c2ecf20Sopenharmony_ci if (BRRx_MASK(rbgx) == rbgx) { 4898c2ecf20Sopenharmony_ci rbga = rbgx; 4908c2ecf20Sopenharmony_ci adg->rbga_rate_for_441khz = rate / div; 4918c2ecf20Sopenharmony_ci ckr |= brg_table[i] << 20; 4928c2ecf20Sopenharmony_ci if (req_441kHz_rate && 4938c2ecf20Sopenharmony_ci !rsnd_flags_has(adg, AUDIO_OUT_48)) 4948c2ecf20Sopenharmony_ci parent_clk_name = __clk_get_name(clk); 4958c2ecf20Sopenharmony_ci } 4968c2ecf20Sopenharmony_ci } 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci /* RBGB */ 4998c2ecf20Sopenharmony_ci if (!adg->rbgb_rate_for_48khz && (0 == rate % 48000)) { 5008c2ecf20Sopenharmony_ci div = 6; 5018c2ecf20Sopenharmony_ci if (req_48kHz_rate) 5028c2ecf20Sopenharmony_ci div = rate / req_48kHz_rate; 5038c2ecf20Sopenharmony_ci rbgx = rsnd_adg_calculate_rbgx(div); 5048c2ecf20Sopenharmony_ci if (BRRx_MASK(rbgx) == rbgx) { 5058c2ecf20Sopenharmony_ci rbgb = rbgx; 5068c2ecf20Sopenharmony_ci adg->rbgb_rate_for_48khz = rate / div; 5078c2ecf20Sopenharmony_ci ckr |= brg_table[i] << 16; 5088c2ecf20Sopenharmony_ci if (req_48kHz_rate && 5098c2ecf20Sopenharmony_ci rsnd_flags_has(adg, AUDIO_OUT_48)) 5108c2ecf20Sopenharmony_ci parent_clk_name = __clk_get_name(clk); 5118c2ecf20Sopenharmony_ci } 5128c2ecf20Sopenharmony_ci } 5138c2ecf20Sopenharmony_ci } 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci /* 5168c2ecf20Sopenharmony_ci * ADG supports BRRA/BRRB output only. 5178c2ecf20Sopenharmony_ci * this means all clkout0/1/2/3 will be * same rate 5188c2ecf20Sopenharmony_ci */ 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci of_property_read_u32(np, "#clock-cells", &count); 5218c2ecf20Sopenharmony_ci /* 5228c2ecf20Sopenharmony_ci * for clkout 5238c2ecf20Sopenharmony_ci */ 5248c2ecf20Sopenharmony_ci if (!count) { 5258c2ecf20Sopenharmony_ci clk = clk_register_fixed_rate(dev, clkout_name[CLKOUT], 5268c2ecf20Sopenharmony_ci parent_clk_name, 0, req_rate[0]); 5278c2ecf20Sopenharmony_ci if (!IS_ERR(clk)) { 5288c2ecf20Sopenharmony_ci adg->clkout[CLKOUT] = clk; 5298c2ecf20Sopenharmony_ci of_clk_add_provider(np, of_clk_src_simple_get, clk); 5308c2ecf20Sopenharmony_ci } 5318c2ecf20Sopenharmony_ci } 5328c2ecf20Sopenharmony_ci /* 5338c2ecf20Sopenharmony_ci * for clkout0/1/2/3 5348c2ecf20Sopenharmony_ci */ 5358c2ecf20Sopenharmony_ci else { 5368c2ecf20Sopenharmony_ci for (i = 0; i < CLKOUTMAX; i++) { 5378c2ecf20Sopenharmony_ci clk = clk_register_fixed_rate(dev, clkout_name[i], 5388c2ecf20Sopenharmony_ci parent_clk_name, 0, 5398c2ecf20Sopenharmony_ci req_rate[0]); 5408c2ecf20Sopenharmony_ci if (!IS_ERR(clk)) 5418c2ecf20Sopenharmony_ci adg->clkout[i] = clk; 5428c2ecf20Sopenharmony_ci } 5438c2ecf20Sopenharmony_ci adg->onecell.clks = adg->clkout; 5448c2ecf20Sopenharmony_ci adg->onecell.clk_num = CLKOUTMAX; 5458c2ecf20Sopenharmony_ci of_clk_add_provider(np, of_clk_src_onecell_get, 5468c2ecf20Sopenharmony_ci &adg->onecell); 5478c2ecf20Sopenharmony_ci } 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_cirsnd_adg_get_clkout_end: 5508c2ecf20Sopenharmony_ci adg->ckr = ckr; 5518c2ecf20Sopenharmony_ci adg->rbga = rbga; 5528c2ecf20Sopenharmony_ci adg->rbgb = rbgb; 5538c2ecf20Sopenharmony_ci} 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci#ifdef DEBUG 5568c2ecf20Sopenharmony_cistatic void rsnd_adg_clk_dbg_info(struct rsnd_priv *priv, struct rsnd_adg *adg) 5578c2ecf20Sopenharmony_ci{ 5588c2ecf20Sopenharmony_ci struct device *dev = rsnd_priv_to_dev(priv); 5598c2ecf20Sopenharmony_ci struct clk *clk; 5608c2ecf20Sopenharmony_ci int i; 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci for_each_rsnd_clk(clk, adg, i) 5638c2ecf20Sopenharmony_ci dev_dbg(dev, "%s : %pa : %ld\n", 5648c2ecf20Sopenharmony_ci clk_name[i], clk, clk_get_rate(clk)); 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci dev_dbg(dev, "BRGCKR = 0x%08x, BRRA/BRRB = 0x%x/0x%x\n", 5678c2ecf20Sopenharmony_ci adg->ckr, adg->rbga, adg->rbgb); 5688c2ecf20Sopenharmony_ci dev_dbg(dev, "BRGA (for 44100 base) = %d\n", adg->rbga_rate_for_441khz); 5698c2ecf20Sopenharmony_ci dev_dbg(dev, "BRGB (for 48000 base) = %d\n", adg->rbgb_rate_for_48khz); 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci /* 5728c2ecf20Sopenharmony_ci * Actual CLKOUT will be exchanged in rsnd_adg_ssi_clk_try_start() 5738c2ecf20Sopenharmony_ci * by BRGCKR::BRGCKR_31 5748c2ecf20Sopenharmony_ci */ 5758c2ecf20Sopenharmony_ci for_each_rsnd_clkout(clk, adg, i) 5768c2ecf20Sopenharmony_ci dev_dbg(dev, "clkout %d : %pa : %ld\n", i, 5778c2ecf20Sopenharmony_ci clk, clk_get_rate(clk)); 5788c2ecf20Sopenharmony_ci} 5798c2ecf20Sopenharmony_ci#else 5808c2ecf20Sopenharmony_ci#define rsnd_adg_clk_dbg_info(priv, adg) 5818c2ecf20Sopenharmony_ci#endif 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ciint rsnd_adg_probe(struct rsnd_priv *priv) 5848c2ecf20Sopenharmony_ci{ 5858c2ecf20Sopenharmony_ci struct rsnd_adg *adg; 5868c2ecf20Sopenharmony_ci struct device *dev = rsnd_priv_to_dev(priv); 5878c2ecf20Sopenharmony_ci int ret; 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci adg = devm_kzalloc(dev, sizeof(*adg), GFP_KERNEL); 5908c2ecf20Sopenharmony_ci if (!adg) 5918c2ecf20Sopenharmony_ci return -ENOMEM; 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci ret = rsnd_mod_init(priv, &adg->mod, &adg_ops, 5948c2ecf20Sopenharmony_ci NULL, 0, 0); 5958c2ecf20Sopenharmony_ci if (ret) 5968c2ecf20Sopenharmony_ci return ret; 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci rsnd_adg_get_clkin(priv, adg); 5998c2ecf20Sopenharmony_ci rsnd_adg_get_clkout(priv, adg); 6008c2ecf20Sopenharmony_ci rsnd_adg_clk_dbg_info(priv, adg); 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci priv->adg = adg; 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci rsnd_adg_clk_enable(priv); 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci return 0; 6078c2ecf20Sopenharmony_ci} 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_civoid rsnd_adg_remove(struct rsnd_priv *priv) 6108c2ecf20Sopenharmony_ci{ 6118c2ecf20Sopenharmony_ci struct device *dev = rsnd_priv_to_dev(priv); 6128c2ecf20Sopenharmony_ci struct device_node *np = dev->of_node; 6138c2ecf20Sopenharmony_ci struct rsnd_adg *adg = priv->adg; 6148c2ecf20Sopenharmony_ci struct clk *clk; 6158c2ecf20Sopenharmony_ci int i; 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci for_each_rsnd_clkout(clk, adg, i) 6188c2ecf20Sopenharmony_ci if (adg->clkout[i]) 6198c2ecf20Sopenharmony_ci clk_unregister_fixed_rate(adg->clkout[i]); 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci of_clk_del_provider(np); 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci rsnd_adg_clk_disable(priv); 6248c2ecf20Sopenharmony_ci} 625