Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
Kuninori Morimoto | 2575 | 97.10% | 103 | 92.79% |
Julia Lawall | 22 | 0.83% | 1 | 0.90% |
Hiroyuki Yokoyama | 18 | 0.68% | 1 | 0.90% |
Sergei Shtylyov | 13 | 0.49% | 1 | 0.90% |
Jiada Wang | 9 | 0.34% | 2 | 1.80% |
Takashi Iwai | 7 | 0.26% | 1 | 0.90% |
Jiasheng Jiang | 5 | 0.19% | 1 | 0.90% |
Kees Cook | 3 | 0.11% | 1 | 0.90% |
Total | 2652 | 111 |
// SPDX-License-Identifier: GPL-2.0 // // Renesas R-Car SRC support // // Copyright (C) 2013 Renesas Solutions Corp. // Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> /* * You can use Synchronous Sampling Rate Convert (if no DVC) * * amixer set "SRC Out Rate" on * aplay xxx.wav & * amixer set "SRC Out Rate" 96000 // convert rate to 96000Hz * amixer set "SRC Out Rate" 22050 // convert rate to 22050Hz */ /* * you can enable below define if you don't need * SSI interrupt status debug message when debugging * see rsnd_print_irq_status() * * #define RSND_DEBUG_NO_IRQ_STATUS 1 */ #include <linux/of_irq.h> #include "rsnd.h" #define SRC_NAME "src" /* SCU_SYSTEM_STATUS0/1 */ #define OUF_SRC(id) ((1 << (id + 16)) | (1 << id)) struct rsnd_src { struct rsnd_mod mod; struct rsnd_mod *dma; struct rsnd_kctrl_cfg_s sen; /* sync convert enable */ struct rsnd_kctrl_cfg_s sync; /* sync convert */ int irq; }; #define RSND_SRC_NAME_SIZE 16 #define rsnd_src_get(priv, id) ((struct rsnd_src *)(priv->src) + id) #define rsnd_src_nr(priv) ((priv)->src_nr) #define rsnd_src_sync_is_enabled(mod) (rsnd_mod_to_src(mod)->sen.val) #define rsnd_mod_to_src(_mod) \ container_of((_mod), struct rsnd_src, mod) #define for_each_rsnd_src(pos, priv, i) \ for ((i) = 0; \ ((i) < rsnd_src_nr(priv)) && \ ((pos) = (struct rsnd_src *)(priv)->src + i); \ i++) /* * image of SRC (Sampling Rate Converter) * * 96kHz <-> +-----+ 48kHz +-----+ 48kHz +-------+ * 48kHz <-> | SRC | <------> | SSI | <-----> | codec | * 44.1kHz <-> +-----+ +-----+ +-------+ * ... * */ static void rsnd_src_activation(struct rsnd_mod *mod) { rsnd_mod_write(mod, SRC_SWRSR, 0); rsnd_mod_write(mod, SRC_SWRSR, 1); } static void rsnd_src_halt(struct rsnd_mod *mod) { rsnd_mod_write(mod, SRC_SRCIR, 1); rsnd_mod_write(mod, SRC_SWRSR, 0); } static struct dma_chan *rsnd_src_dma_req(struct rsnd_dai_stream *io, struct rsnd_mod *mod) { struct rsnd_priv *priv = rsnd_mod_to_priv(mod); int is_play = rsnd_io_is_play(io); return rsnd_dma_request_channel(rsnd_src_of_node(priv), SRC_NAME, mod, is_play ? "rx" : "tx"); } static u32 rsnd_src_convert_rate(struct rsnd_dai_stream *io, struct rsnd_mod *mod) { struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); struct rsnd_src *src = rsnd_mod_to_src(mod); u32 convert_rate; if (!runtime) return 0; if (!rsnd_src_sync_is_enabled(mod)) return rsnd_io_converted_rate(io); convert_rate = src->sync.val; if (!convert_rate) convert_rate = rsnd_io_converted_rate(io); if (!convert_rate) convert_rate = runtime->rate; return convert_rate; } unsigned int rsnd_src_get_rate(struct rsnd_priv *priv, struct rsnd_dai_stream *io, int is_in) { struct rsnd_mod *src_mod = rsnd_io_to_mod_src(io); struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); unsigned int rate = 0; int is_play = rsnd_io_is_play(io); /* * Playback * runtime_rate -> [SRC] -> convert_rate * * Capture * convert_rate -> [SRC] -> runtime_rate */ if (is_play == is_in) return runtime->rate; /* * return convert rate if SRC is used, * otherwise, return runtime->rate as usual */ if (src_mod) rate = rsnd_src_convert_rate(io, src_mod); if (!rate) rate = runtime->rate; return rate; } static const u32 bsdsr_table_pattern1[] = { 0x01800000, /* 6 - 1/6 */ 0x01000000, /* 6 - 1/4 */ 0x00c00000, /* 6 - 1/3 */ 0x00800000, /* 6 - 1/2 */ 0x00600000, /* 6 - 2/3 */ 0x00400000, /* 6 - 1 */ }; static const u32 bsdsr_table_pattern2[] = { 0x02400000, /* 6 - 1/6 */ 0x01800000, /* 6 - 1/4 */ 0x01200000, /* 6 - 1/3 */ 0x00c00000, /* 6 - 1/2 */ 0x00900000, /* 6 - 2/3 */ 0x00600000, /* 6 - 1 */ }; static const u32 bsisr_table[] = { 0x00100060, /* 6 - 1/6 */ 0x00100040, /* 6 - 1/4 */ 0x00100030, /* 6 - 1/3 */ 0x00100020, /* 6 - 1/2 */ 0x00100020, /* 6 - 2/3 */ 0x00100020, /* 6 - 1 */ }; static const u32 chan288888[] = { 0x00000006, /* 1 to 2 */ 0x000001fe, /* 1 to 8 */ 0x000001fe, /* 1 to 8 */ 0x000001fe, /* 1 to 8 */ 0x000001fe, /* 1 to 8 */ 0x000001fe, /* 1 to 8 */ }; static const u32 chan244888[] = { 0x00000006, /* 1 to 2 */ 0x0000001e, /* 1 to 4 */ 0x0000001e, /* 1 to 4 */ 0x000001fe, /* 1 to 8 */ 0x000001fe, /* 1 to 8 */ 0x000001fe, /* 1 to 8 */ }; static const u32 chan222222[] = { 0x00000006, /* 1 to 2 */ 0x00000006, /* 1 to 2 */ 0x00000006, /* 1 to 2 */ 0x00000006, /* 1 to 2 */ 0x00000006, /* 1 to 2 */ 0x00000006, /* 1 to 2 */ }; static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io, struct rsnd_mod *mod) { struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct device *dev = rsnd_priv_to_dev(priv); struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); int is_play = rsnd_io_is_play(io); int use_src = 0; u32 fin, fout; u32 ifscr, fsrate, adinr; u32 cr, route; u32 i_busif, o_busif, tmp; const u32 *bsdsr_table; const u32 *chptn; uint ratio; int chan; int idx; if (!runtime) return; fin = rsnd_src_get_in_rate(priv, io); fout = rsnd_src_get_out_rate(priv, io); chan = rsnd_runtime_channel_original(io); /* 6 - 1/6 are very enough ratio for SRC_BSDSR */ if (fin == fout) ratio = 0; else if (fin > fout) ratio = 100 * fin / fout; else ratio = 100 * fout / fin; if (ratio > 600) { dev_err(dev, "FSO/FSI ratio error\n"); return; } use_src = (fin != fout) | rsnd_src_sync_is_enabled(mod); /* * SRC_ADINR */ adinr = rsnd_get_adinr_bit(mod, io) | chan; /* * SRC_IFSCR / SRC_IFSVR */ ifscr = 0; fsrate = 0; if (use_src) { u64 n; ifscr = 1; n = (u64)0x0400000 * fin; do_div(n, fout); fsrate = n; } /* * SRC_SRCCR / SRC_ROUTE_MODE0 */ cr = 0x00011110; route = 0x0; if (use_src) { route = 0x1; if (rsnd_src_sync_is_enabled(mod)) { cr |= 0x1; route |= rsnd_io_is_play(io) ? (0x1 << 24) : (0x1 << 25); } } /* * SRC_BSDSR / SRC_BSISR * * see * Combination of Register Setting Related to * FSO/FSI Ratio and Channel, Latency */ switch (rsnd_mod_id(mod)) { case 0: chptn = chan288888; bsdsr_table = bsdsr_table_pattern1; break; case 1: case 3: case 4: chptn = chan244888; bsdsr_table = bsdsr_table_pattern1; break; case 2: case 9: chptn = chan222222; bsdsr_table = bsdsr_table_pattern1; break; case 5: case 6: case 7: case 8: chptn = chan222222; bsdsr_table = bsdsr_table_pattern2; break; default: goto convert_rate_err; } /* * E3 need to overwrite */ if (rsnd_is_gen3_e3(priv)) switch (rsnd_mod_id(mod)) { case 0: case 4: chptn = chan222222; } for (idx = 0; idx < ARRAY_SIZE(chan222222); idx++) if (chptn[idx] & (1 << chan)) break; if (chan > 8 || idx >= ARRAY_SIZE(chan222222)) goto convert_rate_err; /* BUSIF_MODE */ tmp = rsnd_get_busif_shift(io, mod); i_busif = ( is_play ? tmp : 0) | 1; o_busif = (!is_play ? tmp : 0) | 1; rsnd_mod_write(mod, SRC_ROUTE_MODE0, route); rsnd_mod_write(mod, SRC_SRCIR, 1); /* initialize */ rsnd_mod_write(mod, SRC_ADINR, adinr); rsnd_mod_write(mod, SRC_IFSCR, ifscr); rsnd_mod_write(mod, SRC_IFSVR, fsrate); rsnd_mod_write(mod, SRC_SRCCR, cr); rsnd_mod_write(mod, SRC_BSDSR, bsdsr_table[idx]); rsnd_mod_write(mod, SRC_BSISR, bsisr_table[idx]); rsnd_mod_write(mod, SRC_SRCIR, 0); /* cancel initialize */ rsnd_mod_write(mod, SRC_I_BUSIF_MODE, i_busif); rsnd_mod_write(mod, SRC_O_BUSIF_MODE, o_busif); rsnd_mod_write(mod, SRC_BUSIF_DALIGN, rsnd_get_dalign(mod, io)); rsnd_adg_set_src_timesel_gen2(mod, io, fin, fout); return; convert_rate_err: dev_err(dev, "unknown BSDSR/BSDIR settings\n"); } static int rsnd_src_irq(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv, int enable) { struct rsnd_src *src = rsnd_mod_to_src(mod); u32 sys_int_val, int_val, sys_int_mask; int irq = src->irq; int id = rsnd_mod_id(mod); sys_int_val = sys_int_mask = OUF_SRC(id); int_val = 0x3300; /* * IRQ is not supported on non-DT * see * rsnd_src_probe_() */ if ((irq <= 0) || !enable) { sys_int_val = 0; int_val = 0; } /* * WORKAROUND * * ignore over flow error when rsnd_src_sync_is_enabled() */ if (rsnd_src_sync_is_enabled(mod)) sys_int_val = sys_int_val & 0xffff; rsnd_mod_write(mod, SRC_INT_ENABLE0, int_val); rsnd_mod_bset(mod, SCU_SYS_INT_EN0, sys_int_mask, sys_int_val); rsnd_mod_bset(mod, SCU_SYS_INT_EN1, sys_int_mask, sys_int_val); return 0; } static void rsnd_src_status_clear(struct rsnd_mod *mod) { u32 val = OUF_SRC(rsnd_mod_id(mod)); rsnd_mod_write(mod, SCU_SYS_STATUS0, val); rsnd_mod_write(mod, SCU_SYS_STATUS1, val); } static bool rsnd_src_error_occurred(struct rsnd_mod *mod) { struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct device *dev = rsnd_priv_to_dev(priv); u32 val0, val1; u32 status0, status1; bool ret = false; val0 = val1 = OUF_SRC(rsnd_mod_id(mod)); /* * WORKAROUND * * ignore over flow error when rsnd_src_sync_is_enabled() */ if (rsnd_src_sync_is_enabled(mod)) val0 = val0 & 0xffff; status0 = rsnd_mod_read(mod, SCU_SYS_STATUS0); status1 = rsnd_mod_read(mod, SCU_SYS_STATUS1); if ((status0 & val0) || (status1 & val1)) { rsnd_print_irq_status(dev, "%s err status : 0x%08x, 0x%08x\n", rsnd_mod_name(mod), status0, status1); ret = true; } return ret; } static int rsnd_src_start(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) { u32 val; /* * WORKAROUND * * Enable SRC output if you want to use sync convert together with DVC */ val = (rsnd_io_to_mod_dvc(io) && !rsnd_src_sync_is_enabled(mod)) ? 0x01 : 0x11; rsnd_mod_write(mod, SRC_CTRL, val); return 0; } static int rsnd_src_stop(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) { rsnd_mod_write(mod, SRC_CTRL, 0); return 0; } static int rsnd_src_init(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) { struct rsnd_src *src = rsnd_mod_to_src(mod); int ret; /* reset sync convert_rate */ src->sync.val = 0; ret = rsnd_mod_power_on(mod); if (ret < 0) return ret; rsnd_src_activation(mod); rsnd_src_set_convert_rate(io, mod); rsnd_src_status_clear(mod); return 0; } static int rsnd_src_quit(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) { struct rsnd_src *src = rsnd_mod_to_src(mod); rsnd_src_halt(mod); rsnd_mod_power_off(mod); /* reset sync convert_rate */ src->sync.val = 0; return 0; } static void __rsnd_src_interrupt(struct rsnd_mod *mod, struct rsnd_dai_stream *io) { struct rsnd_priv *priv = rsnd_mod_to_priv(mod); bool stop = false; spin_lock(&priv->lock); /* ignore all cases if not working */ if (!rsnd_io_is_working(io)) goto rsnd_src_interrupt_out; if (rsnd_src_error_occurred(mod)) stop = true; rsnd_src_status_clear(mod); rsnd_src_interrupt_out: spin_unlock(&priv->lock); if (stop) snd_pcm_stop_xrun(io->substream); } static irqreturn_t rsnd_src_interrupt(int irq, void *data) { struct rsnd_mod *mod = data; rsnd_mod_interrupt(mod, __rsnd_src_interrupt); return IRQ_HANDLED; } static int rsnd_src_probe_(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) { struct rsnd_src *src = rsnd_mod_to_src(mod); struct device *dev = rsnd_priv_to_dev(priv); int irq = src->irq; int ret; if (irq > 0) { /* * IRQ is not supported on non-DT * see * rsnd_src_irq() */ ret = devm_request_irq(dev, irq, rsnd_src_interrupt, IRQF_SHARED, dev_name(dev), mod); if (ret) return ret; } ret = rsnd_dma_attach(io, mod, &src->dma); return ret; } static int rsnd_src_pcm_new(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct snd_soc_pcm_runtime *rtd) { struct rsnd_src *src = rsnd_mod_to_src(mod); int ret; /* * enable SRC sync convert if possible */ /* * It can't use SRC Synchronous convert * when Capture if it uses CMD */ if (rsnd_io_to_mod_cmd(io) && !rsnd_io_is_play(io)) return 0; /* * enable sync convert */ ret = rsnd_kctrl_new_s(mod, io, rtd, rsnd_io_is_play(io) ? "SRC Out Rate Switch" : "SRC In Rate Switch", rsnd_kctrl_accept_anytime, rsnd_src_set_convert_rate, &src->sen, 1); if (ret < 0) return ret; ret = rsnd_kctrl_new_s(mod, io, rtd, rsnd_io_is_play(io) ? "SRC Out Rate" : "SRC In Rate", rsnd_kctrl_accept_runtime, rsnd_src_set_convert_rate, &src->sync, 192000); return ret; } #ifdef CONFIG_DEBUG_FS static void rsnd_src_debug_info(struct seq_file *m, struct rsnd_dai_stream *io, struct rsnd_mod *mod) { rsnd_debugfs_mod_reg_show(m, mod, RSND_BASE_SCU, rsnd_mod_id(mod) * 0x20, 0x20); seq_puts(m, "\n"); rsnd_debugfs_mod_reg_show(m, mod, RSND_BASE_SCU, 0x1c0, 0x20); seq_puts(m, "\n"); rsnd_debugfs_mod_reg_show(m, mod, RSND_BASE_SCU, 0x200 + rsnd_mod_id(mod) * 0x40, 0x40); } #define DEBUG_INFO .debug_info = rsnd_src_debug_info #else #define DEBUG_INFO #endif static struct rsnd_mod_ops rsnd_src_ops = { .name = SRC_NAME, .dma_req = rsnd_src_dma_req, .probe = rsnd_src_probe_, .init = rsnd_src_init, .quit = rsnd_src_quit, .start = rsnd_src_start, .stop = rsnd_src_stop, .irq = rsnd_src_irq, .pcm_new = rsnd_src_pcm_new, .get_status = rsnd_mod_get_status, DEBUG_INFO }; struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id) { if (WARN_ON(id < 0 || id >= rsnd_src_nr(priv))) id = 0; return rsnd_mod_get(rsnd_src_get(priv, id)); } int rsnd_src_probe(struct rsnd_priv *priv) { struct device_node *node; struct device_node *np; struct device *dev = rsnd_priv_to_dev(priv); struct rsnd_src *src; struct clk *clk; char name[RSND_SRC_NAME_SIZE]; int i, nr, ret; node = rsnd_src_of_node(priv); if (!node) return 0; /* not used is not error */ nr = rsnd_node_count(priv, node, SRC_NAME); if (!nr) { ret = -EINVAL; goto rsnd_src_probe_done; } src = devm_kcalloc(dev, nr, sizeof(*src), GFP_KERNEL); if (!src) { ret = -ENOMEM; goto rsnd_src_probe_done; } priv->src_nr = nr; priv->src = src; i = 0; for_each_child_of_node(node, np) { if (!of_device_is_available(np)) goto skip; i = rsnd_node_fixed_index(dev, np, SRC_NAME, i); if (i < 0) { ret = -EINVAL; of_node_put(np); goto rsnd_src_probe_done; } src = rsnd_src_get(priv, i); snprintf(name, RSND_SRC_NAME_SIZE, "%s.%d", SRC_NAME, i); src->irq = irq_of_parse_and_map(np, 0); if (!src->irq) { ret = -EINVAL; of_node_put(np); goto rsnd_src_probe_done; } clk = devm_clk_get(dev, name); if (IS_ERR(clk)) { ret = PTR_ERR(clk); of_node_put(np); goto rsnd_src_probe_done; } ret = rsnd_mod_init(priv, rsnd_mod_get(src), &rsnd_src_ops, clk, RSND_MOD_SRC, i); if (ret) { of_node_put(np); goto rsnd_src_probe_done; } skip: i++; } ret = 0; rsnd_src_probe_done: of_node_put(node); return ret; } void rsnd_src_remove(struct rsnd_priv *priv) { struct rsnd_src *src; int i; for_each_rsnd_src(src, priv, i) { rsnd_mod_quit(rsnd_mod_get(src)); } }
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1