diff options
author | André Fabian Silva Delgado <emulatorman@parabola.nu> | 2016-03-25 03:53:42 -0300 |
---|---|---|
committer | André Fabian Silva Delgado <emulatorman@parabola.nu> | 2016-03-25 03:53:42 -0300 |
commit | 03dd4cb26d967f9588437b0fc9cc0e8353322bb7 (patch) | |
tree | fa581f6dc1c0596391690d1f67eceef3af8246dc /sound/soc/sh/rcar/ssi.c | |
parent | d4e493caf788ef44982e131ff9c786546904d934 (diff) |
Linux-libre 4.5-gnu
Diffstat (limited to 'sound/soc/sh/rcar/ssi.c')
-rw-r--r-- | sound/soc/sh/rcar/ssi.c | 755 |
1 files changed, 442 insertions, 313 deletions
diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 1427ec21b..7ee89da4d 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -24,7 +24,9 @@ #define OIEN (1 << 26) /* Overflow Interrupt Enable */ #define IIEN (1 << 25) /* Idle Mode Interrupt Enable */ #define DIEN (1 << 24) /* Data Interrupt Enable */ - +#define CHNL_4 (1 << 22) /* Channels */ +#define CHNL_6 (2 << 22) /* Channels */ +#define CHNL_8 (3 << 22) /* Channels */ #define DWL_8 (0 << 19) /* Data Word Length */ #define DWL_16 (1 << 19) /* Data Word Length */ #define DWL_18 (2 << 19) /* Data Word Length */ @@ -39,6 +41,7 @@ #define SCKP (1 << 13) /* Serial Bit Clock Polarity */ #define SWSP (1 << 12) /* Serial WS Polarity */ #define SDTA (1 << 10) /* Serial Data Alignment */ +#define PDTA (1 << 9) /* Parallel Data Alignment */ #define DEL (1 << 8) /* Serial Data Delay */ #define CKDV(v) (v << 4) /* Serial Clock Division Ratio */ #define TRMD (1 << 1) /* Transmit/Receive Mode Select */ @@ -56,35 +59,44 @@ * SSIWSR */ #define CONT (1 << 8) /* WS Continue Function */ +#define WS_MODE (1 << 0) /* WS Mode */ #define SSI_NAME "ssi" struct rsnd_ssi { - struct rsnd_ssi_platform_info *info; /* rcar_snd.h */ struct rsnd_ssi *parent; struct rsnd_mod mod; + struct rsnd_mod *dma; + u32 flags; u32 cr_own; u32 cr_clk; + u32 cr_mode; + u32 wsr; int chan; + int rate; int err; + int irq; unsigned int usrcnt; }; +/* flags */ +#define RSND_SSI_CLK_PIN_SHARE (1 << 0) +#define RSND_SSI_NO_BUSIF (1 << 1) /* SSI+DMA without BUSIF */ + #define for_each_rsnd_ssi(pos, priv, i) \ for (i = 0; \ (i < rsnd_ssi_nr(priv)) && \ ((pos) = ((struct rsnd_ssi *)(priv)->ssi + i)); \ i++) +#define rsnd_ssi_get(priv, id) ((struct rsnd_ssi *)(priv->ssi) + id) +#define rsnd_ssi_to_dma(mod) ((ssi)->dma) #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_of_node(priv) \ - of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,ssi") +#define rsnd_ssi_mode_flags(p) ((p)->flags) +#define rsnd_ssi_is_parent(ssi, io) ((ssi) == rsnd_io_to_mod_ssip(io)) +#define rsnd_ssi_is_multi_slave(ssi, io) ((mod) != rsnd_io_to_mod_ssi(io)) int rsnd_ssi_use_busif(struct rsnd_dai_stream *io) { @@ -103,6 +115,16 @@ int rsnd_ssi_use_busif(struct rsnd_dai_stream *io) return use_busif; } +static void rsnd_ssi_status_clear(struct rsnd_mod *mod) +{ + rsnd_mod_write(mod, SSISR, 0); +} + +static u32 rsnd_ssi_status_get(struct rsnd_mod *mod) +{ + return rsnd_mod_read(mod, SSISR); +} + static void rsnd_ssi_status_check(struct rsnd_mod *mod, u32 bit) { @@ -112,7 +134,7 @@ static void rsnd_ssi_status_check(struct rsnd_mod *mod, int i; for (i = 0; i < 1024; i++) { - status = rsnd_mod_read(mod, SSISR); + status = rsnd_ssi_status_get(mod); if (status & bit) return; @@ -122,13 +144,79 @@ static void rsnd_ssi_status_check(struct rsnd_mod *mod, dev_warn(dev, "status check failed\n"); } +static int rsnd_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 */ + rsnd_mod_write(ssi_mod, SSI_INT_ENABLE, + rsnd_ssi_is_dma_mode(ssi_mod) ? + 0x0e000000 : 0x0f000000); + + return 0; +} + +static int rsnd_ssi_irq_disable(struct rsnd_mod *ssi_mod) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod); + + if (rsnd_is_gen1(priv)) + return 0; + + /* disable SSI interrupt if Gen2 */ + rsnd_mod_write(ssi_mod, SSI_INT_ENABLE, 0x00000000); + + return 0; +} + +u32 rsnd_ssi_multi_slaves(struct rsnd_dai_stream *io) +{ + struct rsnd_mod *mod; + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + struct rsnd_priv *priv = rsnd_io_to_priv(io); + struct device *dev = rsnd_priv_to_dev(priv); + enum rsnd_mod_type types[] = { + RSND_MOD_SSIM1, + RSND_MOD_SSIM2, + RSND_MOD_SSIM3, + }; + int i, mask; + + switch (runtime->channels) { + case 2: /* Multi channel is not needed for Stereo */ + return 0; + case 6: + break; + default: + dev_err(dev, "unsupported channel\n"); + return 0; + } + + mask = 0; + for (i = 0; i < ARRAY_SIZE(types); i++) { + mod = rsnd_io_to_mod(io, types[i]); + if (!mod) + continue; + + mask |= 1 << rsnd_mod_id(mod); + } + + return mask; +} + 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); + 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 slots = rsnd_get_slot_width(io); int j, ret; int ssi_clk_mul_table[] = { 1, 2, 4, 8, 16, 6, 12, @@ -136,6 +224,24 @@ 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 (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 */ @@ -143,15 +249,18 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi, /* * this driver is assuming that - * system word is 64fs (= 2 x 32bit) + * system word is 32bit x slots * see rsnd_ssi_init() */ - main_rate = rate * 32 * 2 * ssi_clk_mul_table[j]; + main_rate = rate * 32 * slots * 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), @@ -165,113 +274,91 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi, return -EIO; } -static void rsnd_ssi_master_clk_stop(struct rsnd_ssi *ssi) +static void rsnd_ssi_master_clk_stop(struct rsnd_ssi *ssi, + struct rsnd_dai_stream *io) { + 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); + + 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; - 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 int rsnd_ssi_config_init(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); + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + u32 cr_own; u32 cr_mode; - u32 cr; + u32 wsr; + int is_tdm; - if (0 == ssi->usrcnt) { - rsnd_mod_power_on(mod); + is_tdm = (rsnd_get_slot_width(io) >= 6) ? 1 : 0; - if (rsnd_rdai_is_clk_master(rdai)) { - struct rsnd_ssi *ssi_parent = rsnd_ssi_parent(ssi); + /* + * always use 32bit system word. + * see also rsnd_ssi_master_clk_enable() + */ + cr_own = FORCE | SWL_32 | PDTA; - if (ssi_parent) - rsnd_ssi_hw_start(ssi_parent, io); - else - rsnd_ssi_master_clk_start(ssi, io); - } + 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; + default: + return -EINVAL; } - if (rsnd_ssi_is_dma_mode(mod)) { + if (rsnd_ssi_is_dma_mode(rsnd_mod_get(ssi))) { 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_mod_write(mod, SSISR, 0); - - 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__); - return; + /* + * TDM Extend Mode + * see + * rsnd_ssiu_init_gen2() + */ + wsr = ssi->wsr; + if (is_tdm) { + wsr |= WS_MODE; + cr_own |= CHNL_8; } - 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); + ssi->cr_own = cr_own; + ssi->cr_mode = cr_mode; + ssi->wsr = wsr; - /* - * 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); - } - - rsnd_mod_power_off(mod); - - ssi->chan = 0; - } - - dev_dbg(dev, "%s[%d] hw stopped\n", - rsnd_mod_name(mod), rsnd_mod_id(mod)); + return 0; } /* @@ -282,49 +369,30 @@ 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 *rdai = rsnd_io_to_rdai(io); - struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); - u32 cr; + int ret; + + ssi->usrcnt++; - cr = FORCE; + rsnd_mod_power_on(mod); - /* - * always use 32bit system word for easy clock calculation. - * see also rsnd_ssi_master_clk_enable() - */ - cr |= SWL_32; + ret = rsnd_ssi_master_clk_start(ssi, io); + if (ret < 0) + return ret; - /* - * 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 (rsnd_ssi_is_parent(mod, io)) + return 0; - 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; + ret = rsnd_ssi_config_init(ssi, io); + if (ret < 0) + return ret; - /* - * set ssi parameter - */ - ssi->cr_own = cr; ssi->err = -1; /* ignore 1st error */ + /* clear error status */ + rsnd_ssi_status_clear(mod); + + rsnd_ssi_irq_enable(mod); + return 0; } @@ -335,12 +403,29 @@ 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 (ssi->err > 0) - dev_warn(dev, "%s[%d] under/over flow err = %d\n", - rsnd_mod_name(mod), rsnd_mod_id(mod), ssi->err); + if (!ssi->usrcnt) { + dev_err(dev, "%s[%d] usrcnt error\n", + rsnd_mod_name(mod), rsnd_mod_id(mod)); + return -EIO; + } + + if (!rsnd_ssi_is_parent(mod, io)) { + 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); + + ssi->cr_own = 0; + ssi->err = 0; + + rsnd_ssi_irq_disable(mod); + } - ssi->cr_own = 0; - ssi->err = 0; + rsnd_ssi_master_clk_stop(ssi, io); + + rsnd_mod_power_off(mod); + + ssi->usrcnt--; return 0; } @@ -351,14 +436,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. @@ -367,39 +451,83 @@ 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; } -static void rsnd_ssi_record_error(struct rsnd_ssi *ssi, u32 status) +static u32 rsnd_ssi_record_error(struct rsnd_ssi *ssi) { struct rsnd_mod *mod = rsnd_mod_get(ssi); + u32 status = rsnd_ssi_status_get(mod); /* under/over flow error */ - if (status & (UIRQ | OIRQ)) { + if (status & (UIRQ | OIRQ)) ssi->err++; - /* clear error status */ - rsnd_mod_write(mod, SSISR, 0); - } + 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 will be set via SSIU :: SSI_CONTROL + * if Multi channel mode + */ + if (!rsnd_ssi_multi_slaves(io)) + cr |= EN; + + rsnd_mod_write(mod, SSICR, cr); + rsnd_mod_write(mod, SSIWSR, ssi->wsr); + + 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; - rsnd_src_ssiu_start(mod, io, rsnd_ssi_use_busif(io)); + /* + * 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_src_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; } @@ -410,15 +538,16 @@ static int rsnd_ssi_stop(struct rsnd_mod *mod, { struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); - rsnd_src_ssi_irq_disable(mod); - - rsnd_ssi_record_error(ssi, rsnd_mod_read(mod, SSISR)); - - rsnd_ssi_hw_stop(io, ssi); - - rsnd_src_ssiu_stop(mod, io); + /* + * 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, @@ -426,6 +555,7 @@ static void __rsnd_ssi_interrupt(struct rsnd_mod *mod, { 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); int is_dma = rsnd_ssi_is_dma_mode(mod); u32 status; bool elapsed = false; @@ -436,7 +566,7 @@ static void __rsnd_ssi_interrupt(struct rsnd_mod *mod, if (!rsnd_io_is_working(io)) goto rsnd_ssi_interrupt_out; - status = rsnd_mod_read(mod, SSISR); + status = rsnd_ssi_record_error(ssi); /* PIO only */ if (!is_dma && (status & DIRQ)) { @@ -459,23 +589,24 @@ static void __rsnd_ssi_interrupt(struct rsnd_mod *mod, /* DMA only */ if (is_dma && (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, io, priv); - if (ssi->err < 1024) - rsnd_ssi_start(mod, io, priv); - else - dev_warn(dev, "no more SSI restart\n"); + __rsnd_ssi_stop(mod, io, priv); + __rsnd_ssi_start(mod, io, priv); } - rsnd_ssi_record_error(ssi, status); + if (ssi->err > 1024) { + rsnd_ssi_irq_disable(mod); + dev_warn(dev, "no more %s[%d] restart\n", + rsnd_mod_name(mod), rsnd_mod_id(mod)); + } + + rsnd_ssi_status_clear(mod); rsnd_ssi_interrupt_out: spin_unlock(&priv->lock); @@ -495,15 +626,49 @@ static irqreturn_t rsnd_ssi_interrupt(int irq, void *data) /* * SSI PIO */ -static int rsnd_ssi_pio_probe(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv) +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) { struct device *dev = rsnd_priv_to_dev(priv); struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); int ret; - ret = devm_request_irq(dev, ssi->info->irq, + /* + * SSIP/SSIU/IRQ are not needed on + * SSI Multi slaves + */ + if (rsnd_ssi_is_multi_slave(mod, io)) + return 0; + + rsnd_ssi_parent_attach(mod, io, priv); + + ret = rsnd_ssiu_attach(io, mod); + if (ret < 0) + return ret; + + ret = devm_request_irq(dev, ssi->irq, rsnd_ssi_interrupt, IRQF_SHARED, dev_name(dev), mod); @@ -513,7 +678,7 @@ static int rsnd_ssi_pio_probe(struct rsnd_mod *mod, static struct rsnd_mod_ops rsnd_ssi_pio_ops = { .name = SSI_NAME, - .probe = rsnd_ssi_pio_probe, + .probe = rsnd_ssi_common_probe, .init = rsnd_ssi_init, .quit = rsnd_ssi_quit, .start = rsnd_ssi_start, @@ -526,20 +691,23 @@ static int rsnd_ssi_dma_probe(struct rsnd_mod *mod, struct rsnd_priv *priv) { struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); - struct device *dev = rsnd_priv_to_dev(priv); - int dma_id = ssi->info->dma_id; + int dma_id = 0; /* not needed */ int ret; - ret = devm_request_irq(dev, ssi->info->irq, - rsnd_ssi_interrupt, - IRQF_SHARED, - dev_name(dev), mod); + /* + * SSIP/SSIU/IRQ/DMA are not needed on + * SSI Multi slaves + */ + if (rsnd_ssi_is_multi_slave(mod, io)) + return 0; + + ret = rsnd_ssi_common_probe(mod, io, priv); if (ret) return ret; - ret = rsnd_dma_init( - io, rsnd_mod_to_dma(mod), - dma_id); + ssi->dma = rsnd_dma_attach(io, mod, dma_id); + if (IS_ERR(ssi->dma)) + return PTR_ERR(ssi->dma); return ret; } @@ -550,9 +718,7 @@ static int rsnd_ssi_dma_remove(struct rsnd_mod *mod, { struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); struct device *dev = rsnd_priv_to_dev(priv); - int irq = ssi->info->irq; - - rsnd_dma_quit(io, rsnd_mod_to_dma(mod)); + int irq = ssi->irq; /* PIO will request IRQ again */ devm_free_irq(dev, irq, mod); @@ -581,32 +747,6 @@ static int rsnd_ssi_fallback(struct rsnd_mod *mod, return 0; } -static int rsnd_ssi_dma_start(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv) -{ - struct rsnd_dma *dma = rsnd_mod_to_dma(mod); - - rsnd_dma_start(io, dma); - - rsnd_ssi_start(mod, io, priv); - - return 0; -} - -static int rsnd_ssi_dma_stop(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv) -{ - struct rsnd_dma *dma = rsnd_mod_to_dma(mod); - - rsnd_ssi_stop(mod, io, priv); - - rsnd_dma_stop(io, dma); - - return 0; -} - static struct dma_chan *rsnd_ssi_dma_req(struct rsnd_dai_stream *io, struct rsnd_mod *mod) { @@ -630,8 +770,8 @@ static struct rsnd_mod_ops rsnd_ssi_dma_ops = { .remove = rsnd_ssi_dma_remove, .init = rsnd_ssi_init, .quit = rsnd_ssi_quit, - .start = rsnd_ssi_dma_start, - .stop = rsnd_ssi_dma_stop, + .start = rsnd_ssi_start, + .stop = rsnd_ssi_stop, .fallback = rsnd_ssi_fallback, .hw_params = rsnd_ssi_hw_params, }; @@ -652,110 +792,76 @@ static struct rsnd_mod_ops rsnd_ssi_non_ops = { /* * ssi mod function */ -struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id) +static void rsnd_ssi_connect(struct rsnd_mod *mod, + struct rsnd_dai_stream *io) { - if (WARN_ON(id < 0 || id >= rsnd_ssi_nr(priv))) - id = 0; - - return rsnd_mod_get((struct rsnd_ssi *)(priv->ssi) + id); -} - -int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod) -{ - struct rsnd_ssi *ssi = rsnd_mod_to_ssi(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; + 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; - 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; + /* 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 void rsnd_of_parse_ssi(struct platform_device *pdev, - const struct rsnd_of_data *of_data, - struct rsnd_priv *priv) +void rsnd_parse_connect_ssi(struct rsnd_dai *rdai, + struct device_node *playback, + struct device_node *capture) { + struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai); struct device_node *node; struct device_node *np; - struct rsnd_ssi_platform_info *ssi_info; - struct rcar_snd_info *info = rsnd_priv_to_info(priv); - struct device *dev = &pdev->dev; - int nr, i; + struct rsnd_mod *mod; + int i; node = rsnd_ssi_of_node(priv); if (!node) return; - nr = of_get_child_count(node); - if (!nr) - goto rsnd_of_parse_ssi_end; - - ssi_info = devm_kzalloc(dev, - sizeof(struct rsnd_ssi_platform_info) * nr, - GFP_KERNEL); - if (!ssi_info) { - dev_err(dev, "ssi info allocation error\n"); - goto rsnd_of_parse_ssi_end; - } - - info->ssi_info = ssi_info; - info->ssi_info_nr = nr; - - i = -1; + i = 0; for_each_child_of_node(node, np) { + mod = rsnd_ssi_mod_get(priv, i); + if (np == playback) + rsnd_ssi_connect(mod, &rdai->playback); + if (np == capture) + rsnd_ssi_connect(mod, &rdai->capture); i++; + } - ssi_info = info->ssi_info + i; - - /* - * pin settings - */ - if (of_get_property(np, "shared-pin", NULL)) - ssi_info->flags |= RSND_SSI_CLK_PIN_SHARE; + of_node_put(node); +} - /* - * irq - */ - ssi_info->irq = irq_of_parse_and_map(np, 0); +struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id) +{ + if (WARN_ON(id < 0 || id >= rsnd_ssi_nr(priv))) + id = 0; - /* - * DMA - */ - ssi_info->dma_id = of_get_property(np, "pio-transfer", NULL) ? - 0 : 1; + return rsnd_mod_get(rsnd_ssi_get(priv, id)); +} - if (of_get_property(np, "no-busif", NULL)) - ssi_info->flags |= RSND_SSI_NO_BUSIF; - } +int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod) +{ + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); -rsnd_of_parse_ssi_end: - of_node_put(node); + return !!(rsnd_ssi_mode_flags(ssi) & RSND_SSI_CLK_PIN_SHARE); } -int rsnd_ssi_probe(struct platform_device *pdev, - const struct rsnd_of_data *of_data, - struct rsnd_priv *priv) +int rsnd_ssi_probe(struct rsnd_priv *priv) { - struct rcar_snd_info *info = rsnd_priv_to_info(priv); - struct rsnd_ssi_platform_info *pinfo; + struct device_node *node; + struct device_node *np; struct device *dev = rsnd_priv_to_dev(priv); struct rsnd_mod_ops *ops; struct clk *clk; @@ -763,50 +869,73 @@ int rsnd_ssi_probe(struct platform_device *pdev, char name[RSND_SSI_NAME_SIZE]; int i, nr, ret; - rsnd_of_parse_ssi(pdev, of_data, priv); + node = rsnd_ssi_of_node(priv); + if (!node) + return -EINVAL; + + nr = of_get_child_count(node); + if (!nr) { + ret = -EINVAL; + goto rsnd_ssi_probe_done; + } - /* - * init SSI - */ - nr = info->ssi_info_nr; ssi = devm_kzalloc(dev, sizeof(*ssi) * nr, GFP_KERNEL); - if (!ssi) - return -ENOMEM; + if (!ssi) { + ret = -ENOMEM; + goto rsnd_ssi_probe_done; + } priv->ssi = ssi; priv->ssi_nr = nr; - for_each_rsnd_ssi(ssi, priv, i) { - pinfo = &info->ssi_info[i]; + i = 0; + for_each_child_of_node(node, np) { + ssi = rsnd_ssi_get(priv, i); snprintf(name, RSND_SSI_NAME_SIZE, "%s.%d", SSI_NAME, i); clk = devm_clk_get(dev, name); - if (IS_ERR(clk)) - return PTR_ERR(clk); + if (IS_ERR(clk)) { + ret = PTR_ERR(clk); + goto rsnd_ssi_probe_done; + } - ssi->info = pinfo; + if (of_get_property(np, "shared-pin", NULL)) + ssi->flags |= RSND_SSI_CLK_PIN_SHARE; + + if (of_get_property(np, "no-busif", NULL)) + ssi->flags |= RSND_SSI_NO_BUSIF; + + ssi->irq = irq_of_parse_and_map(np, 0); + if (!ssi->irq) { + ret = -EINVAL; + goto rsnd_ssi_probe_done; + } ops = &rsnd_ssi_non_ops; - if (pinfo->dma_id > 0) - ops = &rsnd_ssi_dma_ops; - else if (rsnd_ssi_pio_available(ssi)) + if (of_get_property(np, "pio-transfer", NULL)) ops = &rsnd_ssi_pio_ops; + else + ops = &rsnd_ssi_dma_ops; ret = rsnd_mod_init(priv, rsnd_mod_get(ssi), ops, clk, RSND_MOD_SSI, i); if (ret) - return ret; + goto rsnd_ssi_probe_done; - rsnd_ssi_parent_setup(priv, ssi); + i++; } - return 0; + ret = 0; + +rsnd_ssi_probe_done: + of_node_put(node); + + return ret; } -void rsnd_ssi_remove(struct platform_device *pdev, - struct rsnd_priv *priv) +void rsnd_ssi_remove(struct rsnd_priv *priv) { struct rsnd_ssi *ssi; int i; |