static int rsnd_src_set_convert_rate(struct rsnd_mod *mod) { struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); struct rsnd_src *src = rsnd_mod_to_src(mod); u32 convert_rate = rsnd_src_convert_rate(src); u32 fsrate = 0; if (convert_rate) fsrate = 0x0400000 / convert_rate * runtime->rate; /* set/clear soft reset */ rsnd_mod_write(mod, SRC_SWRSR, 0); rsnd_mod_write(mod, SRC_SWRSR, 1); /* Set channel number and output bit length */ rsnd_mod_write(mod, SRC_ADINR, rsnd_get_adinr(mod)); /* Enable the initial value of IFS */ if (fsrate) { rsnd_mod_write(mod, SRC_IFSCR, 1); /* Set initial value of IFS */ rsnd_mod_write(mod, SRC_IFSVR, fsrate); } /* use DMA transfer */ rsnd_mod_write(mod, SRC_BUSIF_MODE, 1); return 0; }
/* * SSI PIO */ static irqreturn_t rsnd_ssi_pio_interrupt(int irq, void *data) { struct rsnd_ssi *ssi = data; struct rsnd_dai_stream *io = ssi->io; u32 status = rsnd_mod_read(&ssi->mod, SSISR); irqreturn_t ret = IRQ_NONE; if (io && (status & DIRQ)) { struct rsnd_dai *rdai = ssi->rdai; struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); u32 *buf = (u32 *)(runtime->dma_area + rsnd_dai_pointer_offset(io, 0)); rsnd_ssi_record_error(ssi, status); /* * 8/16/32 data can be assesse to TDR/RDR register * directly as 32bit data * see rsnd_ssi_init() */ if (rsnd_dai_is_play(rdai, io)) rsnd_mod_write(&ssi->mod, SSITDR, *buf); else *buf = rsnd_mod_read(&ssi->mod, SSIRDR); rsnd_dai_pointer_update(io, sizeof(*buf)); ret = IRQ_HANDLED; } return ret; }
static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi, struct rsnd_dai *rdai, struct rsnd_dai_stream *io) { struct rsnd_priv *priv = rsnd_mod_to_priv(&ssi->mod); struct device *dev = rsnd_priv_to_dev(priv); u32 cr; if (0 == ssi->usrcnt) { clk_enable(ssi->clk); if (rsnd_rdai_is_clk_master(rdai)) { struct snd_pcm_runtime *runtime; runtime = rsnd_io_to_runtime(io); if (rsnd_ssi_clk_from_parent(ssi)) rsnd_ssi_hw_start(ssi->parent, rdai, io); else rsnd_ssi_master_clk_start(ssi, runtime->rate); } } cr = ssi->cr_own | ssi->cr_clk | ssi->cr_etc | EN; rsnd_mod_write(&ssi->mod, SSICR, cr); ssi->usrcnt++; dev_dbg(dev, "ssi%d hw started\n", rsnd_mod_id(&ssi->mod)); }
static int rsnd_src_set_convert_timing_gen1(struct rsnd_mod *mod, struct rsnd_dai *rdai, struct rsnd_dai_stream *io) { struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct rsnd_src *src = rsnd_mod_to_src(mod); struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); u32 convert_rate = rsnd_src_convert_rate(src); u32 mask; u32 val; int shift; int id = rsnd_mod_id(mod); int ret; /* * SRC_TIMING_SELECT */ shift = (id % 4) * 8; mask = 0x1F << shift; /* * ADG is used as source clock if SRC was used, * then, SSI WS is used as destination clock. * SSI WS is used as source clock if SRC is not used * (when playback, source/destination become reverse when capture) */ ret = 0; if (convert_rate) { /* use ADG */ val = 0; ret = rsnd_adg_set_convert_clk_gen1(priv, mod, runtime->rate, convert_rate); } else if (8 == id) { /* use SSI WS, but SRU8 is special */ val = id << shift; } else { /* use SSI WS */ val = (id + 1) << shift; } if (ret < 0) return ret; switch (id / 4) { case 0: rsnd_mod_bset(mod, SRC_TMG_SEL0, mask, val); break; case 1: rsnd_mod_bset(mod, SRC_TMG_SEL1, mask, val); break; case 2: rsnd_mod_bset(mod, SRC_TMG_SEL2, mask, val); break; } return 0; }
/* * 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 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 int rsnd_ssi_dma_inquiry(struct rsnd_dma *dma, dma_addr_t *buf, int *len) { struct rsnd_ssi *ssi = rsnd_dma_to_ssi(dma); struct rsnd_dai_stream *io = ssi->io; struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); *len = io->byte_per_period; *buf = runtime->dma_addr + rsnd_dai_pointer_offset(io, ssi->dma_offset + *len); ssi->dma_offset = *len; /* it cares A/B plane */ return 0; }
static int rsnd_src_set_convert_rate_gen2(struct rsnd_mod *mod) { struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct device *dev = rsnd_priv_to_dev(priv); struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); struct rsnd_src *src = rsnd_mod_to_src(mod); u32 convert_rate = rsnd_src_convert_rate(src); uint ratio; int ret; /* 6 - 1/6 are very enough ratio for SRC_BSDSR */ if (!convert_rate) ratio = 0; else if (convert_rate > runtime->rate) ratio = 100 * convert_rate / runtime->rate; else ratio = 100 * runtime->rate / convert_rate; if (ratio > 600) { dev_err(dev, "FSO/FSI ratio error\n"); return -EINVAL; } ret = rsnd_src_set_convert_rate(mod); if (ret < 0) return ret; rsnd_mod_write(mod, SRC_SRCCR, 0x00011110); if (convert_rate) { /* Gen1/Gen2 are not compatible */ rsnd_mod_write(mod, SRC_ROUTE_MODE0, 1); } switch (rsnd_mod_id(mod)) { case 5: case 6: case 7: case 8: rsnd_mod_write(mod, SRC_BSDSR, 0x02400000); break; default: rsnd_mod_write(mod, SRC_BSDSR, 0x01800000); break; } rsnd_mod_write(mod, SRC_BSISR, 0x00100060); return 0; }
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); }
static int rsnd_src_set_convert_rate(struct rsnd_mod *mod, struct rsnd_dai *rdai, struct rsnd_dai_stream *io) { struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); struct rsnd_src *src = rsnd_mod_to_src(mod); u32 convert_rate = rsnd_src_convert_rate(src); u32 adinr = runtime->channels; u32 fsrate = 0; if (convert_rate) fsrate = 0x0400000 / convert_rate * runtime->rate; /* set/clear soft reset */ rsnd_mod_write(mod, SRC_SWRSR, 0); rsnd_mod_write(mod, SRC_SWRSR, 1); /* * Initialize the operation of the SRC internal circuits * see rsnd_src_start() */ rsnd_mod_write(mod, SRC_SRCIR, 1); /* Set channel number and output bit length */ switch (runtime->sample_bits) { case 16: adinr |= OTBL_16; break; case 32: adinr |= OTBL_24; break; default: return -EIO; } rsnd_mod_write(mod, SRC_ADINR, adinr); /* Enable the initial value of IFS */ if (fsrate) { rsnd_mod_write(mod, SRC_IFSCR, 1); /* Set initial value of IFS */ rsnd_mod_write(mod, SRC_IFSVR, fsrate); } /* use DMA transfer */ rsnd_mod_write(mod, SRC_BUSIF_MODE, 1); return 0; }
static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi, struct rsnd_dai_stream *io) { struct rsnd_priv *priv = rsnd_io_to_priv(io); struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); struct device *dev = rsnd_priv_to_dev(priv); int i, j, ret; int adg_clk_div_table[] = { 1, 6, /* see adg.c */ }; int ssi_clk_mul_table[] = { 1, 2, 4, 8, 16, 6, 12, }; unsigned int main_rate; unsigned int rate = rsnd_src_get_ssi_rate(priv, io, runtime); /* * Find best clock, and try to start ADG */ for (i = 0; i < ARRAY_SIZE(adg_clk_div_table); i++) { for (j = 0; j < ARRAY_SIZE(ssi_clk_mul_table); j++) { /* * this driver is assuming that * system word is 64fs (= 2 x 32bit) * see rsnd_ssi_init() */ main_rate = rate / adg_clk_div_table[i] * 32 * 2 * ssi_clk_mul_table[j]; ret = rsnd_adg_ssi_clk_try_start(&ssi->mod, main_rate); if (0 == ret) { ssi->cr_clk = FORCE | SWL_32 | SCKD | SWSD | CKDV(j); dev_dbg(dev, "%s[%d] outputs %u Hz\n", rsnd_mod_name(&ssi->mod), rsnd_mod_id(&ssi->mod), rate); return 0; } } } dev_err(dev, "unsupported clock rate\n"); return -EIO; }
static int rsnd_src_set_convert_timing_gen2(struct rsnd_mod *mod) { struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); struct rsnd_src *src = rsnd_mod_to_src(mod); u32 convert_rate = rsnd_src_convert_rate(src); int ret; if (convert_rate) ret = rsnd_adg_set_convert_clk_gen2(mod, io, runtime->rate, convert_rate); else ret = rsnd_adg_set_convert_timing_gen2(mod, io); return ret; }
static void rsnd_adg_get_timesel_ratio(struct rsnd_priv *priv, struct rsnd_dai_stream *io, unsigned int in_rate, unsigned int out_rate, u32 *in, u32 *out, u32 *en) { struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); unsigned int target_rate; u32 *target_val; u32 _in; u32 _out; u32 _en; /* default = SSI WS */ _in = _out = rsnd_adg_ssi_ws_timing_gen2(io); target_rate = 0; target_val = NULL; _en = 0; if (runtime->rate != in_rate) { target_rate = out_rate; target_val = &_out; } else if (runtime->rate != out_rate) { target_rate = in_rate; target_val = &_in; } if (target_rate) __rsnd_adg_get_timesel_ratio(priv, io, target_rate, target_val, &_en); if (in) *in = _in; if (out) *out = _out; if (en) *en = _en; }
/* * settting function */ u32 rsnd_get_adinr(struct rsnd_mod *mod) { struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); struct device *dev = rsnd_priv_to_dev(priv); u32 adinr = runtime->channels; switch (runtime->sample_bits) { case 16: adinr |= (8 << 16); break; case 32: adinr |= (0 << 16); break; default: dev_warn(dev, "not supported sample bits\n"); return 0; } return adinr; }
static int rsnd_scu_set_hpbif(struct rsnd_priv *priv, struct rsnd_mod *mod, struct rsnd_dai *rdai, struct rsnd_dai_stream *io) { struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); u32 adinr = runtime->channels; switch (runtime->sample_bits) { case 16: adinr |= OTBL_16; break; case 32: adinr |= OTBL_24; break; default: return -EIO; } rsnd_mod_write(mod, BUSIF_MODE, 1); rsnd_mod_write(mod, BUSIF_ADINR, adinr); return 0; }
/* * Gen1/Gen2 common functions */ int rsnd_src_ssiu_start(struct rsnd_mod *ssi_mod, int use_busif) { struct rsnd_dai_stream *io = rsnd_mod_to_io(ssi_mod); struct rsnd_dai *rdai = rsnd_io_to_rdai(io); struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); int ssi_id = rsnd_mod_id(ssi_mod); /* * SSI_MODE0 */ rsnd_mod_bset(ssi_mod, SSI_MODE0, (1 << ssi_id), !use_busif << ssi_id); /* * SSI_MODE1 */ if (rsnd_ssi_is_pin_sharing(ssi_mod)) { int shift = -1; switch (ssi_id) { case 1: shift = 0; break; case 2: shift = 2; break; case 4: shift = 16; break; } if (shift >= 0) rsnd_mod_bset(ssi_mod, SSI_MODE1, 0x3 << shift, rsnd_rdai_is_clk_master(rdai) ? 0x2 << shift : 0x1 << shift); } /* * DMA settings for SSIU */ if (use_busif) { u32 val = 0x76543210; u32 mask = ~0; rsnd_mod_write(ssi_mod, SSI_BUSIF_ADINR, rsnd_get_adinr(ssi_mod)); rsnd_mod_write(ssi_mod, SSI_BUSIF_MODE, 1); rsnd_mod_write(ssi_mod, SSI_CTRL, 0x1); mask <<= runtime->channels * 4; val = val & mask; switch (runtime->sample_bits) { case 16: val |= 0x67452301 & ~mask; break; case 32: val |= 0x76543210 & ~mask; break; } rsnd_mod_write(ssi_mod, BUSIF_DALIGN, 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; }