static void rsnd_dvc_volume_update(struct rsnd_dai_stream *io, struct rsnd_mod *mod) { struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod); u32 zcmcr = 0; u32 vrpdr = 0; u32 vrdbr = 0; int i; for (i = 0; i < dvc->mute.cfg.size; i++) zcmcr |= (!!dvc->mute.cfg.val[i]) << i; if (dvc->ren.val) { vrpdr = rsnd_dvc_get_vrpdr(dvc); vrdbr = rsnd_dvc_get_vrdbr(dvc); } /* Disable DVC Register access */ rsnd_mod_write(mod, DVC_DVUER, 0); /* Zero Cross Mute Function */ rsnd_mod_write(mod, DVC_ZCMCR, zcmcr); /* Volume Ramp Function */ rsnd_mod_write(mod, DVC_VRPDR, vrpdr); rsnd_mod_write(mod, DVC_VRDBR, vrdbr); /* add DVC_VRWTR here */ /* Digital Volume Function Parameter */ rsnd_dvc_volume_parameter(io, mod); /* Enable DVC Register access */ rsnd_mod_write(mod, DVC_DVUER, 1); }
static int rsnd_ssiu_init_gen2(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) { int ret; ret = rsnd_ssiu_init(mod, io, priv); if (ret < 0) return ret; if (rsnd_runtime_is_ssi_tdm(io)) { /* * TDM Extend Mode * see * rsnd_ssi_config_init() */ rsnd_mod_write(mod, SSI_MODE, 0x1); } if (rsnd_ssi_use_busif(io)) { rsnd_mod_write(mod, SSI_BUSIF_ADINR, rsnd_get_adinr_bit(mod, io) | (rsnd_io_is_play(io) ? rsnd_runtime_channel_after_ctu(io) : rsnd_runtime_channel_original(io))); rsnd_mod_write(mod, SSI_BUSIF_MODE, 1); rsnd_mod_write(mod, SSI_BUSIF_DALIGN, rsnd_get_dalign(mod, io)); } return 0; }
static void rsnd_mix_volume_parameter(struct rsnd_dai_stream *io, struct rsnd_mod *mod) { rsnd_mod_write(mod, MIX_MDBAR, 0); rsnd_mod_write(mod, MIX_MDBBR, 0); rsnd_mod_write(mod, MIX_MDBCR, 0); rsnd_mod_write(mod, MIX_MDBDR, 0); }
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 */ }
static int rsnd_src_stop_gen2(struct rsnd_mod *mod, struct rsnd_dai *rdai) { struct rsnd_src *src = rsnd_mod_to_src(mod); rsnd_mod_write(mod, SSI_CTRL, 0); rsnd_mod_write(mod, SRC_CTRL, 0); rsnd_dma_stop(rsnd_mod_to_dma(&src->mod)); return rsnd_src_stop(mod, rdai); }
static void rsnd_mix_volume_update(struct rsnd_dai_stream *io, struct rsnd_mod *mod) { /* Disable MIX dB setting */ rsnd_mod_write(mod, MIX_MDBER, 0); /* common volume parameter */ rsnd_mix_volume_parameter(io, mod); /* Enable MIX dB setting */ rsnd_mod_write(mod, MIX_MDBER, 1); }
static int rsnd_src_start_gen2(struct rsnd_mod *mod, struct rsnd_dai *rdai, struct rsnd_dai_stream *io) { struct rsnd_src *src = rsnd_mod_to_src(mod); rsnd_dma_start(rsnd_mod_to_dma(&src->mod)); rsnd_mod_write(mod, SSI_CTRL, 0x1); rsnd_mod_write(mod, SRC_CTRL, 0x11); return rsnd_src_start(mod, rdai, io); }
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_dvc_volume_parameter(struct rsnd_dai_stream *io, struct rsnd_mod *mod) { struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod); u32 val[RSND_MAX_CHANNELS]; int i; /* Enable Ramp */ if (dvc->ren.val) for (i = 0; i < RSND_MAX_CHANNELS; i++) val[i] = dvc->volume.cfg.max; else for (i = 0; i < RSND_MAX_CHANNELS; i++) val[i] = dvc->volume.val[i]; /* Enable Digital Volume */ rsnd_mod_write(mod, DVC_VOL0R, val[0]); rsnd_mod_write(mod, DVC_VOL1R, val[1]); rsnd_mod_write(mod, DVC_VOL2R, val[2]); rsnd_mod_write(mod, DVC_VOL3R, val[3]); rsnd_mod_write(mod, DVC_VOL4R, val[4]); rsnd_mod_write(mod, DVC_VOL5R, val[5]); rsnd_mod_write(mod, DVC_VOL6R, val[6]); rsnd_mod_write(mod, DVC_VOL7R, val[7]); }
static int rsnd_ssiu_stop_gen2(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) { if (!rsnd_ssi_use_busif(io)) return 0; rsnd_mod_write(mod, SSI_CTRL, 0); if (rsnd_ssi_multi_slaves_runtime(io)) rsnd_mod_write(mod, SSI_CONTROL, 0); return 0; }
static int rsnd_src_start_gen2(struct rsnd_mod *mod, struct rsnd_dai *rdai) { struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); struct rsnd_src *src = rsnd_mod_to_src(mod); u32 val = rsnd_io_to_mod_dvc(io) ? 0x01 : 0x11; rsnd_dma_start(rsnd_mod_to_dma(&src->mod)); rsnd_mod_write(mod, SSI_CTRL, 0x1); rsnd_mod_write(mod, SRC_CTRL, val); return rsnd_src_start(mod, rdai); }
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; }
static void rsnd_src_irq_ctrol_gen2(struct rsnd_mod *mod, int enable) { struct rsnd_src *src = rsnd_mod_to_src(mod); u32 sys_int_val, int_val, sys_int_mask; int irq = src->info->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_gen2() */ if ((irq <= 0) || !enable) { sys_int_val = 0; int_val = 0; } 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); }
/* * 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)) { if (rsnd_ssi_clk_from_parent(ssi)) rsnd_ssi_hw_start(ssi->parent, rdai, io); else rsnd_ssi_master_clk_start(ssi, io); } } 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_start(struct rsnd_mod *mod, struct rsnd_dai *rdai) { struct rsnd_src *src = rsnd_mod_to_src(mod); /* * Cancel the initialization and operate the SRC function * see rsnd_src_set_convert_rate() */ rsnd_mod_write(mod, SRC_SRCIR, 0); if (rsnd_src_convert_rate(src)) rsnd_mod_write(mod, SRC_ROUTE_MODE0, 1); return 0; }
static void rsnd_mix_volume_init(struct rsnd_dai_stream *io, struct rsnd_mod *mod) { rsnd_mod_write(mod, MIX_MIXIR, 1); /* General Information */ rsnd_mod_write(mod, MIX_ADINR, rsnd_runtime_channel_after_ctu(io)); /* volume step */ rsnd_mod_write(mod, MIX_MIXMR, 0); rsnd_mod_write(mod, MIX_MVPDR, 0); /* common volume parameter */ rsnd_mix_volume_parameter(io, mod); rsnd_mod_write(mod, MIX_MIXIR, 0); }
int rsnd_src_ssiu_stop(struct rsnd_mod *ssi_mod) { /* * DMA settings for SSIU */ rsnd_mod_write(ssi_mod, SSI_CTRL, 0); return 0; }
static void rsnd_ssi_hw_stop(struct rsnd_ssi *ssi) { struct rsnd_priv *priv = rsnd_mod_to_priv(&ssi->mod); struct rsnd_dai_stream *io = rsnd_mod_to_io(&ssi->mod); struct rsnd_dai *rdai = rsnd_io_to_rdai(io); struct device *dev = rsnd_priv_to_dev(priv); u32 cr; if (0 == ssi->usrcnt) /* stop might be called without start */ return; ssi->usrcnt--; if (0 == ssi->usrcnt) { /* * disable all IRQ, * and, wait all data was sent */ cr = ssi->cr_own | ssi->cr_clk; rsnd_mod_write(&ssi->mod, SSICR, cr | EN); rsnd_ssi_status_check(&ssi->mod, DIRQ); /* * disable SSI, * and, wait idle state */ rsnd_mod_write(&ssi->mod, SSICR, cr); /* disabled all */ rsnd_ssi_status_check(&ssi->mod, IIRQ); if (rsnd_rdai_is_clk_master(rdai)) { if (rsnd_ssi_clk_from_parent(ssi)) rsnd_ssi_hw_stop(ssi->parent); else rsnd_ssi_master_clk_stop(ssi); } rsnd_mod_hw_stop(&ssi->mod); } dev_dbg(dev, "%s[%d] hw stopped\n", rsnd_mod_name(&ssi->mod), rsnd_mod_id(&ssi->mod)); }
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_src_stop_gen2(struct rsnd_mod *mod) { rsnd_src_irq_disable_gen2(mod); rsnd_mod_write(mod, SRC_CTRL, 0); rsnd_src_error_record_gen2(mod); return rsnd_src_stop(mod); }
static void rsnd_mix_volume_parameter(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 rsnd_mix *mix = rsnd_mod_to_mix(mod); u32 volA = rsnd_mix_get_vol(mix, A); u32 volB = rsnd_mix_get_vol(mix, B); u32 volC = rsnd_mix_get_vol(mix, C); u32 volD = rsnd_mix_get_vol(mix, D); dev_dbg(dev, "MIX A/B/C/D = %02x/%02x/%02x/%02x\n", volA, volB, volC, volD); rsnd_mod_write(mod, MIX_MDBAR, volA); rsnd_mod_write(mod, MIX_MDBBR, volB); rsnd_mod_write(mod, MIX_MDBCR, volC); rsnd_mod_write(mod, MIX_MDBDR, volD); }
/* * Gen2 functions */ static int rsnd_src_set_convert_rate_gen2(struct rsnd_mod *mod, struct rsnd_dai *rdai) { int ret; ret = rsnd_src_set_convert_rate(mod, rdai); if (ret < 0) return ret; rsnd_mod_write(mod, SSI_BUSIF_ADINR, rsnd_get_adinr(mod)); rsnd_mod_write(mod, SSI_BUSIF_MODE, 1); rsnd_mod_write(mod, SRC_SRCCR, 0x00011110); rsnd_mod_write(mod, SRC_BSDSR, 0x01800000); rsnd_mod_write(mod, SRC_BSISR, 0x00100060); return 0; }
static int rsnd_src_start(struct rsnd_mod *mod) { /* * Cancel the initialization and operate the SRC function * see rsnd_src_init() */ rsnd_mod_write(mod, SRC_SRCIR, 0); return 0; }
static int rsnd_src_stop(struct rsnd_mod *mod, struct rsnd_dai *rdai) { struct rsnd_src *src = rsnd_mod_to_src(mod); if (rsnd_src_convert_rate(src)) rsnd_mod_write(mod, SRC_ROUTE_MODE0, 0); return 0; }
static void rsnd_ssi_record_error(struct rsnd_ssi *ssi, u32 status) { /* under/over flow error */ if (status & (UIRQ | OIRQ)) { ssi->err++; /* clear error status */ rsnd_mod_write(&ssi->mod, SSISR, 0); } }
static void rsnd_mix_volume_init(struct rsnd_dai_stream *io, struct rsnd_mod *mod) { struct rsnd_mix *mix = rsnd_mod_to_mix(mod); rsnd_mod_write(mod, MIX_MIXIR, 1); /* General Information */ rsnd_mod_write(mod, MIX_ADINR, rsnd_runtime_channel_after_ctu(io)); /* volume step */ rsnd_mod_write(mod, MIX_MIXMR, rsnd_kctrl_vals(mix->ren)); rsnd_mod_write(mod, MIX_MVPDR, rsnd_kctrl_vals(mix->rup) << 8 | rsnd_kctrl_vals(mix->rdw)); /* common volume parameter */ rsnd_mix_volume_parameter(io, mod); rsnd_mod_write(mod, MIX_MIXIR, 0); }
static int rsnd_src_set_convert_rate_gen1(struct rsnd_mod *mod, struct rsnd_dai *rdai) { int ret; ret = rsnd_src_set_convert_rate(mod, rdai); if (ret < 0) return ret; /* Select SRC mode (fixed value) */ rsnd_mod_write(mod, SRC_SRCCR, 0x00010110); /* Set the restriction value of the FS ratio (98%) */ rsnd_mod_write(mod, SRC_MNFSR, rsnd_mod_read(mod, SRC_IFSVR) / 100 * 98); /* no SRC_BFSSR settings, since SRC_SRCCR::BUFMD is 0 */ return 0; }
/* * Gen2 functions */ static int rsnd_src_set_convert_rate_gen2(struct rsnd_mod *mod, struct rsnd_dai *rdai, struct rsnd_dai_stream *io) { int ret; ret = rsnd_src_set_convert_rate(mod, rdai, io); if (ret < 0) return ret; rsnd_mod_write(mod, SSI_BUSIF_ADINR, rsnd_mod_read(mod, SRC_ADINR)); rsnd_mod_write(mod, SSI_BUSIF_MODE, rsnd_mod_read(mod, SRC_BUSIF_MODE)); rsnd_mod_write(mod, SRC_SRCCR, 0x00011110); rsnd_mod_write(mod, SRC_BSDSR, 0x01800000); rsnd_mod_write(mod, SRC_BSISR, 0x00100060); return 0; }
int rsnd_src_enable_ssi_irq(struct rsnd_mod *ssi_mod, struct rsnd_dai *rdai) { struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod); /* enable PIO interrupt if Gen2 */ if (rsnd_is_gen2(priv)) rsnd_mod_write(ssi_mod, INT_ENABLE, 0x0f000000); return 0; }