static irqreturn_t rsnd_ssi_interrupt(int irq, void *data) { struct rsnd_ssi *ssi = data; struct rsnd_mod *mod = &ssi->mod; struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); int is_dma = rsnd_ssi_is_dma_mode(mod); u32 status = rsnd_mod_read(mod, SSISR); if (!io) return IRQ_NONE; /* PIO only */ if (!is_dma && (status & DIRQ)) { struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); u32 *buf = (u32 *)(runtime->dma_area + rsnd_dai_pointer_offset(io, 0)); /* * 8/16/32 data can be assesse to TDR/RDR register * directly as 32bit data * see rsnd_ssi_init() */ if (rsnd_io_is_play(io)) rsnd_mod_write(mod, SSITDR, *buf); else *buf = rsnd_mod_read(mod, SSIRDR); rsnd_dai_pointer_update(io, sizeof(*buf)); } /* PIO / DMA */ if (status & (UIRQ | OIRQ)) { struct device *dev = rsnd_priv_to_dev(priv); /* * restart SSI */ dev_dbg(dev, "%s[%d] restart\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); rsnd_ssi_stop(mod, priv); if (ssi->err < 1024) rsnd_ssi_start(mod, priv); else dev_warn(dev, "no more SSI restart\n"); } rsnd_ssi_record_error(ssi, status); return IRQ_HANDLED; }
static void __rsnd_ssi_interrupt(struct rsnd_mod *mod, struct rsnd_dai_stream *io) { struct rsnd_priv *priv = rsnd_mod_to_priv(mod); int is_dma = rsnd_ssi_is_dma_mode(mod); u32 status; bool elapsed = false; bool stop = false; spin_lock(&priv->lock); /* ignore all cases if not working */ if (!rsnd_io_is_working(io)) goto rsnd_ssi_interrupt_out; status = rsnd_ssi_status_get(mod); /* PIO only */ if (!is_dma && (status & DIRQ)) { struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); u32 *buf = (u32 *)(runtime->dma_area + rsnd_dai_pointer_offset(io, 0)); /* * 8/16/32 data can be assesse to TDR/RDR register * directly as 32bit data * see rsnd_ssi_init() */ if (rsnd_io_is_play(io)) rsnd_mod_write(mod, SSITDR, *buf); else *buf = rsnd_mod_read(mod, SSIRDR); elapsed = rsnd_dai_pointer_update(io, sizeof(*buf)); } /* DMA only */ if (is_dma && (status & (UIRQ | OIRQ))) stop = true; rsnd_ssi_status_clear(mod); rsnd_ssi_interrupt_out: spin_unlock(&priv->lock); if (elapsed) rsnd_dai_period_elapsed(io); if (stop) snd_pcm_stop_xrun(io->substream); }
int rsnd_src_ssi_irq_enable(struct rsnd_mod *ssi_mod) { struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod); if (rsnd_is_gen1(priv)) return 0; /* enable SSI interrupt if Gen2 */ if (rsnd_ssi_is_dma_mode(ssi_mod)) rsnd_mod_write(ssi_mod, INT_ENABLE, 0x0e000000); else rsnd_mod_write(ssi_mod, INT_ENABLE, 0x0f000000); 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 void rsnd_ssi_hw_start(struct rsnd_ssi *ssi, struct rsnd_dai_stream *io) { struct rsnd_priv *priv = rsnd_io_to_priv(io); struct rsnd_dai *rdai = rsnd_io_to_rdai(io); struct device *dev = rsnd_priv_to_dev(priv); u32 cr_mode; u32 cr; if (0 == ssi->usrcnt) { rsnd_mod_hw_start(&ssi->mod); if (rsnd_rdai_is_clk_master(rdai)) { if (rsnd_ssi_clk_from_parent(ssi)) rsnd_ssi_hw_start(ssi->parent, io); else rsnd_ssi_master_clk_start(ssi, io); } } cr_mode = rsnd_ssi_is_dma_mode(&ssi->mod) ? DMEN : /* DMA : enable DMA */ DIEN; /* PIO : enable Data interrupt */ cr = ssi->cr_own | ssi->cr_clk | cr_mode | UIEN | OIEN | EN; rsnd_mod_write(&ssi->mod, SSICR, cr); /* enable WS continue */ if (rsnd_rdai_is_clk_master(rdai)) rsnd_mod_write(&ssi->mod, SSIWSR, CONT); /* clear error status */ rsnd_mod_write(&ssi->mod, SSISR, 0); ssi->usrcnt++; dev_dbg(dev, "%s[%d] hw started\n", rsnd_mod_name(&ssi->mod), rsnd_mod_id(&ssi->mod)); }
static int rsnd_ssi_irq(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv, int enable) { u32 val = 0; if (rsnd_is_gen1(priv)) return 0; if (rsnd_ssi_is_parent(mod, io)) return 0; if (!rsnd_ssi_is_run_mods(mod, io)) return 0; if (enable) val = rsnd_ssi_is_dma_mode(mod) ? 0x0e000000 : 0x0f000000; rsnd_mod_write(mod, SSI_INT_ENABLE, val); return 0; }
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; }