162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// Renesas R-Car SRC support 462306a36Sopenharmony_ci// 562306a36Sopenharmony_ci// Copyright (C) 2013 Renesas Solutions Corp. 662306a36Sopenharmony_ci// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci/* 962306a36Sopenharmony_ci * You can use Synchronous Sampling Rate Convert (if no DVC) 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * amixer set "SRC Out Rate" on 1262306a36Sopenharmony_ci * aplay xxx.wav & 1362306a36Sopenharmony_ci * amixer set "SRC Out Rate" 96000 // convert rate to 96000Hz 1462306a36Sopenharmony_ci * amixer set "SRC Out Rate" 22050 // convert rate to 22050Hz 1562306a36Sopenharmony_ci */ 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci/* 1862306a36Sopenharmony_ci * you can enable below define if you don't need 1962306a36Sopenharmony_ci * SSI interrupt status debug message when debugging 2062306a36Sopenharmony_ci * see rsnd_print_irq_status() 2162306a36Sopenharmony_ci * 2262306a36Sopenharmony_ci * #define RSND_DEBUG_NO_IRQ_STATUS 1 2362306a36Sopenharmony_ci */ 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include "rsnd.h" 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#define SRC_NAME "src" 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci/* SCU_SYSTEM_STATUS0/1 */ 3062306a36Sopenharmony_ci#define OUF_SRC(id) ((1 << (id + 16)) | (1 << id)) 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistruct rsnd_src { 3362306a36Sopenharmony_ci struct rsnd_mod mod; 3462306a36Sopenharmony_ci struct rsnd_mod *dma; 3562306a36Sopenharmony_ci struct rsnd_kctrl_cfg_s sen; /* sync convert enable */ 3662306a36Sopenharmony_ci struct rsnd_kctrl_cfg_s sync; /* sync convert */ 3762306a36Sopenharmony_ci int irq; 3862306a36Sopenharmony_ci}; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci#define RSND_SRC_NAME_SIZE 16 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci#define rsnd_src_get(priv, id) ((struct rsnd_src *)(priv->src) + id) 4362306a36Sopenharmony_ci#define rsnd_src_nr(priv) ((priv)->src_nr) 4462306a36Sopenharmony_ci#define rsnd_src_sync_is_enabled(mod) (rsnd_mod_to_src(mod)->sen.val) 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci#define rsnd_mod_to_src(_mod) \ 4762306a36Sopenharmony_ci container_of((_mod), struct rsnd_src, mod) 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci#define for_each_rsnd_src(pos, priv, i) \ 5062306a36Sopenharmony_ci for ((i) = 0; \ 5162306a36Sopenharmony_ci ((i) < rsnd_src_nr(priv)) && \ 5262306a36Sopenharmony_ci ((pos) = (struct rsnd_src *)(priv)->src + i); \ 5362306a36Sopenharmony_ci i++) 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci/* 5762306a36Sopenharmony_ci * image of SRC (Sampling Rate Converter) 5862306a36Sopenharmony_ci * 5962306a36Sopenharmony_ci * 96kHz <-> +-----+ 48kHz +-----+ 48kHz +-------+ 6062306a36Sopenharmony_ci * 48kHz <-> | SRC | <------> | SSI | <-----> | codec | 6162306a36Sopenharmony_ci * 44.1kHz <-> +-----+ +-----+ +-------+ 6262306a36Sopenharmony_ci * ... 6362306a36Sopenharmony_ci * 6462306a36Sopenharmony_ci */ 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistatic void rsnd_src_activation(struct rsnd_mod *mod) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci rsnd_mod_write(mod, SRC_SWRSR, 0); 6962306a36Sopenharmony_ci rsnd_mod_write(mod, SRC_SWRSR, 1); 7062306a36Sopenharmony_ci} 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistatic void rsnd_src_halt(struct rsnd_mod *mod) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci rsnd_mod_write(mod, SRC_SRCIR, 1); 7562306a36Sopenharmony_ci rsnd_mod_write(mod, SRC_SWRSR, 0); 7662306a36Sopenharmony_ci} 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cistatic struct dma_chan *rsnd_src_dma_req(struct rsnd_dai_stream *io, 7962306a36Sopenharmony_ci struct rsnd_mod *mod) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci struct rsnd_priv *priv = rsnd_mod_to_priv(mod); 8262306a36Sopenharmony_ci int is_play = rsnd_io_is_play(io); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci return rsnd_dma_request_channel(rsnd_src_of_node(priv), 8562306a36Sopenharmony_ci SRC_NAME, mod, 8662306a36Sopenharmony_ci is_play ? "rx" : "tx"); 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic u32 rsnd_src_convert_rate(struct rsnd_dai_stream *io, 9062306a36Sopenharmony_ci struct rsnd_mod *mod) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); 9362306a36Sopenharmony_ci struct rsnd_src *src = rsnd_mod_to_src(mod); 9462306a36Sopenharmony_ci u32 convert_rate; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci if (!runtime) 9762306a36Sopenharmony_ci return 0; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci if (!rsnd_src_sync_is_enabled(mod)) 10062306a36Sopenharmony_ci return rsnd_io_converted_rate(io); 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci convert_rate = src->sync.val; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci if (!convert_rate) 10562306a36Sopenharmony_ci convert_rate = rsnd_io_converted_rate(io); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci if (!convert_rate) 10862306a36Sopenharmony_ci convert_rate = runtime->rate; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci return convert_rate; 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ciunsigned int rsnd_src_get_rate(struct rsnd_priv *priv, 11462306a36Sopenharmony_ci struct rsnd_dai_stream *io, 11562306a36Sopenharmony_ci int is_in) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci struct rsnd_mod *src_mod = rsnd_io_to_mod_src(io); 11862306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); 11962306a36Sopenharmony_ci unsigned int rate = 0; 12062306a36Sopenharmony_ci int is_play = rsnd_io_is_play(io); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci /* 12362306a36Sopenharmony_ci * Playback 12462306a36Sopenharmony_ci * runtime_rate -> [SRC] -> convert_rate 12562306a36Sopenharmony_ci * 12662306a36Sopenharmony_ci * Capture 12762306a36Sopenharmony_ci * convert_rate -> [SRC] -> runtime_rate 12862306a36Sopenharmony_ci */ 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci if (is_play == is_in) 13162306a36Sopenharmony_ci return runtime->rate; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci /* 13462306a36Sopenharmony_ci * return convert rate if SRC is used, 13562306a36Sopenharmony_ci * otherwise, return runtime->rate as usual 13662306a36Sopenharmony_ci */ 13762306a36Sopenharmony_ci if (src_mod) 13862306a36Sopenharmony_ci rate = rsnd_src_convert_rate(io, src_mod); 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci if (!rate) 14162306a36Sopenharmony_ci rate = runtime->rate; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci return rate; 14462306a36Sopenharmony_ci} 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cistatic const u32 bsdsr_table_pattern1[] = { 14762306a36Sopenharmony_ci 0x01800000, /* 6 - 1/6 */ 14862306a36Sopenharmony_ci 0x01000000, /* 6 - 1/4 */ 14962306a36Sopenharmony_ci 0x00c00000, /* 6 - 1/3 */ 15062306a36Sopenharmony_ci 0x00800000, /* 6 - 1/2 */ 15162306a36Sopenharmony_ci 0x00600000, /* 6 - 2/3 */ 15262306a36Sopenharmony_ci 0x00400000, /* 6 - 1 */ 15362306a36Sopenharmony_ci}; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_cistatic const u32 bsdsr_table_pattern2[] = { 15662306a36Sopenharmony_ci 0x02400000, /* 6 - 1/6 */ 15762306a36Sopenharmony_ci 0x01800000, /* 6 - 1/4 */ 15862306a36Sopenharmony_ci 0x01200000, /* 6 - 1/3 */ 15962306a36Sopenharmony_ci 0x00c00000, /* 6 - 1/2 */ 16062306a36Sopenharmony_ci 0x00900000, /* 6 - 2/3 */ 16162306a36Sopenharmony_ci 0x00600000, /* 6 - 1 */ 16262306a36Sopenharmony_ci}; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_cistatic const u32 bsisr_table[] = { 16562306a36Sopenharmony_ci 0x00100060, /* 6 - 1/6 */ 16662306a36Sopenharmony_ci 0x00100040, /* 6 - 1/4 */ 16762306a36Sopenharmony_ci 0x00100030, /* 6 - 1/3 */ 16862306a36Sopenharmony_ci 0x00100020, /* 6 - 1/2 */ 16962306a36Sopenharmony_ci 0x00100020, /* 6 - 2/3 */ 17062306a36Sopenharmony_ci 0x00100020, /* 6 - 1 */ 17162306a36Sopenharmony_ci}; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_cistatic const u32 chan288888[] = { 17462306a36Sopenharmony_ci 0x00000006, /* 1 to 2 */ 17562306a36Sopenharmony_ci 0x000001fe, /* 1 to 8 */ 17662306a36Sopenharmony_ci 0x000001fe, /* 1 to 8 */ 17762306a36Sopenharmony_ci 0x000001fe, /* 1 to 8 */ 17862306a36Sopenharmony_ci 0x000001fe, /* 1 to 8 */ 17962306a36Sopenharmony_ci 0x000001fe, /* 1 to 8 */ 18062306a36Sopenharmony_ci}; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic const u32 chan244888[] = { 18362306a36Sopenharmony_ci 0x00000006, /* 1 to 2 */ 18462306a36Sopenharmony_ci 0x0000001e, /* 1 to 4 */ 18562306a36Sopenharmony_ci 0x0000001e, /* 1 to 4 */ 18662306a36Sopenharmony_ci 0x000001fe, /* 1 to 8 */ 18762306a36Sopenharmony_ci 0x000001fe, /* 1 to 8 */ 18862306a36Sopenharmony_ci 0x000001fe, /* 1 to 8 */ 18962306a36Sopenharmony_ci}; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_cistatic const u32 chan222222[] = { 19262306a36Sopenharmony_ci 0x00000006, /* 1 to 2 */ 19362306a36Sopenharmony_ci 0x00000006, /* 1 to 2 */ 19462306a36Sopenharmony_ci 0x00000006, /* 1 to 2 */ 19562306a36Sopenharmony_ci 0x00000006, /* 1 to 2 */ 19662306a36Sopenharmony_ci 0x00000006, /* 1 to 2 */ 19762306a36Sopenharmony_ci 0x00000006, /* 1 to 2 */ 19862306a36Sopenharmony_ci}; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_cistatic void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io, 20162306a36Sopenharmony_ci struct rsnd_mod *mod) 20262306a36Sopenharmony_ci{ 20362306a36Sopenharmony_ci struct rsnd_priv *priv = rsnd_mod_to_priv(mod); 20462306a36Sopenharmony_ci struct device *dev = rsnd_priv_to_dev(priv); 20562306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); 20662306a36Sopenharmony_ci int is_play = rsnd_io_is_play(io); 20762306a36Sopenharmony_ci int use_src = 0; 20862306a36Sopenharmony_ci u32 fin, fout; 20962306a36Sopenharmony_ci u32 ifscr, fsrate, adinr; 21062306a36Sopenharmony_ci u32 cr, route; 21162306a36Sopenharmony_ci u32 i_busif, o_busif, tmp; 21262306a36Sopenharmony_ci const u32 *bsdsr_table; 21362306a36Sopenharmony_ci const u32 *chptn; 21462306a36Sopenharmony_ci uint ratio; 21562306a36Sopenharmony_ci int chan; 21662306a36Sopenharmony_ci int idx; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci if (!runtime) 21962306a36Sopenharmony_ci return; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci fin = rsnd_src_get_in_rate(priv, io); 22262306a36Sopenharmony_ci fout = rsnd_src_get_out_rate(priv, io); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci chan = rsnd_runtime_channel_original(io); 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci /* 6 - 1/6 are very enough ratio for SRC_BSDSR */ 22762306a36Sopenharmony_ci if (fin == fout) 22862306a36Sopenharmony_ci ratio = 0; 22962306a36Sopenharmony_ci else if (fin > fout) 23062306a36Sopenharmony_ci ratio = 100 * fin / fout; 23162306a36Sopenharmony_ci else 23262306a36Sopenharmony_ci ratio = 100 * fout / fin; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci if (ratio > 600) { 23562306a36Sopenharmony_ci dev_err(dev, "FSO/FSI ratio error\n"); 23662306a36Sopenharmony_ci return; 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci use_src = (fin != fout) | rsnd_src_sync_is_enabled(mod); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci /* 24262306a36Sopenharmony_ci * SRC_ADINR 24362306a36Sopenharmony_ci */ 24462306a36Sopenharmony_ci adinr = rsnd_get_adinr_bit(mod, io) | chan; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci /* 24762306a36Sopenharmony_ci * SRC_IFSCR / SRC_IFSVR 24862306a36Sopenharmony_ci */ 24962306a36Sopenharmony_ci ifscr = 0; 25062306a36Sopenharmony_ci fsrate = 0; 25162306a36Sopenharmony_ci if (use_src) { 25262306a36Sopenharmony_ci u64 n; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci ifscr = 1; 25562306a36Sopenharmony_ci n = (u64)0x0400000 * fin; 25662306a36Sopenharmony_ci do_div(n, fout); 25762306a36Sopenharmony_ci fsrate = n; 25862306a36Sopenharmony_ci } 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci /* 26162306a36Sopenharmony_ci * SRC_SRCCR / SRC_ROUTE_MODE0 26262306a36Sopenharmony_ci */ 26362306a36Sopenharmony_ci cr = 0x00011110; 26462306a36Sopenharmony_ci route = 0x0; 26562306a36Sopenharmony_ci if (use_src) { 26662306a36Sopenharmony_ci route = 0x1; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci if (rsnd_src_sync_is_enabled(mod)) { 26962306a36Sopenharmony_ci cr |= 0x1; 27062306a36Sopenharmony_ci route |= rsnd_io_is_play(io) ? 27162306a36Sopenharmony_ci (0x1 << 24) : (0x1 << 25); 27262306a36Sopenharmony_ci } 27362306a36Sopenharmony_ci } 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci /* 27662306a36Sopenharmony_ci * SRC_BSDSR / SRC_BSISR 27762306a36Sopenharmony_ci * 27862306a36Sopenharmony_ci * see 27962306a36Sopenharmony_ci * Combination of Register Setting Related to 28062306a36Sopenharmony_ci * FSO/FSI Ratio and Channel, Latency 28162306a36Sopenharmony_ci */ 28262306a36Sopenharmony_ci switch (rsnd_mod_id(mod)) { 28362306a36Sopenharmony_ci case 0: 28462306a36Sopenharmony_ci chptn = chan288888; 28562306a36Sopenharmony_ci bsdsr_table = bsdsr_table_pattern1; 28662306a36Sopenharmony_ci break; 28762306a36Sopenharmony_ci case 1: 28862306a36Sopenharmony_ci case 3: 28962306a36Sopenharmony_ci case 4: 29062306a36Sopenharmony_ci chptn = chan244888; 29162306a36Sopenharmony_ci bsdsr_table = bsdsr_table_pattern1; 29262306a36Sopenharmony_ci break; 29362306a36Sopenharmony_ci case 2: 29462306a36Sopenharmony_ci case 9: 29562306a36Sopenharmony_ci chptn = chan222222; 29662306a36Sopenharmony_ci bsdsr_table = bsdsr_table_pattern1; 29762306a36Sopenharmony_ci break; 29862306a36Sopenharmony_ci case 5: 29962306a36Sopenharmony_ci case 6: 30062306a36Sopenharmony_ci case 7: 30162306a36Sopenharmony_ci case 8: 30262306a36Sopenharmony_ci chptn = chan222222; 30362306a36Sopenharmony_ci bsdsr_table = bsdsr_table_pattern2; 30462306a36Sopenharmony_ci break; 30562306a36Sopenharmony_ci default: 30662306a36Sopenharmony_ci goto convert_rate_err; 30762306a36Sopenharmony_ci } 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci /* 31062306a36Sopenharmony_ci * E3 need to overwrite 31162306a36Sopenharmony_ci */ 31262306a36Sopenharmony_ci if (rsnd_is_e3(priv)) 31362306a36Sopenharmony_ci switch (rsnd_mod_id(mod)) { 31462306a36Sopenharmony_ci case 0: 31562306a36Sopenharmony_ci case 4: 31662306a36Sopenharmony_ci chptn = chan222222; 31762306a36Sopenharmony_ci } 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(chan222222); idx++) 32062306a36Sopenharmony_ci if (chptn[idx] & (1 << chan)) 32162306a36Sopenharmony_ci break; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci if (chan > 8 || 32462306a36Sopenharmony_ci idx >= ARRAY_SIZE(chan222222)) 32562306a36Sopenharmony_ci goto convert_rate_err; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci /* BUSIF_MODE */ 32862306a36Sopenharmony_ci tmp = rsnd_get_busif_shift(io, mod); 32962306a36Sopenharmony_ci i_busif = ( is_play ? tmp : 0) | 1; 33062306a36Sopenharmony_ci o_busif = (!is_play ? tmp : 0) | 1; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci rsnd_mod_write(mod, SRC_ROUTE_MODE0, route); 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci rsnd_mod_write(mod, SRC_SRCIR, 1); /* initialize */ 33562306a36Sopenharmony_ci rsnd_mod_write(mod, SRC_ADINR, adinr); 33662306a36Sopenharmony_ci rsnd_mod_write(mod, SRC_IFSCR, ifscr); 33762306a36Sopenharmony_ci rsnd_mod_write(mod, SRC_IFSVR, fsrate); 33862306a36Sopenharmony_ci rsnd_mod_write(mod, SRC_SRCCR, cr); 33962306a36Sopenharmony_ci rsnd_mod_write(mod, SRC_BSDSR, bsdsr_table[idx]); 34062306a36Sopenharmony_ci rsnd_mod_write(mod, SRC_BSISR, bsisr_table[idx]); 34162306a36Sopenharmony_ci rsnd_mod_write(mod, SRC_SRCIR, 0); /* cancel initialize */ 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci rsnd_mod_write(mod, SRC_I_BUSIF_MODE, i_busif); 34462306a36Sopenharmony_ci rsnd_mod_write(mod, SRC_O_BUSIF_MODE, o_busif); 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci rsnd_mod_write(mod, SRC_BUSIF_DALIGN, rsnd_get_dalign(mod, io)); 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci rsnd_adg_set_src_timesel_gen2(mod, io, fin, fout); 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci return; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ciconvert_rate_err: 35362306a36Sopenharmony_ci dev_err(dev, "unknown BSDSR/BSDIR settings\n"); 35462306a36Sopenharmony_ci} 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_cistatic int rsnd_src_irq(struct rsnd_mod *mod, 35762306a36Sopenharmony_ci struct rsnd_dai_stream *io, 35862306a36Sopenharmony_ci struct rsnd_priv *priv, 35962306a36Sopenharmony_ci int enable) 36062306a36Sopenharmony_ci{ 36162306a36Sopenharmony_ci struct rsnd_src *src = rsnd_mod_to_src(mod); 36262306a36Sopenharmony_ci u32 sys_int_val, int_val, sys_int_mask; 36362306a36Sopenharmony_ci int irq = src->irq; 36462306a36Sopenharmony_ci int id = rsnd_mod_id(mod); 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci sys_int_val = 36762306a36Sopenharmony_ci sys_int_mask = OUF_SRC(id); 36862306a36Sopenharmony_ci int_val = 0x3300; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci /* 37162306a36Sopenharmony_ci * IRQ is not supported on non-DT 37262306a36Sopenharmony_ci * see 37362306a36Sopenharmony_ci * rsnd_src_probe_() 37462306a36Sopenharmony_ci */ 37562306a36Sopenharmony_ci if ((irq <= 0) || !enable) { 37662306a36Sopenharmony_ci sys_int_val = 0; 37762306a36Sopenharmony_ci int_val = 0; 37862306a36Sopenharmony_ci } 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci /* 38162306a36Sopenharmony_ci * WORKAROUND 38262306a36Sopenharmony_ci * 38362306a36Sopenharmony_ci * ignore over flow error when rsnd_src_sync_is_enabled() 38462306a36Sopenharmony_ci */ 38562306a36Sopenharmony_ci if (rsnd_src_sync_is_enabled(mod)) 38662306a36Sopenharmony_ci sys_int_val = sys_int_val & 0xffff; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci rsnd_mod_write(mod, SRC_INT_ENABLE0, int_val); 38962306a36Sopenharmony_ci rsnd_mod_bset(mod, SCU_SYS_INT_EN0, sys_int_mask, sys_int_val); 39062306a36Sopenharmony_ci rsnd_mod_bset(mod, SCU_SYS_INT_EN1, sys_int_mask, sys_int_val); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci return 0; 39362306a36Sopenharmony_ci} 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_cistatic void rsnd_src_status_clear(struct rsnd_mod *mod) 39662306a36Sopenharmony_ci{ 39762306a36Sopenharmony_ci u32 val = OUF_SRC(rsnd_mod_id(mod)); 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci rsnd_mod_write(mod, SCU_SYS_STATUS0, val); 40062306a36Sopenharmony_ci rsnd_mod_write(mod, SCU_SYS_STATUS1, val); 40162306a36Sopenharmony_ci} 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_cistatic bool rsnd_src_error_occurred(struct rsnd_mod *mod) 40462306a36Sopenharmony_ci{ 40562306a36Sopenharmony_ci struct rsnd_priv *priv = rsnd_mod_to_priv(mod); 40662306a36Sopenharmony_ci struct device *dev = rsnd_priv_to_dev(priv); 40762306a36Sopenharmony_ci u32 val0, val1; 40862306a36Sopenharmony_ci u32 status0, status1; 40962306a36Sopenharmony_ci bool ret = false; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci val0 = val1 = OUF_SRC(rsnd_mod_id(mod)); 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci /* 41462306a36Sopenharmony_ci * WORKAROUND 41562306a36Sopenharmony_ci * 41662306a36Sopenharmony_ci * ignore over flow error when rsnd_src_sync_is_enabled() 41762306a36Sopenharmony_ci */ 41862306a36Sopenharmony_ci if (rsnd_src_sync_is_enabled(mod)) 41962306a36Sopenharmony_ci val0 = val0 & 0xffff; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci status0 = rsnd_mod_read(mod, SCU_SYS_STATUS0); 42262306a36Sopenharmony_ci status1 = rsnd_mod_read(mod, SCU_SYS_STATUS1); 42362306a36Sopenharmony_ci if ((status0 & val0) || (status1 & val1)) { 42462306a36Sopenharmony_ci rsnd_print_irq_status(dev, "%s err status : 0x%08x, 0x%08x\n", 42562306a36Sopenharmony_ci rsnd_mod_name(mod), status0, status1); 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci ret = true; 42862306a36Sopenharmony_ci } 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci return ret; 43162306a36Sopenharmony_ci} 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_cistatic int rsnd_src_start(struct rsnd_mod *mod, 43462306a36Sopenharmony_ci struct rsnd_dai_stream *io, 43562306a36Sopenharmony_ci struct rsnd_priv *priv) 43662306a36Sopenharmony_ci{ 43762306a36Sopenharmony_ci u32 val; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci /* 44062306a36Sopenharmony_ci * WORKAROUND 44162306a36Sopenharmony_ci * 44262306a36Sopenharmony_ci * Enable SRC output if you want to use sync convert together with DVC 44362306a36Sopenharmony_ci */ 44462306a36Sopenharmony_ci val = (rsnd_io_to_mod_dvc(io) && !rsnd_src_sync_is_enabled(mod)) ? 44562306a36Sopenharmony_ci 0x01 : 0x11; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci rsnd_mod_write(mod, SRC_CTRL, val); 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci return 0; 45062306a36Sopenharmony_ci} 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_cistatic int rsnd_src_stop(struct rsnd_mod *mod, 45362306a36Sopenharmony_ci struct rsnd_dai_stream *io, 45462306a36Sopenharmony_ci struct rsnd_priv *priv) 45562306a36Sopenharmony_ci{ 45662306a36Sopenharmony_ci rsnd_mod_write(mod, SRC_CTRL, 0); 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci return 0; 45962306a36Sopenharmony_ci} 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_cistatic int rsnd_src_init(struct rsnd_mod *mod, 46262306a36Sopenharmony_ci struct rsnd_dai_stream *io, 46362306a36Sopenharmony_ci struct rsnd_priv *priv) 46462306a36Sopenharmony_ci{ 46562306a36Sopenharmony_ci struct rsnd_src *src = rsnd_mod_to_src(mod); 46662306a36Sopenharmony_ci int ret; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci /* reset sync convert_rate */ 46962306a36Sopenharmony_ci src->sync.val = 0; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci ret = rsnd_mod_power_on(mod); 47262306a36Sopenharmony_ci if (ret < 0) 47362306a36Sopenharmony_ci return ret; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci rsnd_src_activation(mod); 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci rsnd_src_set_convert_rate(io, mod); 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci rsnd_src_status_clear(mod); 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci return 0; 48262306a36Sopenharmony_ci} 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_cistatic int rsnd_src_quit(struct rsnd_mod *mod, 48562306a36Sopenharmony_ci struct rsnd_dai_stream *io, 48662306a36Sopenharmony_ci struct rsnd_priv *priv) 48762306a36Sopenharmony_ci{ 48862306a36Sopenharmony_ci struct rsnd_src *src = rsnd_mod_to_src(mod); 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci rsnd_src_halt(mod); 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci rsnd_mod_power_off(mod); 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci /* reset sync convert_rate */ 49562306a36Sopenharmony_ci src->sync.val = 0; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci return 0; 49862306a36Sopenharmony_ci} 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_cistatic void __rsnd_src_interrupt(struct rsnd_mod *mod, 50162306a36Sopenharmony_ci struct rsnd_dai_stream *io) 50262306a36Sopenharmony_ci{ 50362306a36Sopenharmony_ci struct rsnd_priv *priv = rsnd_mod_to_priv(mod); 50462306a36Sopenharmony_ci bool stop = false; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci spin_lock(&priv->lock); 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci /* ignore all cases if not working */ 50962306a36Sopenharmony_ci if (!rsnd_io_is_working(io)) 51062306a36Sopenharmony_ci goto rsnd_src_interrupt_out; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci if (rsnd_src_error_occurred(mod)) 51362306a36Sopenharmony_ci stop = true; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci rsnd_src_status_clear(mod); 51662306a36Sopenharmony_cirsnd_src_interrupt_out: 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci spin_unlock(&priv->lock); 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci if (stop) 52162306a36Sopenharmony_ci snd_pcm_stop_xrun(io->substream); 52262306a36Sopenharmony_ci} 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_cistatic irqreturn_t rsnd_src_interrupt(int irq, void *data) 52562306a36Sopenharmony_ci{ 52662306a36Sopenharmony_ci struct rsnd_mod *mod = data; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci rsnd_mod_interrupt(mod, __rsnd_src_interrupt); 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci return IRQ_HANDLED; 53162306a36Sopenharmony_ci} 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_cistatic int rsnd_src_probe_(struct rsnd_mod *mod, 53462306a36Sopenharmony_ci struct rsnd_dai_stream *io, 53562306a36Sopenharmony_ci struct rsnd_priv *priv) 53662306a36Sopenharmony_ci{ 53762306a36Sopenharmony_ci struct rsnd_src *src = rsnd_mod_to_src(mod); 53862306a36Sopenharmony_ci struct device *dev = rsnd_priv_to_dev(priv); 53962306a36Sopenharmony_ci int irq = src->irq; 54062306a36Sopenharmony_ci int ret; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci if (irq > 0) { 54362306a36Sopenharmony_ci /* 54462306a36Sopenharmony_ci * IRQ is not supported on non-DT 54562306a36Sopenharmony_ci * see 54662306a36Sopenharmony_ci * rsnd_src_irq() 54762306a36Sopenharmony_ci */ 54862306a36Sopenharmony_ci ret = devm_request_irq(dev, irq, 54962306a36Sopenharmony_ci rsnd_src_interrupt, 55062306a36Sopenharmony_ci IRQF_SHARED, 55162306a36Sopenharmony_ci dev_name(dev), mod); 55262306a36Sopenharmony_ci if (ret) 55362306a36Sopenharmony_ci return ret; 55462306a36Sopenharmony_ci } 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci ret = rsnd_dma_attach(io, mod, &src->dma); 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci return ret; 55962306a36Sopenharmony_ci} 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_cistatic int rsnd_src_pcm_new(struct rsnd_mod *mod, 56262306a36Sopenharmony_ci struct rsnd_dai_stream *io, 56362306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd) 56462306a36Sopenharmony_ci{ 56562306a36Sopenharmony_ci struct rsnd_src *src = rsnd_mod_to_src(mod); 56662306a36Sopenharmony_ci int ret; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci /* 56962306a36Sopenharmony_ci * enable SRC sync convert if possible 57062306a36Sopenharmony_ci */ 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci /* 57362306a36Sopenharmony_ci * It can't use SRC Synchronous convert 57462306a36Sopenharmony_ci * when Capture if it uses CMD 57562306a36Sopenharmony_ci */ 57662306a36Sopenharmony_ci if (rsnd_io_to_mod_cmd(io) && !rsnd_io_is_play(io)) 57762306a36Sopenharmony_ci return 0; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci /* 58062306a36Sopenharmony_ci * enable sync convert 58162306a36Sopenharmony_ci */ 58262306a36Sopenharmony_ci ret = rsnd_kctrl_new_s(mod, io, rtd, 58362306a36Sopenharmony_ci rsnd_io_is_play(io) ? 58462306a36Sopenharmony_ci "SRC Out Rate Switch" : 58562306a36Sopenharmony_ci "SRC In Rate Switch", 58662306a36Sopenharmony_ci rsnd_kctrl_accept_anytime, 58762306a36Sopenharmony_ci rsnd_src_set_convert_rate, 58862306a36Sopenharmony_ci &src->sen, 1); 58962306a36Sopenharmony_ci if (ret < 0) 59062306a36Sopenharmony_ci return ret; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci ret = rsnd_kctrl_new_s(mod, io, rtd, 59362306a36Sopenharmony_ci rsnd_io_is_play(io) ? 59462306a36Sopenharmony_ci "SRC Out Rate" : 59562306a36Sopenharmony_ci "SRC In Rate", 59662306a36Sopenharmony_ci rsnd_kctrl_accept_runtime, 59762306a36Sopenharmony_ci rsnd_src_set_convert_rate, 59862306a36Sopenharmony_ci &src->sync, 192000); 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci return ret; 60162306a36Sopenharmony_ci} 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 60462306a36Sopenharmony_cistatic void rsnd_src_debug_info(struct seq_file *m, 60562306a36Sopenharmony_ci struct rsnd_dai_stream *io, 60662306a36Sopenharmony_ci struct rsnd_mod *mod) 60762306a36Sopenharmony_ci{ 60862306a36Sopenharmony_ci rsnd_debugfs_mod_reg_show(m, mod, RSND_GEN2_SCU, 60962306a36Sopenharmony_ci rsnd_mod_id(mod) * 0x20, 0x20); 61062306a36Sopenharmony_ci seq_puts(m, "\n"); 61162306a36Sopenharmony_ci rsnd_debugfs_mod_reg_show(m, mod, RSND_GEN2_SCU, 61262306a36Sopenharmony_ci 0x1c0, 0x20); 61362306a36Sopenharmony_ci seq_puts(m, "\n"); 61462306a36Sopenharmony_ci rsnd_debugfs_mod_reg_show(m, mod, RSND_GEN2_SCU, 61562306a36Sopenharmony_ci 0x200 + rsnd_mod_id(mod) * 0x40, 0x40); 61662306a36Sopenharmony_ci} 61762306a36Sopenharmony_ci#define DEBUG_INFO .debug_info = rsnd_src_debug_info 61862306a36Sopenharmony_ci#else 61962306a36Sopenharmony_ci#define DEBUG_INFO 62062306a36Sopenharmony_ci#endif 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_cistatic struct rsnd_mod_ops rsnd_src_ops = { 62362306a36Sopenharmony_ci .name = SRC_NAME, 62462306a36Sopenharmony_ci .dma_req = rsnd_src_dma_req, 62562306a36Sopenharmony_ci .probe = rsnd_src_probe_, 62662306a36Sopenharmony_ci .init = rsnd_src_init, 62762306a36Sopenharmony_ci .quit = rsnd_src_quit, 62862306a36Sopenharmony_ci .start = rsnd_src_start, 62962306a36Sopenharmony_ci .stop = rsnd_src_stop, 63062306a36Sopenharmony_ci .irq = rsnd_src_irq, 63162306a36Sopenharmony_ci .pcm_new = rsnd_src_pcm_new, 63262306a36Sopenharmony_ci .get_status = rsnd_mod_get_status, 63362306a36Sopenharmony_ci DEBUG_INFO 63462306a36Sopenharmony_ci}; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_cistruct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id) 63762306a36Sopenharmony_ci{ 63862306a36Sopenharmony_ci if (WARN_ON(id < 0 || id >= rsnd_src_nr(priv))) 63962306a36Sopenharmony_ci id = 0; 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci return rsnd_mod_get(rsnd_src_get(priv, id)); 64262306a36Sopenharmony_ci} 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ciint rsnd_src_probe(struct rsnd_priv *priv) 64562306a36Sopenharmony_ci{ 64662306a36Sopenharmony_ci struct device_node *node; 64762306a36Sopenharmony_ci struct device_node *np; 64862306a36Sopenharmony_ci struct device *dev = rsnd_priv_to_dev(priv); 64962306a36Sopenharmony_ci struct rsnd_src *src; 65062306a36Sopenharmony_ci struct clk *clk; 65162306a36Sopenharmony_ci char name[RSND_SRC_NAME_SIZE]; 65262306a36Sopenharmony_ci int i, nr, ret; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci /* This driver doesn't support Gen1 at this point */ 65562306a36Sopenharmony_ci if (rsnd_is_gen1(priv)) 65662306a36Sopenharmony_ci return 0; 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci node = rsnd_src_of_node(priv); 65962306a36Sopenharmony_ci if (!node) 66062306a36Sopenharmony_ci return 0; /* not used is not error */ 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci nr = rsnd_node_count(priv, node, SRC_NAME); 66362306a36Sopenharmony_ci if (!nr) { 66462306a36Sopenharmony_ci ret = -EINVAL; 66562306a36Sopenharmony_ci goto rsnd_src_probe_done; 66662306a36Sopenharmony_ci } 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci src = devm_kcalloc(dev, nr, sizeof(*src), GFP_KERNEL); 66962306a36Sopenharmony_ci if (!src) { 67062306a36Sopenharmony_ci ret = -ENOMEM; 67162306a36Sopenharmony_ci goto rsnd_src_probe_done; 67262306a36Sopenharmony_ci } 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci priv->src_nr = nr; 67562306a36Sopenharmony_ci priv->src = src; 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci i = 0; 67862306a36Sopenharmony_ci for_each_child_of_node(node, np) { 67962306a36Sopenharmony_ci if (!of_device_is_available(np)) 68062306a36Sopenharmony_ci goto skip; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci i = rsnd_node_fixed_index(dev, np, SRC_NAME, i); 68362306a36Sopenharmony_ci if (i < 0) { 68462306a36Sopenharmony_ci ret = -EINVAL; 68562306a36Sopenharmony_ci of_node_put(np); 68662306a36Sopenharmony_ci goto rsnd_src_probe_done; 68762306a36Sopenharmony_ci } 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci src = rsnd_src_get(priv, i); 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci snprintf(name, RSND_SRC_NAME_SIZE, "%s.%d", 69262306a36Sopenharmony_ci SRC_NAME, i); 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci src->irq = irq_of_parse_and_map(np, 0); 69562306a36Sopenharmony_ci if (!src->irq) { 69662306a36Sopenharmony_ci ret = -EINVAL; 69762306a36Sopenharmony_ci of_node_put(np); 69862306a36Sopenharmony_ci goto rsnd_src_probe_done; 69962306a36Sopenharmony_ci } 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci clk = devm_clk_get(dev, name); 70262306a36Sopenharmony_ci if (IS_ERR(clk)) { 70362306a36Sopenharmony_ci ret = PTR_ERR(clk); 70462306a36Sopenharmony_ci of_node_put(np); 70562306a36Sopenharmony_ci goto rsnd_src_probe_done; 70662306a36Sopenharmony_ci } 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci ret = rsnd_mod_init(priv, rsnd_mod_get(src), 70962306a36Sopenharmony_ci &rsnd_src_ops, clk, RSND_MOD_SRC, i); 71062306a36Sopenharmony_ci if (ret) { 71162306a36Sopenharmony_ci of_node_put(np); 71262306a36Sopenharmony_ci goto rsnd_src_probe_done; 71362306a36Sopenharmony_ci } 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ciskip: 71662306a36Sopenharmony_ci i++; 71762306a36Sopenharmony_ci } 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci ret = 0; 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_cirsnd_src_probe_done: 72262306a36Sopenharmony_ci of_node_put(node); 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci return ret; 72562306a36Sopenharmony_ci} 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_civoid rsnd_src_remove(struct rsnd_priv *priv) 72862306a36Sopenharmony_ci{ 72962306a36Sopenharmony_ci struct rsnd_src *src; 73062306a36Sopenharmony_ci int i; 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci for_each_rsnd_src(src, priv, i) { 73362306a36Sopenharmony_ci rsnd_mod_quit(rsnd_mod_get(src)); 73462306a36Sopenharmony_ci } 73562306a36Sopenharmony_ci} 736