/* * SSI PIO */ static void rsnd_ssi_parent_attach(struct rsnd_mod *mod, struct rsnd_dai_stream *io) { struct rsnd_dai *rdai = rsnd_io_to_rdai(io); struct rsnd_priv *priv = rsnd_mod_to_priv(mod); if (!__rsnd_ssi_is_pin_sharing(mod)) return; if (!rsnd_rdai_is_clk_master(rdai)) return; switch (rsnd_mod_id(mod)) { case 1: case 2: rsnd_dai_connect(rsnd_ssi_mod_get(priv, 0), io, RSND_MOD_SSIP); break; case 4: rsnd_dai_connect(rsnd_ssi_mod_get(priv, 3), io, RSND_MOD_SSIP); break; case 8: rsnd_dai_connect(rsnd_ssi_mod_get(priv, 7), io, RSND_MOD_SSIP); break; } }
/* * SSI mod common functions */ static int rsnd_ssi_init(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); struct rsnd_dai *rdai = rsnd_io_to_rdai(io); 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_io_is_play(io)) cr |= TRMD; /* * set ssi parameter */ ssi->cr_own = cr; ssi->err = -1; /* ignore 1st error */ 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 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); }
/* * ssi mod function */ static void rsnd_ssi_connect(struct rsnd_mod *mod, struct rsnd_dai_stream *io) { struct rsnd_dai *rdai = rsnd_io_to_rdai(io); enum rsnd_mod_type types[] = { RSND_MOD_SSI, RSND_MOD_SSIM1, RSND_MOD_SSIM2, RSND_MOD_SSIM3, }; enum rsnd_mod_type type; int i; /* try SSI -> SSIM1 -> SSIM2 -> SSIM3 */ for (i = 0; i < ARRAY_SIZE(types); i++) { type = types[i]; if (!rsnd_io_to_mod(io, type)) { rsnd_dai_connect(mod, io, type); rsnd_set_slot(rdai, 2 * (i + 1), (i + 1)); return; } } }
static int rsnd_ssiu_init(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) { struct rsnd_dai *rdai = rsnd_io_to_rdai(io); u32 multi_ssi_slaves = rsnd_ssi_multi_slaves_runtime(io); int use_busif = rsnd_ssi_use_busif(io); int id = rsnd_mod_id(mod); u32 mask1, val1; u32 mask2, val2; /* clear status */ switch (id) { case 0: case 1: case 2: case 3: case 4: rsnd_mod_write(mod, SSI_SYS_STATUS0, 0xf << (id * 4)); rsnd_mod_write(mod, SSI_SYS_STATUS2, 0xf << (id * 4)); rsnd_mod_write(mod, SSI_SYS_STATUS4, 0xf << (id * 4)); rsnd_mod_write(mod, SSI_SYS_STATUS6, 0xf << (id * 4)); break; case 9: rsnd_mod_write(mod, SSI_SYS_STATUS1, 0xf << 4); rsnd_mod_write(mod, SSI_SYS_STATUS3, 0xf << 4); rsnd_mod_write(mod, SSI_SYS_STATUS5, 0xf << 4); rsnd_mod_write(mod, SSI_SYS_STATUS7, 0xf << 4); break; } /* * SSI_MODE0 */ rsnd_mod_bset(mod, SSI_MODE0, (1 << id), !use_busif << id); /* * SSI_MODE1 */ mask1 = (1 << 4) | (1 << 20); /* mask sync bit */ mask2 = (1 << 4); /* mask sync bit */ val1 = val2 = 0; if (id == 8) { /* * SSI8 pin is sharing with SSI7, nothing to do. */ } else if (rsnd_ssi_is_pin_sharing(io)) { int shift = -1; switch (id) { case 1: shift = 0; break; case 2: shift = 2; break; case 4: shift = 16; break; default: return -EINVAL; } mask1 |= 0x3 << shift; val1 = rsnd_rdai_is_clk_master(rdai) ? 0x2 << shift : 0x1 << shift; } else if (multi_ssi_slaves) { mask2 |= 0x00000007; mask1 |= 0x0000000f; switch (multi_ssi_slaves) { case 0x0206: /* SSI0/1/2/9 */ val2 = (1 << 4) | /* SSI0129 sync */ (rsnd_rdai_is_clk_master(rdai) ? 0x2 : 0x1); /* fall through */ case 0x0006: /* SSI0/1/2 */ val1 = rsnd_rdai_is_clk_master(rdai) ? 0xa : 0x5; if (!val2) /* SSI012 sync */ val1 |= (1 << 4); } } rsnd_mod_bset(mod, SSI_MODE1, mask1, val1); rsnd_mod_bset(mod, SSI_MODE2, mask2, val2); 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; }
static int rsnd_ssi_master_clk_start(struct rsnd_mod *mod, struct rsnd_dai_stream *io) { struct rsnd_priv *priv = rsnd_io_to_priv(io); struct device *dev = rsnd_priv_to_dev(priv); 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); int chan = rsnd_runtime_channel_for_ssi(io); int j, ret; int ssi_clk_mul_table[] = { 1, 2, 4, 8, 16, 6, 12, }; unsigned int main_rate; unsigned int rate = rsnd_io_is_play(io) ? rsnd_src_get_out_rate(priv, io) : rsnd_src_get_in_rate(priv, io); if (!rsnd_rdai_is_clk_master(rdai)) return 0; if (ssi_parent_mod && !rsnd_ssi_is_parent(mod, io)) return 0; if (rsnd_ssi_is_multi_slave(mod, io)) return 0; if (ssi->usrcnt > 1) { if (ssi->rate != rate) { dev_err(dev, "SSI parent/child should use same rate\n"); return -EINVAL; } return 0; } /* * Find best clock, and try to start ADG */ for (j = 0; j < ARRAY_SIZE(ssi_clk_mul_table); j++) { /* * this driver is assuming that * system word is 32bit x chan * see rsnd_ssi_init() */ main_rate = rate * 32 * chan * ssi_clk_mul_table[j]; ret = rsnd_adg_ssi_clk_try_start(mod, main_rate); if (0 == ret) { ssi->cr_clk = FORCE | SWL_32 | SCKD | SWSD | CKDV(j); ssi->wsr = CONT; ssi->rate = rate; dev_dbg(dev, "%s[%d] outputs %u Hz\n", rsnd_mod_name(mod), rsnd_mod_id(mod), rate); return 0; } } dev_err(dev, "unsupported clock rate\n"); return -EIO; }