diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 81c789f8e9a648..599dfb69555a15 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -211,6 +211,7 @@ enum rsnd_mod_type { RSND_MOD_CMD, RSND_MOD_SRC, RSND_MOD_SSIU, + RSND_MOD_SSIP, /* SSI parent */ RSND_MOD_SSI, RSND_MOD_MAX, }; @@ -339,6 +340,7 @@ struct rsnd_dai_stream { }; #define rsnd_io_to_mod(io, i) ((i) < RSND_MOD_MAX ? (io)->mod[(i)] : NULL) #define rsnd_io_to_mod_ssi(io) rsnd_io_to_mod((io), RSND_MOD_SSI) +#define rsnd_io_to_mod_ssip(io) rsnd_io_to_mod((io), RSND_MOD_SSIP) #define rsnd_io_to_mod_src(io) rsnd_io_to_mod((io), RSND_MOD_SRC) #define rsnd_io_to_mod_ctu(io) rsnd_io_to_mod((io), RSND_MOD_CTU) #define rsnd_io_to_mod_mix(io) rsnd_io_to_mod((io), RSND_MOD_MIX) diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index bb08d6624d7de6..3e814711f301e4 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -67,7 +67,9 @@ struct rsnd_ssi { u32 cr_own; u32 cr_clk; + u32 cr_mode; int chan; + int rate; int err; unsigned int usrcnt; }; @@ -82,9 +84,9 @@ struct rsnd_ssi { #define rsnd_ssi_nr(priv) ((priv)->ssi_nr) #define rsnd_mod_to_ssi(_mod) container_of((_mod), struct rsnd_ssi, mod) #define rsnd_ssi_pio_available(ssi) ((ssi)->info->irq > 0) -#define rsnd_ssi_parent(ssi) ((ssi)->parent) #define rsnd_ssi_mode_flags(p) ((p)->info->flags) #define rsnd_ssi_dai_id(ssi) ((ssi)->info->dai_id) +#define rsnd_ssi_is_parent(ssi, io) ((ssi) == rsnd_io_to_mod_ssip(io)) #define rsnd_ssi_of_node(priv) \ of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,ssi") @@ -168,7 +170,9 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi, 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); + struct rsnd_dai *rdai = rsnd_io_to_rdai(io); struct rsnd_mod *mod = rsnd_mod_get(ssi); + struct rsnd_mod *ssi_parent_mod = rsnd_io_to_mod_ssip(io); int j, ret; int ssi_clk_mul_table[] = { 1, 2, 4, 8, 16, 6, 12, @@ -176,6 +180,21 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi, unsigned int main_rate; unsigned int rate = rsnd_src_get_ssi_rate(priv, io, runtime); + if (!rsnd_rdai_is_clk_master(rdai)) + return 0; + + if (ssi_parent_mod && !rsnd_ssi_is_parent(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 */ @@ -193,6 +212,10 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi, ssi->cr_clk = FORCE | SWL_32 | SCKD | SWSD | CKDV(j); + ssi->rate = rate; + + rsnd_mod_write(mod, SSIWSR, CONT); + dev_dbg(dev, "%s[%d] outputs %u Hz\n", rsnd_mod_name(mod), rsnd_mod_id(mod), rate); @@ -205,113 +228,26 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi, return -EIO; } -static void rsnd_ssi_master_clk_stop(struct rsnd_ssi *ssi) -{ - struct rsnd_mod *mod = rsnd_mod_get(ssi); - - ssi->cr_clk = 0; - rsnd_adg_ssi_clk_stop(mod); -} - -static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi, - struct rsnd_dai_stream *io) +static void rsnd_ssi_master_clk_stop(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); struct rsnd_mod *mod = rsnd_mod_get(ssi); - u32 cr_mode; - u32 cr; - - if (0 == ssi->usrcnt) { - rsnd_mod_power_on(mod); - - if (rsnd_rdai_is_clk_master(rdai)) { - struct rsnd_ssi *ssi_parent = rsnd_ssi_parent(ssi); - - if (ssi_parent) - rsnd_ssi_hw_start(ssi_parent, io); - else - rsnd_ssi_master_clk_start(ssi, io); - } - } - - 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 */ - } - - cr = ssi->cr_own | - ssi->cr_clk | - cr_mode | - EN; - - rsnd_mod_write(mod, SSICR, cr); - - /* enable WS continue */ - if (rsnd_rdai_is_clk_master(rdai)) - rsnd_mod_write(mod, SSIWSR, CONT); - - /* clear error status */ - rsnd_ssi_status_clear(mod); + struct rsnd_mod *ssi_parent_mod = rsnd_io_to_mod_ssip(io); - ssi->usrcnt++; - - dev_dbg(dev, "%s[%d] hw started\n", - rsnd_mod_name(mod), rsnd_mod_id(mod)); -} - -static void rsnd_ssi_hw_stop(struct rsnd_dai_stream *io, struct rsnd_ssi *ssi) -{ - struct rsnd_mod *mod = rsnd_mod_get(ssi); - struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - struct rsnd_dai *rdai = rsnd_io_to_rdai(io); - struct device *dev = rsnd_priv_to_dev(priv); - u32 cr; - - if (0 == ssi->usrcnt) { - dev_err(dev, "%s called without starting\n", __func__); + if (!rsnd_rdai_is_clk_master(rdai)) 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(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); - if (rsnd_rdai_is_clk_master(rdai)) { - struct rsnd_ssi *ssi_parent = rsnd_ssi_parent(ssi); - - if (ssi_parent) - rsnd_ssi_hw_stop(io, ssi_parent); - else - rsnd_ssi_master_clk_stop(ssi); - } + if (ssi_parent_mod && !rsnd_ssi_is_parent(mod, io)) + return; - rsnd_mod_power_off(mod); + if (ssi->usrcnt > 1) + return; - ssi->chan = 0; - } + ssi->cr_clk = 0; + ssi->rate = 0; - dev_dbg(dev, "%s[%d] hw stopped\n", - rsnd_mod_name(mod), rsnd_mod_id(mod)); + rsnd_adg_ssi_clk_stop(mod); } /* @@ -325,6 +261,18 @@ static int rsnd_ssi_init(struct rsnd_mod *mod, struct rsnd_dai *rdai = rsnd_io_to_rdai(io); struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); u32 cr; + int ret; + + ssi->usrcnt++; + + rsnd_mod_power_on(mod); + + ret = rsnd_ssi_master_clk_start(ssi, io); + if (ret < 0) + return ret; + + if (rsnd_ssi_is_parent(mod, io)) + return 0; cr = FORCE; @@ -359,12 +307,24 @@ static int rsnd_ssi_init(struct rsnd_mod *mod, if (rsnd_io_is_play(io)) cr |= TRMD; - /* - * set ssi parameter - */ ssi->cr_own = cr; + + if (rsnd_ssi_is_dma_mode(mod)) { + cr = UIEN | OIEN | /* over/under run */ + DMEN; /* DMA : enable DMA */ + } else { + cr = DIEN; /* PIO : enable Data interrupt */ + } + + ssi->cr_mode = cr; + ssi->err = -1; /* ignore 1st error */ + /* clear error status */ + rsnd_ssi_status_clear(mod); + + rsnd_ssi_irq_enable(mod); + return 0; } @@ -375,6 +335,9 @@ static int rsnd_ssi_quit(struct rsnd_mod *mod, struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); struct device *dev = rsnd_priv_to_dev(priv); + if (rsnd_ssi_is_parent(mod, io)) + goto rsnd_ssi_quit_end; + 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); @@ -382,6 +345,19 @@ static int rsnd_ssi_quit(struct rsnd_mod *mod, ssi->cr_own = 0; ssi->err = 0; + rsnd_ssi_irq_disable(mod); + +rsnd_ssi_quit_end: + rsnd_ssi_master_clk_stop(ssi, io); + + rsnd_mod_power_off(mod); + + ssi->usrcnt--; + + if (ssi->usrcnt < 0) + dev_err(dev, "%s[%d] usrcnt error\n", + rsnd_mod_name(mod), rsnd_mod_id(mod)); + return 0; } @@ -391,14 +367,13 @@ static int rsnd_ssi_hw_params(struct rsnd_mod *mod, struct snd_pcm_hw_params *params) { struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); - struct rsnd_ssi *ssi_parent = rsnd_ssi_parent(ssi); int chan = params_channels(params); /* * Already working. * It will happen if SSI has parent/child connection. */ - if (ssi->usrcnt) { + if (ssi->usrcnt > 1) { /* * it is error if child <-> parent SSI uses * different channels. @@ -407,11 +382,7 @@ static int rsnd_ssi_hw_params(struct rsnd_mod *mod, return -EIO; } - /* It will be removed on rsnd_ssi_hw_stop */ ssi->chan = chan; - if (ssi_parent) - return rsnd_ssi_hw_params(rsnd_mod_get(ssi_parent), io, - substream, params); return 0; } @@ -432,15 +403,59 @@ static u32 rsnd_ssi_record_error(struct rsnd_ssi *ssi) return status; } +static int __rsnd_ssi_start(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) +{ + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + u32 cr; + + cr = ssi->cr_own | + ssi->cr_clk | + ssi->cr_mode | + EN; + + rsnd_mod_write(mod, SSICR, cr); + + return 0; +} + static int rsnd_ssi_start(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) +{ + /* + * no limit to start + * see also + * rsnd_ssi_stop + * rsnd_ssi_interrupt + */ + return __rsnd_ssi_start(mod, io, priv); +} + +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; + + /* + * disable all IRQ, + * and, wait all data was sent + */ + cr = ssi->cr_own | + ssi->cr_clk; - rsnd_ssi_hw_start(ssi, io); + rsnd_mod_write(mod, SSICR, cr | EN); + rsnd_ssi_status_check(mod, DIRQ); - rsnd_ssi_irq_enable(mod); + /* + * disable SSI, + * and, wait idle state + */ + rsnd_mod_write(mod, SSICR, cr); /* disabled all */ + rsnd_ssi_status_check(mod, IIRQ); return 0; } @@ -451,13 +466,16 @@ static int rsnd_ssi_stop(struct rsnd_mod *mod, { struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); - rsnd_ssi_irq_disable(mod); - - rsnd_ssi_record_error(ssi); - - rsnd_ssi_hw_stop(io, ssi); + /* + * don't stop if not last user + * see also + * rsnd_ssi_start + * rsnd_ssi_interrupt + */ + if (ssi->usrcnt > 1) + return 0; - return 0; + return __rsnd_ssi_stop(mod, io, priv); } static void __rsnd_ssi_interrupt(struct rsnd_mod *mod, @@ -505,8 +523,8 @@ static void __rsnd_ssi_interrupt(struct rsnd_mod *mod, dev_dbg(dev, "%s[%d] restart\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); - rsnd_ssi_stop(mod, io, priv); - rsnd_ssi_start(mod, io, priv); + __rsnd_ssi_stop(mod, io, priv); + __rsnd_ssi_start(mod, io, priv); } if (ssi->err > 1024) { @@ -535,6 +553,27 @@ static irqreturn_t rsnd_ssi_interrupt(int irq, void *data) /* * SSI PIO */ +static void rsnd_ssi_parent_attach(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) +{ + if (!__rsnd_ssi_is_pin_sharing(mod)) + 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; + } +} + static int rsnd_ssi_common_probe(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) @@ -543,6 +582,8 @@ static int rsnd_ssi_common_probe(struct rsnd_mod *mod, struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); int ret; + rsnd_ssi_parent_attach(mod, io, priv); + ret = rsnd_ssiu_attach(io, mod); if (ret < 0) return ret; @@ -679,28 +720,6 @@ int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod) return !!(rsnd_ssi_mode_flags(ssi) & RSND_SSI_CLK_PIN_SHARE); } -static void rsnd_ssi_parent_setup(struct rsnd_priv *priv, struct rsnd_ssi *ssi) -{ - struct rsnd_mod *mod = rsnd_mod_get(ssi); - - if (!__rsnd_ssi_is_pin_sharing(mod)) - return; - - switch (rsnd_mod_id(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_of_parse_ssi(struct platform_device *pdev, const struct rsnd_of_data *of_data, struct rsnd_priv *priv) @@ -810,8 +829,6 @@ int rsnd_ssi_probe(struct platform_device *pdev, RSND_MOD_SSI, i); if (ret) return ret; - - rsnd_ssi_parent_setup(priv, ssi); } return 0;