static int rsnd_ssi_common_probe(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) { struct device *dev = rsnd_priv_to_dev(priv); struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); int ret; /* * SSIP/SSIU/IRQ are not needed on * SSI Multi slaves */ if (rsnd_ssi_is_multi_slave(mod, io)) return 0; /* * It can't judge ssi parent at this point * see rsnd_ssi_pcm_new() */ ret = rsnd_ssiu_attach(io, mod); if (ret < 0) return ret; ret = devm_request_irq(dev, ssi->irq, rsnd_ssi_interrupt, IRQF_SHARED, dev_name(dev), mod); return ret; }
static int rsnd_ssi_hw_params(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); int chan = params_channels(params); /* * Already working. * It will happen if SSI has parent/child connection. */ if (ssi->usrcnt > 1) { /* * it is error if child <-> parent SSI uses * different channels. */ if (ssi->chan != chan) return -EIO; } ssi->chan = chan; return 0; }
static int rsnd_ssi_quit(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) { struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); struct device *dev = rsnd_priv_to_dev(priv); if (!rsnd_ssi_is_run_mods(mod, io)) return 0; if (!ssi->usrcnt) { dev_err(dev, "%s[%d] usrcnt error\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); return -EIO; } if (!rsnd_ssi_is_parent(mod, io)) ssi->cr_own = 0; rsnd_ssi_master_clk_stop(mod, io); rsnd_mod_power_off(mod); ssi->usrcnt--; return 0; }
/* * SSI mod common functions */ static int rsnd_ssi_init(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) { struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); int ret; if (!rsnd_ssi_is_run_mods(mod, io)) return 0; ssi->usrcnt++; rsnd_mod_power_on(mod); ret = rsnd_ssi_master_clk_start(mod, io); if (ret < 0) return ret; if (!rsnd_ssi_is_parent(mod, io)) rsnd_ssi_config_init(mod, io); rsnd_ssi_register_setup(mod); /* clear error status */ rsnd_ssi_status_clear(mod); return 0; }
static int rsnd_ssi_dma_probe(struct rsnd_mod *mod, struct rsnd_dai *rdai, struct rsnd_dai_stream *io) { struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); struct rcar_snd_info *info = rsnd_priv_to_info(priv); struct device *dev = rsnd_priv_to_dev(priv); int dma_id = ssi->info->dma_id; int is_play; int ret; if (info->dai_info) is_play = rsnd_info_is_playback(priv, ssi); else is_play = rsnd_ssi_is_play(&ssi->mod); ret = rsnd_dma_init( priv, rsnd_mod_to_dma(mod), is_play, dma_id); if (ret < 0) dev_err(dev, "SSI DMA failed\n"); return ret; }
static void rsnd_ssi_parent_clk_setup(struct rsnd_priv *priv, struct rsnd_ssi *ssi) { if (!rsnd_ssi_is_pin_sharing(&ssi->mod)) return; switch (rsnd_mod_id(&ssi->mod)) { case 1: case 2: ssi->parent = rsnd_mod_to_ssi(rsnd_ssi_mod_get(priv, 0)); break; case 4: ssi->parent = rsnd_mod_to_ssi(rsnd_ssi_mod_get(priv, 3)); break; case 8: ssi->parent = rsnd_mod_to_ssi(rsnd_ssi_mod_get(priv, 7)); break; } }
static void rsnd_ssi_register_setup(struct rsnd_mod *mod) { struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); rsnd_mod_write(mod, SSIWSR, ssi->wsr); rsnd_mod_write(mod, SSICR, ssi->cr_own | ssi->cr_clk | ssi->cr_mode); /* without EN */ }
/* * SSI mod common functions */ static int rsnd_ssi_init(struct rsnd_mod *mod, struct rsnd_dai *rdai, struct rsnd_dai_stream *io) { struct rsnd_ssi *ssi = rsnd_mod_to_ssi(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); u32 cr; cr = FORCE; /* * always use 32bit system word for easy clock calculation. * see also rsnd_ssi_master_clk_enable() */ cr |= SWL_32; /* * init clock settings for SSICR */ switch (runtime->sample_bits) { case 16: cr |= DWL_16; break; case 32: cr |= DWL_24; break; default: return -EIO; } if (rdai->bit_clk_inv) cr |= SCKP; if (rdai->frm_clk_inv) cr |= SWSP; if (rdai->data_alignment) cr |= SDTA; if (rdai->sys_delay) cr |= DEL; if (rsnd_dai_is_play(rdai, io)) cr |= TRMD; /* * set ssi parameter */ ssi->rdai = rdai; ssi->io = io; ssi->cr_own = cr; ssi->err = -1; /* ignore 1st error */ rsnd_ssi_mode_set(priv, rdai, ssi); dev_dbg(dev, "%s.%d init\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); return 0; }
static int rsnd_ssi_pio_stop(struct rsnd_mod *mod, struct rsnd_dai *rdai, struct rsnd_dai_stream *io) { struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); ssi->cr_etc = 0; rsnd_ssi_hw_stop(ssi, rdai); return 0; }
static int rsnd_ssi_dma_remove(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) { struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); struct device *dev = rsnd_priv_to_dev(priv); int irq = ssi->irq; /* PIO will request IRQ again */ devm_free_irq(dev, irq, mod); return 0; }
static int rsnd_ssi_start(struct rsnd_mod *mod, struct rsnd_priv *priv) { struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); rsnd_src_ssiu_start(mod, rsnd_ssi_use_busif(mod)); rsnd_ssi_hw_start(ssi, io); rsnd_src_ssi_irq_enable(mod); return 0; }
/* * SSI PIO */ static int rsnd_ssi_pio_probe(struct rsnd_mod *mod, struct rsnd_priv *priv) { struct device *dev = rsnd_priv_to_dev(priv); struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); int ret; ret = devm_request_irq(dev, ssi->info->irq, rsnd_ssi_interrupt, IRQF_SHARED, dev_name(dev), ssi); return ret; }
static int rsnd_ssi_pio_start(struct rsnd_mod *mod, struct rsnd_dai *rdai, struct rsnd_dai_stream *io) { struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); /* enable PIO IRQ */ ssi->cr_etc = UIEN | OIEN | DIEN; rsnd_src_enable_ssi_irq(mod, rdai, io); rsnd_ssi_hw_start(ssi, rdai, io); return 0; }
static int rsnd_ssi_quit(struct rsnd_mod *mod, struct rsnd_priv *priv) { struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); struct device *dev = rsnd_priv_to_dev(priv); if (ssi->err > 0) dev_warn(dev, "%s[%d] under/over flow err = %d\n", rsnd_mod_name(mod), rsnd_mod_id(mod), ssi->err); ssi->cr_own = 0; ssi->err = 0; return 0; }
static int rsnd_ssi_stop(struct rsnd_mod *mod, struct rsnd_priv *priv) { struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); rsnd_src_ssi_irq_disable(mod); rsnd_ssi_record_error(ssi, rsnd_mod_read(mod, SSISR)); rsnd_ssi_hw_stop(ssi); rsnd_src_ssiu_stop(mod); return 0; }
int rsnd_ssi_use_busif(struct rsnd_mod *mod) { struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); int use_busif = 0; if (!rsnd_ssi_is_dma_mode(mod)) return 0; if (!(rsnd_ssi_mode_flags(ssi) & RSND_SSI_NO_BUSIF)) use_busif = 1; if (rsnd_io_to_mod_src(io)) use_busif = 1; return use_busif; }
static int rsnd_ssi_pio_stop(struct rsnd_mod *mod, struct rsnd_dai *rdai, struct rsnd_dai_stream *io) { struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct device *dev = rsnd_priv_to_dev(priv); struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); dev_dbg(dev, "%s.%d stop\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); ssi->cr_etc = 0; rsnd_ssi_hw_stop(ssi, rdai); return 0; }
static int rsnd_ssi_pio_start(struct rsnd_mod *mod, struct rsnd_dai *rdai, struct rsnd_dai_stream *io) { struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); struct device *dev = rsnd_priv_to_dev(priv); /* enable PIO IRQ */ ssi->cr_etc = UIEN | OIEN | DIEN; rsnd_ssi_hw_start(ssi, rdai, io); dev_dbg(dev, "%s.%d start\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); return 0; }
static int rsnd_ssi_quit(struct rsnd_mod *mod, struct rsnd_dai *rdai, struct rsnd_dai_stream *io) { struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct device *dev = rsnd_priv_to_dev(priv); if (ssi->err > 0) dev_warn(dev, "ssi under/over flow err = %d\n", ssi->err); ssi->rdai = NULL; ssi->cr_own = 0; ssi->err = 0; return 0; }
static int rsnd_ssi_dma_stop(struct rsnd_mod *mod, struct rsnd_dai *rdai, struct rsnd_dai_stream *io) { struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); struct rsnd_dma *dma = rsnd_mod_to_dma(&ssi->mod); ssi->cr_etc = 0; rsnd_ssi_record_error(ssi, rsnd_mod_read(mod, SSISR)); rsnd_ssi_hw_stop(ssi, rdai); rsnd_dma_stop(dma); return 0; }
static int rsnd_ssi_pio_probe(struct rsnd_mod *mod, struct rsnd_dai *rdai, struct rsnd_dai_stream *io) { struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct device *dev = rsnd_priv_to_dev(priv); struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); int irq = ssi->info->pio_irq; int ret; ret = devm_request_irq(dev, irq, rsnd_ssi_pio_interrupt, IRQF_SHARED, dev_name(dev), ssi); if (ret) dev_err(dev, "SSI request interrupt failed\n"); return ret; }
static int rsnd_ssi_dma_start(struct rsnd_mod *mod, struct rsnd_dai *rdai, struct rsnd_dai_stream *io) { struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); struct rsnd_dma *dma = rsnd_mod_to_dma(&ssi->mod); /* enable DMA transfer */ ssi->cr_etc = DMEN; rsnd_dma_start(dma); rsnd_ssi_hw_start(ssi, ssi->rdai, io); /* enable WS continue */ if (rsnd_dai_is_clk_master(rdai)) rsnd_mod_write(&ssi->mod, SSIWSR, CONT); return 0; }
static void rsnd_ssi_master_clk_stop(struct rsnd_mod *mod, struct rsnd_dai_stream *io) { struct rsnd_dai *rdai = rsnd_io_to_rdai(io); struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); struct rsnd_mod *ssi_parent_mod = rsnd_io_to_mod_ssip(io); if (!rsnd_rdai_is_clk_master(rdai)) return; if (ssi_parent_mod && !rsnd_ssi_is_parent(mod, io)) return; if (ssi->usrcnt > 1) return; ssi->cr_clk = 0; ssi->rate = 0; rsnd_adg_ssi_clk_stop(mod); }
static int rsnd_ssi_dma_probe(struct rsnd_mod *mod, struct rsnd_priv *priv) { struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); struct device *dev = rsnd_priv_to_dev(priv); int dma_id = ssi->info->dma_id; int ret; ret = devm_request_irq(dev, ssi->info->irq, rsnd_ssi_interrupt, IRQF_SHARED, dev_name(dev), ssi); if (ret) return ret; ret = rsnd_dma_init( priv, rsnd_mod_to_dma(mod), dma_id); return ret; }
static int rsnd_ssi_pio_start(struct rsnd_mod *mod, struct rsnd_dai *rdai, struct rsnd_dai_stream *io) { struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); struct device *dev = rsnd_priv_to_dev(priv); /* enable PIO IRQ */ ssi->cr_etc = UIEN | OIEN | DIEN; /* enable PIO interrupt if gen2 */ if (rsnd_is_gen2(priv)) rsnd_mod_write(&ssi->mod, INT_ENABLE, 0x0f000000); rsnd_ssi_hw_start(ssi, rdai, io); dev_dbg(dev, "%s.%d start\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); return 0; }
static int rsnd_ssi_stop(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) { struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); u32 cr; if (!rsnd_ssi_is_run_mods(mod, io)) return 0; /* * don't stop if not last user * see also * rsnd_ssi_start * rsnd_ssi_interrupt */ if (ssi->usrcnt > 1) return 0; /* * disable all IRQ, * and, wait all data was sent */ cr = ssi->cr_own | ssi->cr_clk; rsnd_mod_write(mod, SSICR, cr | EN); rsnd_ssi_status_check(mod, DIRQ); /* * disable SSI, * and, wait idle state */ rsnd_mod_write(mod, SSICR, cr); /* disabled all */ rsnd_ssi_status_check(mod, IIRQ); return 0; }
static int rsnd_ssi_dma_probe(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) { struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); int dma_id = 0; /* not needed */ int ret; /* * SSIP/SSIU/IRQ/DMA are not needed on * SSI Multi slaves */ if (rsnd_ssi_is_multi_slave(mod, io)) return 0; ret = rsnd_ssi_common_probe(mod, io, priv); if (ret) return ret; /* SSI probe might be called many times in MUX multi path */ ret = rsnd_dma_attach(io, mod, &ssi->dma, dma_id); return ret; }
int rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod) { struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); return !!(rsnd_ssi_mode_flags(ssi) & RSND_SSI_CLK_PIN_SHARE); }
static void rsnd_ssi_config_init(struct rsnd_mod *mod, struct rsnd_dai_stream *io) { struct rsnd_dai *rdai = rsnd_io_to_rdai(io); struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); u32 cr_own; u32 cr_mode; u32 wsr; int is_tdm; is_tdm = rsnd_runtime_is_ssi_tdm(io); /* * always use 32bit system word. * see also rsnd_ssi_master_clk_enable() */ cr_own = FORCE | SWL_32 | PDTA; if (rdai->bit_clk_inv) cr_own |= SCKP; if (rdai->frm_clk_inv ^ is_tdm) cr_own |= SWSP; if (rdai->data_alignment) cr_own |= SDTA; if (rdai->sys_delay) cr_own |= DEL; if (rsnd_io_is_play(io)) cr_own |= TRMD; switch (runtime->sample_bits) { case 16: cr_own |= DWL_16; break; case 32: cr_own |= DWL_24; break; } if (rsnd_ssi_is_dma_mode(mod)) { cr_mode = UIEN | OIEN | /* over/under run */ DMEN; /* DMA : enable DMA */ } else { cr_mode = DIEN; /* PIO : enable Data interrupt */ } /* * TDM Extend Mode * see * rsnd_ssiu_init_gen2() */ wsr = ssi->wsr; if (is_tdm) { wsr |= WS_MODE; cr_own |= CHNL_8; } ssi->cr_own = cr_own; ssi->cr_mode = cr_mode; ssi->wsr = wsr; }
int rsnd_ssi_is_play(struct rsnd_mod *mod) { struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); return !!(rsnd_ssi_mode_flags(ssi) & RSND_SSI_PLAY); }