2013-07-22 12:36:57 +08:00
|
|
|
/*
|
|
|
|
* Renesas R-Car SSIU/SSI support
|
|
|
|
*
|
|
|
|
* Copyright (C) 2013 Renesas Solutions Corp.
|
|
|
|
* Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
|
|
|
|
*
|
|
|
|
* Based on fsi.c
|
|
|
|
* Kuninori Morimoto <morimoto.kuninori@renesas.com>
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
|
|
* published by the Free Software Foundation.
|
|
|
|
*/
|
|
|
|
#include <linux/delay.h>
|
|
|
|
#include "rsnd.h"
|
|
|
|
#define RSND_SSI_NAME_SIZE 16
|
|
|
|
|
|
|
|
/*
|
|
|
|
* SSICR
|
|
|
|
*/
|
|
|
|
#define FORCE (1 << 31) /* Fixed */
|
2013-07-29 09:59:02 +08:00
|
|
|
#define DMEN (1 << 28) /* DMA Enable */
|
2013-07-22 12:36:57 +08:00
|
|
|
#define UIEN (1 << 27) /* Underflow Interrupt Enable */
|
|
|
|
#define OIEN (1 << 26) /* Overflow Interrupt Enable */
|
|
|
|
#define IIEN (1 << 25) /* Idle Mode Interrupt Enable */
|
|
|
|
#define DIEN (1 << 24) /* Data Interrupt Enable */
|
2015-11-30 16:54:03 +08:00
|
|
|
#define CHNL_4 (1 << 22) /* Channels */
|
|
|
|
#define CHNL_6 (2 << 22) /* Channels */
|
|
|
|
#define CHNL_8 (3 << 22) /* Channels */
|
2013-07-22 12:36:57 +08:00
|
|
|
#define DWL_8 (0 << 19) /* Data Word Length */
|
|
|
|
#define DWL_16 (1 << 19) /* Data Word Length */
|
|
|
|
#define DWL_18 (2 << 19) /* Data Word Length */
|
|
|
|
#define DWL_20 (3 << 19) /* Data Word Length */
|
|
|
|
#define DWL_22 (4 << 19) /* Data Word Length */
|
|
|
|
#define DWL_24 (5 << 19) /* Data Word Length */
|
|
|
|
#define DWL_32 (6 << 19) /* Data Word Length */
|
|
|
|
|
|
|
|
#define SWL_32 (3 << 16) /* R/W System Word Length */
|
|
|
|
#define SCKD (1 << 15) /* Serial Bit Clock Direction */
|
|
|
|
#define SWSD (1 << 14) /* Serial WS Direction */
|
|
|
|
#define SCKP (1 << 13) /* Serial Bit Clock Polarity */
|
|
|
|
#define SWSP (1 << 12) /* Serial WS Polarity */
|
|
|
|
#define SDTA (1 << 10) /* Serial Data Alignment */
|
2015-11-17 16:28:11 +08:00
|
|
|
#define PDTA (1 << 9) /* Parallel Data Alignment */
|
2013-07-22 12:36:57 +08:00
|
|
|
#define DEL (1 << 8) /* Serial Data Delay */
|
|
|
|
#define CKDV(v) (v << 4) /* Serial Clock Division Ratio */
|
|
|
|
#define TRMD (1 << 1) /* Transmit/Receive Mode Select */
|
|
|
|
#define EN (1 << 0) /* SSI Module Enable */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* SSISR
|
|
|
|
*/
|
|
|
|
#define UIRQ (1 << 27) /* Underflow Error Interrupt Status */
|
|
|
|
#define OIRQ (1 << 26) /* Overflow Error Interrupt Status */
|
|
|
|
#define IIRQ (1 << 25) /* Idle Mode Interrupt Status */
|
|
|
|
#define DIRQ (1 << 24) /* Data Interrupt Status Flag */
|
|
|
|
|
2013-07-29 09:59:02 +08:00
|
|
|
/*
|
|
|
|
* SSIWSR
|
|
|
|
*/
|
|
|
|
#define CONT (1 << 8) /* WS Continue Function */
|
2015-11-30 16:54:03 +08:00
|
|
|
#define WS_MODE (1 << 0) /* WS Mode */
|
2013-07-29 09:59:02 +08:00
|
|
|
|
2014-05-23 14:25:43 +08:00
|
|
|
#define SSI_NAME "ssi"
|
|
|
|
|
2013-07-22 12:36:57 +08:00
|
|
|
struct rsnd_ssi {
|
|
|
|
struct rsnd_mod mod;
|
2015-10-26 16:42:25 +08:00
|
|
|
struct rsnd_mod *dma;
|
2013-07-22 12:36:57 +08:00
|
|
|
|
2015-11-10 13:11:35 +08:00
|
|
|
u32 flags;
|
2013-07-22 12:36:57 +08:00
|
|
|
u32 cr_own;
|
|
|
|
u32 cr_clk;
|
2015-10-26 16:43:57 +08:00
|
|
|
u32 cr_mode;
|
2015-11-30 16:53:04 +08:00
|
|
|
u32 wsr;
|
2015-04-10 16:50:30 +08:00
|
|
|
int chan;
|
2015-10-26 16:43:57 +08:00
|
|
|
int rate;
|
2013-07-22 12:36:57 +08:00
|
|
|
int err;
|
2015-11-10 13:11:35 +08:00
|
|
|
int irq;
|
2013-07-22 12:36:57 +08:00
|
|
|
unsigned int usrcnt;
|
|
|
|
};
|
|
|
|
|
2015-11-10 13:11:35 +08:00
|
|
|
/* flags */
|
|
|
|
#define RSND_SSI_CLK_PIN_SHARE (1 << 0)
|
|
|
|
#define RSND_SSI_NO_BUSIF (1 << 1) /* SSI+DMA without BUSIF */
|
|
|
|
|
2013-07-22 12:36:57 +08:00
|
|
|
#define for_each_rsnd_ssi(pos, priv, i) \
|
|
|
|
for (i = 0; \
|
|
|
|
(i < rsnd_ssi_nr(priv)) && \
|
2014-01-24 10:39:40 +08:00
|
|
|
((pos) = ((struct rsnd_ssi *)(priv)->ssi + i)); \
|
2013-07-22 12:36:57 +08:00
|
|
|
i++)
|
|
|
|
|
2015-11-10 13:11:35 +08:00
|
|
|
#define rsnd_ssi_get(priv, id) ((struct rsnd_ssi *)(priv->ssi) + id)
|
2015-10-26 16:38:26 +08:00
|
|
|
#define rsnd_ssi_to_dma(mod) ((ssi)->dma)
|
2014-01-24 10:39:40 +08:00
|
|
|
#define rsnd_ssi_nr(priv) ((priv)->ssi_nr)
|
2013-07-22 12:36:57 +08:00
|
|
|
#define rsnd_mod_to_ssi(_mod) container_of((_mod), struct rsnd_ssi, mod)
|
2015-11-10 13:11:35 +08:00
|
|
|
#define rsnd_ssi_mode_flags(p) ((p)->flags)
|
2015-10-26 16:43:57 +08:00
|
|
|
#define rsnd_ssi_is_parent(ssi, io) ((ssi) == rsnd_io_to_mod_ssip(io))
|
2015-12-17 11:00:10 +08:00
|
|
|
#define rsnd_ssi_is_multi_slave(ssi, io) ((mod) != rsnd_io_to_mod_ssi(io))
|
2013-07-22 12:36:57 +08:00
|
|
|
|
2015-10-22 11:15:46 +08:00
|
|
|
int rsnd_ssi_use_busif(struct rsnd_dai_stream *io)
|
2014-06-23 08:56:23 +08:00
|
|
|
{
|
2015-10-22 11:15:46 +08:00
|
|
|
struct rsnd_mod *mod = rsnd_io_to_mod_ssi(io);
|
2014-06-23 08:56:23 +08:00
|
|
|
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
|
|
|
|
int use_busif = 0;
|
|
|
|
|
2014-11-27 16:05:54 +08:00
|
|
|
if (!rsnd_ssi_is_dma_mode(mod))
|
|
|
|
return 0;
|
|
|
|
|
2014-06-23 08:56:23 +08:00
|
|
|
if (!(rsnd_ssi_mode_flags(ssi) & RSND_SSI_NO_BUSIF))
|
|
|
|
use_busif = 1;
|
|
|
|
if (rsnd_io_to_mod_src(io))
|
|
|
|
use_busif = 1;
|
|
|
|
|
|
|
|
return use_busif;
|
|
|
|
}
|
|
|
|
|
2015-10-26 16:42:09 +08:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2013-07-22 12:36:57 +08:00
|
|
|
static void rsnd_ssi_status_check(struct rsnd_mod *mod,
|
|
|
|
u32 bit)
|
|
|
|
{
|
|
|
|
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
|
|
|
|
struct device *dev = rsnd_priv_to_dev(priv);
|
|
|
|
u32 status;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < 1024; i++) {
|
2015-10-26 16:42:09 +08:00
|
|
|
status = rsnd_ssi_status_get(mod);
|
2013-07-22 12:36:57 +08:00
|
|
|
if (status & bit)
|
|
|
|
return;
|
|
|
|
|
|
|
|
udelay(50);
|
|
|
|
}
|
|
|
|
|
|
|
|
dev_warn(dev, "status check failed\n");
|
|
|
|
}
|
|
|
|
|
2015-10-26 16:40:41 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2015-12-17 11:00:10 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2013-07-22 12:36:57 +08:00
|
|
|
static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi,
|
2013-12-20 11:28:39 +08:00
|
|
|
struct rsnd_dai_stream *io)
|
2013-07-22 12:36:57 +08:00
|
|
|
{
|
2015-01-15 16:08:34 +08:00
|
|
|
struct rsnd_priv *priv = rsnd_io_to_priv(io);
|
2013-12-20 11:28:39 +08:00
|
|
|
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
|
2013-07-22 12:36:57 +08:00
|
|
|
struct device *dev = rsnd_priv_to_dev(priv);
|
2015-10-26 16:43:57 +08:00
|
|
|
struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
|
2015-09-10 15:02:21 +08:00
|
|
|
struct rsnd_mod *mod = rsnd_mod_get(ssi);
|
2015-10-26 16:43:57 +08:00
|
|
|
struct rsnd_mod *ssi_parent_mod = rsnd_io_to_mod_ssip(io);
|
2015-12-17 10:57:27 +08:00
|
|
|
int slots = rsnd_get_slot_width(io);
|
2015-09-10 15:03:48 +08:00
|
|
|
int j, ret;
|
2013-07-22 12:36:57 +08:00
|
|
|
int ssi_clk_mul_table[] = {
|
|
|
|
1, 2, 4, 8, 16, 6, 12,
|
|
|
|
};
|
|
|
|
unsigned int main_rate;
|
2014-03-04 12:51:21 +08:00
|
|
|
unsigned int rate = rsnd_src_get_ssi_rate(priv, io, runtime);
|
2013-07-22 12:36:57 +08:00
|
|
|
|
2015-10-26 16:43:57 +08:00
|
|
|
if (!rsnd_rdai_is_clk_master(rdai))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (ssi_parent_mod && !rsnd_ssi_is_parent(mod, io))
|
|
|
|
return 0;
|
|
|
|
|
2015-12-17 11:00:10 +08:00
|
|
|
if (rsnd_ssi_is_multi_slave(mod, io))
|
|
|
|
return 0;
|
|
|
|
|
2015-10-26 16:43:57 +08:00
|
|
|
if (ssi->usrcnt > 1) {
|
|
|
|
if (ssi->rate != rate) {
|
|
|
|
dev_err(dev, "SSI parent/child should use same rate\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-07-22 12:36:57 +08:00
|
|
|
/*
|
|
|
|
* Find best clock, and try to start ADG
|
|
|
|
*/
|
2015-09-10 15:03:48 +08:00
|
|
|
for (j = 0; j < ARRAY_SIZE(ssi_clk_mul_table); j++) {
|
|
|
|
|
|
|
|
/*
|
|
|
|
* this driver is assuming that
|
2015-11-30 16:53:27 +08:00
|
|
|
* system word is 32bit x slots
|
2015-09-10 15:03:48 +08:00
|
|
|
* see rsnd_ssi_init()
|
|
|
|
*/
|
2015-11-30 16:53:27 +08:00
|
|
|
main_rate = rate * 32 * slots * ssi_clk_mul_table[j];
|
2015-09-10 15:03:48 +08:00
|
|
|
|
|
|
|
ret = rsnd_adg_ssi_clk_try_start(mod, main_rate);
|
|
|
|
if (0 == ret) {
|
|
|
|
ssi->cr_clk = FORCE | SWL_32 |
|
|
|
|
SCKD | SWSD | CKDV(j);
|
2015-11-30 16:53:04 +08:00
|
|
|
ssi->wsr = CONT;
|
2015-09-10 15:03:48 +08:00
|
|
|
|
2015-10-26 16:43:57 +08:00
|
|
|
ssi->rate = rate;
|
|
|
|
|
2015-09-10 15:03:48 +08:00
|
|
|
dev_dbg(dev, "%s[%d] outputs %u Hz\n",
|
|
|
|
rsnd_mod_name(mod),
|
|
|
|
rsnd_mod_id(mod), rate);
|
|
|
|
|
|
|
|
return 0;
|
2013-07-22 12:36:57 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
dev_err(dev, "unsupported clock rate\n");
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
2015-10-26 16:43:57 +08:00
|
|
|
static void rsnd_ssi_master_clk_stop(struct rsnd_ssi *ssi,
|
|
|
|
struct rsnd_dai_stream *io)
|
2013-07-22 12:36:57 +08:00
|
|
|
{
|
2015-01-15 16:07:19 +08:00
|
|
|
struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
|
2015-09-10 15:02:21 +08:00
|
|
|
struct rsnd_mod *mod = rsnd_mod_get(ssi);
|
2015-10-26 16:43:57 +08:00
|
|
|
struct rsnd_mod *ssi_parent_mod = rsnd_io_to_mod_ssip(io);
|
2014-11-27 16:05:44 +08:00
|
|
|
|
2015-10-26 16:43:57 +08:00
|
|
|
if (!rsnd_rdai_is_clk_master(rdai))
|
2013-07-22 12:36:57 +08:00
|
|
|
return;
|
|
|
|
|
2015-10-26 16:43:57 +08:00
|
|
|
if (ssi_parent_mod && !rsnd_ssi_is_parent(mod, io))
|
|
|
|
return;
|
2013-07-22 12:36:57 +08:00
|
|
|
|
2015-10-26 16:43:57 +08:00
|
|
|
if (ssi->usrcnt > 1)
|
|
|
|
return;
|
2015-04-10 16:50:30 +08:00
|
|
|
|
2015-10-26 16:43:57 +08:00
|
|
|
ssi->cr_clk = 0;
|
|
|
|
ssi->rate = 0;
|
2013-07-22 12:36:57 +08:00
|
|
|
|
2015-10-26 16:43:57 +08:00
|
|
|
rsnd_adg_ssi_clk_stop(mod);
|
2013-07-22 12:36:57 +08:00
|
|
|
}
|
|
|
|
|
2015-11-30 16:52:38 +08:00
|
|
|
static int rsnd_ssi_config_init(struct rsnd_ssi *ssi,
|
|
|
|
struct rsnd_dai_stream *io)
|
|
|
|
{
|
|
|
|
struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
|
|
|
|
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
|
|
|
|
u32 cr_own;
|
|
|
|
u32 cr_mode;
|
2015-11-30 16:54:03 +08:00
|
|
|
u32 wsr;
|
2015-12-02 15:34:28 +08:00
|
|
|
int is_tdm;
|
|
|
|
|
2015-12-17 10:57:27 +08:00
|
|
|
is_tdm = (rsnd_get_slot_width(io) >= 6) ? 1 : 0;
|
2015-11-30 16:52:38 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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;
|
2015-12-02 15:34:28 +08:00
|
|
|
if (rdai->frm_clk_inv ^ is_tdm)
|
2015-11-30 16:52:38 +08:00
|
|
|
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(rsnd_mod_get(ssi))) {
|
|
|
|
cr_mode = UIEN | OIEN | /* over/under run */
|
|
|
|
DMEN; /* DMA : enable DMA */
|
|
|
|
} else {
|
|
|
|
cr_mode = DIEN; /* PIO : enable Data interrupt */
|
|
|
|
}
|
|
|
|
|
2015-11-30 16:54:03 +08:00
|
|
|
/*
|
|
|
|
* TDM Extend Mode
|
|
|
|
* see
|
|
|
|
* rsnd_ssiu_init_gen2()
|
|
|
|
*/
|
|
|
|
wsr = ssi->wsr;
|
2015-12-02 15:34:28 +08:00
|
|
|
if (is_tdm) {
|
2015-11-30 16:54:03 +08:00
|
|
|
wsr |= WS_MODE;
|
|
|
|
cr_own |= CHNL_8;
|
|
|
|
}
|
|
|
|
|
2015-11-30 16:52:38 +08:00
|
|
|
ssi->cr_own = cr_own;
|
|
|
|
ssi->cr_mode = cr_mode;
|
2015-11-30 16:54:03 +08:00
|
|
|
ssi->wsr = wsr;
|
2015-11-30 16:52:38 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-07-22 12:36:57 +08:00
|
|
|
/*
|
|
|
|
* SSI mod common functions
|
|
|
|
*/
|
|
|
|
static int rsnd_ssi_init(struct rsnd_mod *mod,
|
2015-06-15 14:25:20 +08:00
|
|
|
struct rsnd_dai_stream *io,
|
2015-01-15 16:07:47 +08:00
|
|
|
struct rsnd_priv *priv)
|
2013-07-22 12:36:57 +08:00
|
|
|
{
|
|
|
|
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
|
2015-10-26 16:43:57 +08:00
|
|
|
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;
|
2013-07-22 12:36:57 +08:00
|
|
|
|
2015-11-30 16:52:38 +08:00
|
|
|
ret = rsnd_ssi_config_init(ssi, io);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
2015-10-26 16:43:57 +08:00
|
|
|
|
2013-07-22 12:36:57 +08:00
|
|
|
ssi->err = -1; /* ignore 1st error */
|
|
|
|
|
2015-10-26 16:43:57 +08:00
|
|
|
/* clear error status */
|
|
|
|
rsnd_ssi_status_clear(mod);
|
|
|
|
|
|
|
|
rsnd_ssi_irq_enable(mod);
|
|
|
|
|
2013-07-22 12:36:57 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int rsnd_ssi_quit(struct rsnd_mod *mod,
|
2015-06-15 14:25:20 +08:00
|
|
|
struct rsnd_dai_stream *io,
|
2015-01-15 16:07:47 +08:00
|
|
|
struct rsnd_priv *priv)
|
2013-07-22 12:36:57 +08:00
|
|
|
{
|
|
|
|
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
|
|
|
|
struct device *dev = rsnd_priv_to_dev(priv);
|
|
|
|
|
2015-12-24 15:02:39 +08:00
|
|
|
if (!ssi->usrcnt) {
|
|
|
|
dev_err(dev, "%s[%d] usrcnt error\n",
|
|
|
|
rsnd_mod_name(mod), rsnd_mod_id(mod));
|
|
|
|
return -EIO;
|
|
|
|
}
|
2015-10-26 16:43:57 +08:00
|
|
|
|
2015-12-24 15:02:39 +08:00
|
|
|
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);
|
2013-07-22 12:36:57 +08:00
|
|
|
|
2015-12-24 15:02:39 +08:00
|
|
|
ssi->cr_own = 0;
|
|
|
|
ssi->err = 0;
|
2013-07-22 12:36:57 +08:00
|
|
|
|
2015-12-24 15:02:39 +08:00
|
|
|
rsnd_ssi_irq_disable(mod);
|
|
|
|
}
|
2015-10-26 16:43:57 +08:00
|
|
|
|
|
|
|
rsnd_ssi_master_clk_stop(ssi, io);
|
|
|
|
|
|
|
|
rsnd_mod_power_off(mod);
|
|
|
|
|
|
|
|
ssi->usrcnt--;
|
|
|
|
|
2013-07-22 12:36:57 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-04-10 16:50:30 +08:00
|
|
|
static int rsnd_ssi_hw_params(struct rsnd_mod *mod,
|
2015-06-15 14:25:20 +08:00
|
|
|
struct rsnd_dai_stream *io,
|
2015-04-10 16:50:30 +08:00
|
|
|
struct snd_pcm_substream *substream,
|
|
|
|
struct snd_pcm_hw_params *params)
|
|
|
|
{
|
|
|
|
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
|
|
|
|
int chan = params_channels(params);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Already working.
|
|
|
|
* It will happen if SSI has parent/child connection.
|
|
|
|
*/
|
2015-10-26 16:43:57 +08:00
|
|
|
if (ssi->usrcnt > 1) {
|
2015-04-10 16:50:30 +08:00
|
|
|
/*
|
|
|
|
* it is error if child <-> parent SSI uses
|
|
|
|
* different channels.
|
|
|
|
*/
|
|
|
|
if (ssi->chan != chan)
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
ssi->chan = chan;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-10-26 16:42:09 +08:00
|
|
|
static u32 rsnd_ssi_record_error(struct rsnd_ssi *ssi)
|
2013-07-22 12:36:57 +08:00
|
|
|
{
|
2015-09-10 15:02:21 +08:00
|
|
|
struct rsnd_mod *mod = rsnd_mod_get(ssi);
|
2015-10-26 16:42:09 +08:00
|
|
|
u32 status = rsnd_ssi_status_get(mod);
|
2015-09-10 15:02:21 +08:00
|
|
|
|
2013-07-22 12:36:57 +08:00
|
|
|
/* under/over flow error */
|
2015-11-26 19:13:40 +08:00
|
|
|
if (status & (UIRQ | OIRQ))
|
2013-07-22 12:36:57 +08:00
|
|
|
ssi->err++;
|
|
|
|
|
2015-10-26 16:42:09 +08:00
|
|
|
return status;
|
2013-07-22 12:36:57 +08:00
|
|
|
}
|
|
|
|
|
2015-10-26 16:43:57 +08:00
|
|
|
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 |
|
2015-12-17 11:00:10 +08:00
|
|
|
ssi->cr_mode;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* EN will be set via SSIU :: SSI_CONTROL
|
|
|
|
* if Multi channel mode
|
|
|
|
*/
|
|
|
|
if (!rsnd_ssi_multi_slaves(io))
|
|
|
|
cr |= EN;
|
2015-10-26 16:43:57 +08:00
|
|
|
|
|
|
|
rsnd_mod_write(mod, SSICR, cr);
|
2015-11-30 16:53:04 +08:00
|
|
|
rsnd_mod_write(mod, SSIWSR, ssi->wsr);
|
2015-10-26 16:43:57 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-11-27 16:07:47 +08:00
|
|
|
static int rsnd_ssi_start(struct rsnd_mod *mod,
|
2015-06-15 14:25:20 +08:00
|
|
|
struct rsnd_dai_stream *io,
|
2015-01-15 16:07:47 +08:00
|
|
|
struct rsnd_priv *priv)
|
2015-10-26 16:43:57 +08:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
* 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)
|
2014-11-27 16:07:47 +08:00
|
|
|
{
|
|
|
|
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
|
2015-10-26 16:43:57 +08:00
|
|
|
u32 cr;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* disable all IRQ,
|
|
|
|
* and, wait all data was sent
|
|
|
|
*/
|
|
|
|
cr = ssi->cr_own |
|
|
|
|
ssi->cr_clk;
|
2014-11-27 16:07:47 +08:00
|
|
|
|
2015-10-26 16:43:57 +08:00
|
|
|
rsnd_mod_write(mod, SSICR, cr | EN);
|
|
|
|
rsnd_ssi_status_check(mod, DIRQ);
|
2014-11-27 16:07:47 +08:00
|
|
|
|
2015-10-26 16:43:57 +08:00
|
|
|
/*
|
|
|
|
* disable SSI,
|
|
|
|
* and, wait idle state
|
|
|
|
*/
|
|
|
|
rsnd_mod_write(mod, SSICR, cr); /* disabled all */
|
|
|
|
rsnd_ssi_status_check(mod, IIRQ);
|
2014-11-27 16:07:47 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int rsnd_ssi_stop(struct rsnd_mod *mod,
|
2015-06-15 14:25:20 +08:00
|
|
|
struct rsnd_dai_stream *io,
|
2015-01-15 16:07:47 +08:00
|
|
|
struct rsnd_priv *priv)
|
2014-11-27 16:07:47 +08:00
|
|
|
{
|
|
|
|
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
|
|
|
|
|
2015-10-26 16:43:57 +08:00
|
|
|
/*
|
|
|
|
* don't stop if not last user
|
|
|
|
* see also
|
|
|
|
* rsnd_ssi_start
|
|
|
|
* rsnd_ssi_interrupt
|
|
|
|
*/
|
|
|
|
if (ssi->usrcnt > 1)
|
|
|
|
return 0;
|
2014-11-27 16:07:47 +08:00
|
|
|
|
2015-10-26 16:43:57 +08:00
|
|
|
return __rsnd_ssi_stop(mod, io, priv);
|
2014-11-27 16:07:47 +08:00
|
|
|
}
|
|
|
|
|
2015-06-15 14:26:56 +08:00
|
|
|
static void __rsnd_ssi_interrupt(struct rsnd_mod *mod,
|
|
|
|
struct rsnd_dai_stream *io)
|
2013-07-22 12:36:57 +08:00
|
|
|
{
|
2015-06-15 14:26:56 +08:00
|
|
|
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
|
2015-01-15 16:07:47 +08:00
|
|
|
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
|
2015-10-26 16:41:36 +08:00
|
|
|
struct device *dev = rsnd_priv_to_dev(priv);
|
2015-01-15 16:09:13 +08:00
|
|
|
int is_dma = rsnd_ssi_is_dma_mode(mod);
|
2015-05-21 11:50:23 +08:00
|
|
|
u32 status;
|
2015-06-15 14:21:15 +08:00
|
|
|
bool elapsed = false;
|
2015-05-21 11:50:23 +08:00
|
|
|
|
|
|
|
spin_lock(&priv->lock);
|
2013-07-22 12:36:57 +08:00
|
|
|
|
2015-05-21 11:50:23 +08:00
|
|
|
/* ignore all cases if not working */
|
2015-06-15 14:27:47 +08:00
|
|
|
if (!rsnd_io_is_working(io))
|
2015-05-21 11:50:23 +08:00
|
|
|
goto rsnd_ssi_interrupt_out;
|
|
|
|
|
2015-10-26 16:42:09 +08:00
|
|
|
status = rsnd_ssi_record_error(ssi);
|
2014-11-27 16:07:47 +08:00
|
|
|
|
|
|
|
/* PIO only */
|
2015-01-15 16:09:13 +08:00
|
|
|
if (!is_dma && (status & DIRQ)) {
|
2013-07-22 12:36:57 +08:00
|
|
|
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()
|
|
|
|
*/
|
2015-01-15 16:06:49 +08:00
|
|
|
if (rsnd_io_is_play(io))
|
2014-01-24 10:41:44 +08:00
|
|
|
rsnd_mod_write(mod, SSITDR, *buf);
|
2013-07-22 12:36:57 +08:00
|
|
|
else
|
2014-01-24 10:41:44 +08:00
|
|
|
*buf = rsnd_mod_read(mod, SSIRDR);
|
2013-07-22 12:36:57 +08:00
|
|
|
|
2015-06-15 14:21:15 +08:00
|
|
|
elapsed = rsnd_dai_pointer_update(io, sizeof(*buf));
|
2014-11-27 16:07:47 +08:00
|
|
|
}
|
2013-07-22 12:36:57 +08:00
|
|
|
|
2015-06-15 14:20:54 +08:00
|
|
|
/* DMA only */
|
|
|
|
if (is_dma && (status & (UIRQ | OIRQ))) {
|
2014-11-27 16:07:47 +08:00
|
|
|
/*
|
|
|
|
* restart SSI
|
|
|
|
*/
|
|
|
|
dev_dbg(dev, "%s[%d] restart\n",
|
|
|
|
rsnd_mod_name(mod), rsnd_mod_id(mod));
|
2015-03-19 12:13:47 +08:00
|
|
|
|
2015-10-26 16:43:57 +08:00
|
|
|
__rsnd_ssi_stop(mod, io, priv);
|
|
|
|
__rsnd_ssi_start(mod, io, priv);
|
2013-07-22 12:36:57 +08:00
|
|
|
}
|
|
|
|
|
2015-10-26 16:41:36 +08:00
|
|
|
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));
|
|
|
|
}
|
|
|
|
|
2015-11-26 19:13:40 +08:00
|
|
|
rsnd_ssi_status_clear(mod);
|
2015-05-21 11:50:23 +08:00
|
|
|
rsnd_ssi_interrupt_out:
|
|
|
|
spin_unlock(&priv->lock);
|
|
|
|
|
2015-06-15 14:21:15 +08:00
|
|
|
if (elapsed)
|
|
|
|
rsnd_dai_period_elapsed(io);
|
2015-06-15 14:26:56 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static irqreturn_t rsnd_ssi_interrupt(int irq, void *data)
|
|
|
|
{
|
|
|
|
struct rsnd_mod *mod = data;
|
|
|
|
|
|
|
|
rsnd_mod_interrupt(mod, __rsnd_ssi_interrupt);
|
2015-06-15 14:21:15 +08:00
|
|
|
|
2014-11-27 16:07:47 +08:00
|
|
|
return IRQ_HANDLED;
|
2013-07-22 12:36:57 +08:00
|
|
|
}
|
|
|
|
|
2014-11-27 16:08:10 +08:00
|
|
|
/*
|
|
|
|
* SSI PIO
|
|
|
|
*/
|
2015-10-26 16:43:57 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-26 16:43:41 +08:00
|
|
|
static int rsnd_ssi_common_probe(struct rsnd_mod *mod,
|
|
|
|
struct rsnd_dai_stream *io,
|
|
|
|
struct rsnd_priv *priv)
|
2014-03-04 12:50:49 +08:00
|
|
|
{
|
|
|
|
struct device *dev = rsnd_priv_to_dev(priv);
|
|
|
|
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
|
|
|
|
int ret;
|
|
|
|
|
2015-12-17 11:00:10 +08:00
|
|
|
/*
|
|
|
|
* SSIP/SSIU/IRQ are not needed on
|
|
|
|
* SSI Multi slaves
|
|
|
|
*/
|
|
|
|
if (rsnd_ssi_is_multi_slave(mod, io))
|
|
|
|
return 0;
|
|
|
|
|
2015-10-26 16:43:57 +08:00
|
|
|
rsnd_ssi_parent_attach(mod, io, priv);
|
|
|
|
|
2015-10-26 16:43:41 +08:00
|
|
|
ret = rsnd_ssiu_attach(io, mod);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
2015-11-10 13:11:35 +08:00
|
|
|
ret = devm_request_irq(dev, ssi->irq,
|
2014-11-27 16:08:10 +08:00
|
|
|
rsnd_ssi_interrupt,
|
2014-03-04 12:50:49 +08:00
|
|
|
IRQF_SHARED,
|
2015-06-15 14:26:56 +08:00
|
|
|
dev_name(dev), mod);
|
2014-05-23 14:25:43 +08:00
|
|
|
|
2014-03-04 12:50:49 +08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2013-07-22 12:36:57 +08:00
|
|
|
static struct rsnd_mod_ops rsnd_ssi_pio_ops = {
|
2014-05-23 14:25:43 +08:00
|
|
|
.name = SSI_NAME,
|
2015-10-26 16:43:41 +08:00
|
|
|
.probe = rsnd_ssi_common_probe,
|
2013-07-22 12:36:57 +08:00
|
|
|
.init = rsnd_ssi_init,
|
|
|
|
.quit = rsnd_ssi_quit,
|
2014-11-27 16:07:17 +08:00
|
|
|
.start = rsnd_ssi_start,
|
|
|
|
.stop = rsnd_ssi_stop,
|
2015-04-10 16:50:30 +08:00
|
|
|
.hw_params = rsnd_ssi_hw_params,
|
2013-07-22 12:36:57 +08:00
|
|
|
};
|
|
|
|
|
2014-03-04 12:50:49 +08:00
|
|
|
static int rsnd_ssi_dma_probe(struct rsnd_mod *mod,
|
2015-06-15 14:25:20 +08:00
|
|
|
struct rsnd_dai_stream *io,
|
2015-01-15 16:07:47 +08:00
|
|
|
struct rsnd_priv *priv)
|
2014-03-04 12:50:49 +08:00
|
|
|
{
|
|
|
|
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
|
2015-11-10 13:11:35 +08:00
|
|
|
int dma_id = 0; /* not needed */
|
2014-03-04 12:50:49 +08:00
|
|
|
int ret;
|
|
|
|
|
2015-12-17 11:00:10 +08:00
|
|
|
/*
|
|
|
|
* SSIP/SSIU/IRQ/DMA are not needed on
|
|
|
|
* SSI Multi slaves
|
|
|
|
*/
|
|
|
|
if (rsnd_ssi_is_multi_slave(mod, io))
|
|
|
|
return 0;
|
|
|
|
|
2015-10-26 16:43:41 +08:00
|
|
|
ret = rsnd_ssi_common_probe(mod, io, priv);
|
2014-11-27 16:07:47 +08:00
|
|
|
if (ret)
|
2015-03-26 12:02:32 +08:00
|
|
|
return ret;
|
2014-11-27 16:07:47 +08:00
|
|
|
|
2015-10-26 16:39:20 +08:00
|
|
|
ssi->dma = rsnd_dma_attach(io, mod, dma_id);
|
2015-10-26 16:38:26 +08:00
|
|
|
if (IS_ERR(ssi->dma))
|
|
|
|
return PTR_ERR(ssi->dma);
|
2014-05-23 14:25:43 +08:00
|
|
|
|
2014-03-04 12:50:49 +08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int rsnd_ssi_dma_remove(struct rsnd_mod *mod,
|
2015-06-15 14:25:20 +08:00
|
|
|
struct rsnd_dai_stream *io,
|
2015-01-15 16:07:47 +08:00
|
|
|
struct rsnd_priv *priv)
|
2014-11-27 16:02:43 +08:00
|
|
|
{
|
2014-11-27 16:07:47 +08:00
|
|
|
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
|
|
|
|
struct device *dev = rsnd_priv_to_dev(priv);
|
2015-11-10 13:11:35 +08:00
|
|
|
int irq = ssi->irq;
|
2014-11-27 16:07:47 +08:00
|
|
|
|
|
|
|
/* PIO will request IRQ again */
|
2015-10-22 11:13:44 +08:00
|
|
|
devm_free_irq(dev, irq, mod);
|
2014-11-27 16:07:47 +08:00
|
|
|
|
2014-11-27 16:02:43 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int rsnd_ssi_fallback(struct rsnd_mod *mod,
|
2015-06-15 14:25:20 +08:00
|
|
|
struct rsnd_dai_stream *io,
|
2015-01-15 16:07:47 +08:00
|
|
|
struct rsnd_priv *priv)
|
2014-03-04 12:50:49 +08:00
|
|
|
{
|
2014-11-10 12:00:58 +08:00
|
|
|
struct device *dev = rsnd_priv_to_dev(priv);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* fallback to PIO
|
|
|
|
*
|
|
|
|
* SSI .probe might be called again.
|
|
|
|
* see
|
|
|
|
* rsnd_rdai_continuance_probe()
|
|
|
|
*/
|
|
|
|
mod->ops = &rsnd_ssi_pio_ops;
|
|
|
|
|
|
|
|
dev_info(dev, "%s[%d] fallback to PIO mode\n",
|
|
|
|
rsnd_mod_name(mod), rsnd_mod_id(mod));
|
|
|
|
|
2014-03-04 12:50:49 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-06-15 14:26:25 +08:00
|
|
|
static struct dma_chan *rsnd_ssi_dma_req(struct rsnd_dai_stream *io,
|
|
|
|
struct rsnd_mod *mod)
|
2014-06-23 08:56:23 +08:00
|
|
|
{
|
ASoC: rsnd: 1st DMAC dma-names cares subnode
Renesas R-Car sound (= rsnd) needs 2 DMAC which are called as
Audio DMAC (= 1st DMAC) and Audio DMAC peri peri (2nd DMAC).
And rsnd had assumed that 1st / 2nd DMACs are implemented as DMAEngine.
But, in result of DMA ML discussion, 2nd DMAC was concluded that it is
not a general purpose DMAC (2nd DMAC is for Device to Device inside
sound system). Additionally, current DMAEngine can't support Device to
Device, and we don't have correct DT bindings for it at this point.
So the easiest solution for it is that move it from DMAEngine to rsnd
driver.
dma-names on DT was implemented as no difference between 1st / 2nd
DMAC's, since rsnd had assumed that both DMACs are implemented as
DMAEngine. That style was "src_dst". But now, 2nd DMAC was implemented
as non DMAEngine, and it doesn't need dma-names anymore. So, this
dma-names rule is no longer needed.
And additionally, dma-names was assumed that it has all
(= SSI/SSIU/SRC/DVC) nodes under sound node.
In upstream code, no SoC/platform is supporting DMA for rsnd driver yet.
This means there is no compatible issue if this patch changes
dma-names's rule of DT.
This patch assumes dma-names for 1st DMAC are tx/rx base, and listed
in each SSI/SRC/DVC subnode
ex)
rcar_sound,dvc {
dvc0: dvc@0 {
dmas = <&audma0 0xbc>;
dma-names = "tx";
};
...
rcar_sound,src {
src0: src@0 {
...
dmas = <&audma0 0x85>, <&audma1 0x9a>;
dma-names = "rx", "tx";
};
...
rcar_sound,ssi {
ssi0: ssi@0 {
...
dmas = <&audma0 0x01>, <&audma1 0x02>, <&audma0 0x15>, <&audma1 0x16>;
dma-names = "rx", "tx", "rxu", "txu";
};
...
Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
2015-02-20 18:31:23 +08:00
|
|
|
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
|
|
|
|
int is_play = rsnd_io_is_play(io);
|
|
|
|
char *name;
|
|
|
|
|
2015-10-22 11:15:46 +08:00
|
|
|
if (rsnd_ssi_use_busif(io))
|
ASoC: rsnd: 1st DMAC dma-names cares subnode
Renesas R-Car sound (= rsnd) needs 2 DMAC which are called as
Audio DMAC (= 1st DMAC) and Audio DMAC peri peri (2nd DMAC).
And rsnd had assumed that 1st / 2nd DMACs are implemented as DMAEngine.
But, in result of DMA ML discussion, 2nd DMAC was concluded that it is
not a general purpose DMAC (2nd DMAC is for Device to Device inside
sound system). Additionally, current DMAEngine can't support Device to
Device, and we don't have correct DT bindings for it at this point.
So the easiest solution for it is that move it from DMAEngine to rsnd
driver.
dma-names on DT was implemented as no difference between 1st / 2nd
DMAC's, since rsnd had assumed that both DMACs are implemented as
DMAEngine. That style was "src_dst". But now, 2nd DMAC was implemented
as non DMAEngine, and it doesn't need dma-names anymore. So, this
dma-names rule is no longer needed.
And additionally, dma-names was assumed that it has all
(= SSI/SSIU/SRC/DVC) nodes under sound node.
In upstream code, no SoC/platform is supporting DMA for rsnd driver yet.
This means there is no compatible issue if this patch changes
dma-names's rule of DT.
This patch assumes dma-names for 1st DMAC are tx/rx base, and listed
in each SSI/SRC/DVC subnode
ex)
rcar_sound,dvc {
dvc0: dvc@0 {
dmas = <&audma0 0xbc>;
dma-names = "tx";
};
...
rcar_sound,src {
src0: src@0 {
...
dmas = <&audma0 0x85>, <&audma1 0x9a>;
dma-names = "rx", "tx";
};
...
rcar_sound,ssi {
ssi0: ssi@0 {
...
dmas = <&audma0 0x01>, <&audma1 0x02>, <&audma0 0x15>, <&audma1 0x16>;
dma-names = "rx", "tx", "rxu", "txu";
};
...
Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
2015-02-20 18:31:23 +08:00
|
|
|
name = is_play ? "rxu" : "txu";
|
|
|
|
else
|
|
|
|
name = is_play ? "rx" : "tx";
|
|
|
|
|
|
|
|
return rsnd_dma_request_channel(rsnd_ssi_of_node(priv),
|
|
|
|
mod, name);
|
2014-06-23 08:56:23 +08:00
|
|
|
}
|
|
|
|
|
2013-07-29 09:59:02 +08:00
|
|
|
static struct rsnd_mod_ops rsnd_ssi_dma_ops = {
|
2014-05-23 14:25:43 +08:00
|
|
|
.name = SSI_NAME,
|
ASoC: rsnd: 1st DMAC dma-names cares subnode
Renesas R-Car sound (= rsnd) needs 2 DMAC which are called as
Audio DMAC (= 1st DMAC) and Audio DMAC peri peri (2nd DMAC).
And rsnd had assumed that 1st / 2nd DMACs are implemented as DMAEngine.
But, in result of DMA ML discussion, 2nd DMAC was concluded that it is
not a general purpose DMAC (2nd DMAC is for Device to Device inside
sound system). Additionally, current DMAEngine can't support Device to
Device, and we don't have correct DT bindings for it at this point.
So the easiest solution for it is that move it from DMAEngine to rsnd
driver.
dma-names on DT was implemented as no difference between 1st / 2nd
DMAC's, since rsnd had assumed that both DMACs are implemented as
DMAEngine. That style was "src_dst". But now, 2nd DMAC was implemented
as non DMAEngine, and it doesn't need dma-names anymore. So, this
dma-names rule is no longer needed.
And additionally, dma-names was assumed that it has all
(= SSI/SSIU/SRC/DVC) nodes under sound node.
In upstream code, no SoC/platform is supporting DMA for rsnd driver yet.
This means there is no compatible issue if this patch changes
dma-names's rule of DT.
This patch assumes dma-names for 1st DMAC are tx/rx base, and listed
in each SSI/SRC/DVC subnode
ex)
rcar_sound,dvc {
dvc0: dvc@0 {
dmas = <&audma0 0xbc>;
dma-names = "tx";
};
...
rcar_sound,src {
src0: src@0 {
...
dmas = <&audma0 0x85>, <&audma1 0x9a>;
dma-names = "rx", "tx";
};
...
rcar_sound,ssi {
ssi0: ssi@0 {
...
dmas = <&audma0 0x01>, <&audma1 0x02>, <&audma0 0x15>, <&audma1 0x16>;
dma-names = "rx", "tx", "rxu", "txu";
};
...
Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
2015-02-20 18:31:23 +08:00
|
|
|
.dma_req = rsnd_ssi_dma_req,
|
2014-03-04 12:50:49 +08:00
|
|
|
.probe = rsnd_ssi_dma_probe,
|
|
|
|
.remove = rsnd_ssi_dma_remove,
|
2013-07-29 09:59:02 +08:00
|
|
|
.init = rsnd_ssi_init,
|
|
|
|
.quit = rsnd_ssi_quit,
|
2015-10-26 16:43:01 +08:00
|
|
|
.start = rsnd_ssi_start,
|
|
|
|
.stop = rsnd_ssi_stop,
|
2014-11-27 16:02:43 +08:00
|
|
|
.fallback = rsnd_ssi_fallback,
|
2015-04-10 16:50:30 +08:00
|
|
|
.hw_params = rsnd_ssi_hw_params,
|
2013-07-29 09:59:02 +08:00
|
|
|
};
|
|
|
|
|
2014-11-27 16:05:01 +08:00
|
|
|
int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod)
|
|
|
|
{
|
|
|
|
return mod->ops == &rsnd_ssi_dma_ops;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-07-22 12:36:57 +08:00
|
|
|
/*
|
|
|
|
* Non SSI
|
|
|
|
*/
|
|
|
|
static struct rsnd_mod_ops rsnd_ssi_non_ops = {
|
2014-05-23 14:25:43 +08:00
|
|
|
.name = SSI_NAME,
|
2013-07-22 12:36:57 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ssi mod function
|
|
|
|
*/
|
2015-12-17 11:00:10 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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_mod *mod;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
node = rsnd_ssi_of_node(priv);
|
|
|
|
if (!node)
|
|
|
|
return;
|
|
|
|
|
|
|
|
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++;
|
|
|
|
}
|
|
|
|
|
|
|
|
of_node_put(node);
|
|
|
|
}
|
|
|
|
|
2013-07-22 12:36:57 +08:00
|
|
|
struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id)
|
|
|
|
{
|
2013-11-06 01:40:05 +08:00
|
|
|
if (WARN_ON(id < 0 || id >= rsnd_ssi_nr(priv)))
|
|
|
|
id = 0;
|
2013-07-22 12:36:57 +08:00
|
|
|
|
2015-11-10 13:11:35 +08:00
|
|
|
return rsnd_mod_get(rsnd_ssi_get(priv, id));
|
2013-07-22 12:36:57 +08:00
|
|
|
}
|
|
|
|
|
2015-10-22 11:15:46 +08:00
|
|
|
int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod)
|
2014-01-24 10:39:32 +08:00
|
|
|
{
|
|
|
|
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
|
|
|
|
|
|
|
|
return !!(rsnd_ssi_mode_flags(ssi) & RSND_SSI_CLK_PIN_SHARE);
|
|
|
|
}
|
|
|
|
|
2015-11-10 13:14:12 +08:00
|
|
|
int rsnd_ssi_probe(struct rsnd_priv *priv)
|
2013-07-22 12:36:57 +08:00
|
|
|
{
|
2015-11-10 13:11:35 +08:00
|
|
|
struct device_node *node;
|
|
|
|
struct device_node *np;
|
2013-07-22 12:36:57 +08:00
|
|
|
struct device *dev = rsnd_priv_to_dev(priv);
|
|
|
|
struct rsnd_mod_ops *ops;
|
|
|
|
struct clk *clk;
|
|
|
|
struct rsnd_ssi *ssi;
|
|
|
|
char name[RSND_SSI_NAME_SIZE];
|
2015-03-26 12:02:09 +08:00
|
|
|
int i, nr, ret;
|
2013-07-22 12:36:57 +08:00
|
|
|
|
2015-11-10 13:11:35 +08:00
|
|
|
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;
|
|
|
|
}
|
2014-03-18 10:29:55 +08:00
|
|
|
|
2014-01-24 10:39:40 +08:00
|
|
|
ssi = devm_kzalloc(dev, sizeof(*ssi) * nr, GFP_KERNEL);
|
2015-11-10 13:11:35 +08:00
|
|
|
if (!ssi) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto rsnd_ssi_probe_done;
|
|
|
|
}
|
2013-07-22 12:36:57 +08:00
|
|
|
|
2014-01-24 10:39:40 +08:00
|
|
|
priv->ssi = ssi;
|
|
|
|
priv->ssi_nr = nr;
|
2013-07-22 12:36:57 +08:00
|
|
|
|
2015-11-10 13:11:35 +08:00
|
|
|
i = 0;
|
|
|
|
for_each_child_of_node(node, np) {
|
|
|
|
ssi = rsnd_ssi_get(priv, i);
|
2013-07-22 12:36:57 +08:00
|
|
|
|
2014-05-23 14:25:43 +08:00
|
|
|
snprintf(name, RSND_SSI_NAME_SIZE, "%s.%d",
|
|
|
|
SSI_NAME, i);
|
2013-07-22 12:36:57 +08:00
|
|
|
|
2013-12-04 14:09:33 +08:00
|
|
|
clk = devm_clk_get(dev, name);
|
2015-11-10 13:11:35 +08:00
|
|
|
if (IS_ERR(clk)) {
|
|
|
|
ret = PTR_ERR(clk);
|
|
|
|
goto rsnd_ssi_probe_done;
|
|
|
|
}
|
2013-07-22 12:36:57 +08:00
|
|
|
|
2015-11-10 13:11:35 +08:00
|
|
|
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;
|
|
|
|
}
|
2013-07-22 12:36:57 +08:00
|
|
|
|
|
|
|
ops = &rsnd_ssi_non_ops;
|
2015-11-10 13:11:35 +08:00
|
|
|
if (of_get_property(np, "pio-transfer", NULL))
|
2014-03-04 12:50:49 +08:00
|
|
|
ops = &rsnd_ssi_pio_ops;
|
2015-11-10 13:11:35 +08:00
|
|
|
else
|
|
|
|
ops = &rsnd_ssi_dma_ops;
|
2013-07-22 12:36:57 +08:00
|
|
|
|
2015-09-10 15:02:21 +08:00
|
|
|
ret = rsnd_mod_init(priv, rsnd_mod_get(ssi), ops, clk,
|
|
|
|
RSND_MOD_SSI, i);
|
2015-03-26 12:02:09 +08:00
|
|
|
if (ret)
|
2015-11-10 13:11:35 +08:00
|
|
|
goto rsnd_ssi_probe_done;
|
|
|
|
|
|
|
|
i++;
|
2013-07-22 12:36:57 +08:00
|
|
|
}
|
|
|
|
|
2015-11-10 13:11:35 +08:00
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
rsnd_ssi_probe_done:
|
|
|
|
of_node_put(node);
|
|
|
|
|
|
|
|
return ret;
|
2013-07-22 12:36:57 +08:00
|
|
|
}
|
2015-03-26 12:02:09 +08:00
|
|
|
|
2015-11-10 13:14:12 +08:00
|
|
|
void rsnd_ssi_remove(struct rsnd_priv *priv)
|
2015-03-26 12:02:09 +08:00
|
|
|
{
|
|
|
|
struct rsnd_ssi *ssi;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for_each_rsnd_ssi(ssi, priv, i) {
|
2015-09-10 15:02:21 +08:00
|
|
|
rsnd_mod_quit(rsnd_mod_get(ssi));
|
2015-03-26 12:02:09 +08:00
|
|
|
}
|
|
|
|
}
|