2013-07-22 11:36:57 +07: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 08:59:02 +07:00
|
|
|
#define DMEN (1 << 28) /* DMA Enable */
|
2013-07-22 11:36:57 +07: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 */
|
|
|
|
|
|
|
|
#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 */
|
|
|
|
#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 08:59:02 +07:00
|
|
|
/*
|
|
|
|
* SSIWSR
|
|
|
|
*/
|
|
|
|
#define CONT (1 << 8) /* WS Continue Function */
|
|
|
|
|
2014-05-23 13:25:43 +07:00
|
|
|
#define SSI_NAME "ssi"
|
|
|
|
|
2013-07-22 11:36:57 +07:00
|
|
|
struct rsnd_ssi {
|
|
|
|
struct rsnd_ssi_platform_info *info; /* rcar_snd.h */
|
|
|
|
struct rsnd_ssi *parent;
|
|
|
|
struct rsnd_mod mod;
|
2015-10-26 15:42:25 +07:00
|
|
|
struct rsnd_mod *dma;
|
2013-07-22 11:36:57 +07:00
|
|
|
|
|
|
|
u32 cr_own;
|
|
|
|
u32 cr_clk;
|
2015-04-10 15:50:30 +07:00
|
|
|
int chan;
|
2013-07-22 11:36:57 +07:00
|
|
|
int err;
|
|
|
|
unsigned int usrcnt;
|
|
|
|
};
|
|
|
|
|
|
|
|
#define for_each_rsnd_ssi(pos, priv, i) \
|
|
|
|
for (i = 0; \
|
|
|
|
(i < rsnd_ssi_nr(priv)) && \
|
2014-01-24 09:39:40 +07:00
|
|
|
((pos) = ((struct rsnd_ssi *)(priv)->ssi + i)); \
|
2013-07-22 11:36:57 +07:00
|
|
|
i++)
|
|
|
|
|
2015-10-26 15:38:26 +07:00
|
|
|
#define rsnd_ssi_to_dma(mod) ((ssi)->dma)
|
2014-01-24 09:39:40 +07:00
|
|
|
#define rsnd_ssi_nr(priv) ((priv)->ssi_nr)
|
2013-07-22 11:36:57 +07:00
|
|
|
#define rsnd_mod_to_ssi(_mod) container_of((_mod), struct rsnd_ssi, mod)
|
2014-11-27 15:08:10 +07:00
|
|
|
#define rsnd_ssi_pio_available(ssi) ((ssi)->info->irq > 0)
|
2015-04-10 15:50:12 +07:00
|
|
|
#define rsnd_ssi_parent(ssi) ((ssi)->parent)
|
2013-07-22 11:36:57 +07:00
|
|
|
#define rsnd_ssi_mode_flags(p) ((p)->info->flags)
|
2013-07-29 08:58:29 +07:00
|
|
|
#define rsnd_ssi_dai_id(ssi) ((ssi)->info->dai_id)
|
2015-02-20 17:30:02 +07:00
|
|
|
#define rsnd_ssi_of_node(priv) \
|
|
|
|
of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,ssi")
|
2013-07-22 11:36:57 +07:00
|
|
|
|
2015-10-22 10:15:46 +07:00
|
|
|
int rsnd_ssi_use_busif(struct rsnd_dai_stream *io)
|
2014-06-23 07:56:23 +07:00
|
|
|
{
|
2015-10-22 10:15:46 +07:00
|
|
|
struct rsnd_mod *mod = rsnd_io_to_mod_ssi(io);
|
2014-06-23 07:56:23 +07:00
|
|
|
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
|
|
|
|
int use_busif = 0;
|
|
|
|
|
2014-11-27 15:05:54 +07:00
|
|
|
if (!rsnd_ssi_is_dma_mode(mod))
|
|
|
|
return 0;
|
|
|
|
|
2014-06-23 07:56:23 +07: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 15:42:09 +07: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 11:36:57 +07: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 15:42:09 +07:00
|
|
|
status = rsnd_ssi_status_get(mod);
|
2013-07-22 11:36:57 +07:00
|
|
|
if (status & bit)
|
|
|
|
return;
|
|
|
|
|
|
|
|
udelay(50);
|
|
|
|
}
|
|
|
|
|
|
|
|
dev_warn(dev, "status check failed\n");
|
|
|
|
}
|
|
|
|
|
2015-10-26 15:40:41 +07: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;
|
|
|
|
}
|
|
|
|
|
2013-07-22 11:36:57 +07:00
|
|
|
static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi,
|
2013-12-20 10:28:39 +07:00
|
|
|
struct rsnd_dai_stream *io)
|
2013-07-22 11:36:57 +07:00
|
|
|
{
|
2015-01-15 15:08:34 +07:00
|
|
|
struct rsnd_priv *priv = rsnd_io_to_priv(io);
|
2013-12-20 10:28:39 +07:00
|
|
|
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
|
2013-07-22 11:36:57 +07:00
|
|
|
struct device *dev = rsnd_priv_to_dev(priv);
|
2015-09-10 14:02:21 +07:00
|
|
|
struct rsnd_mod *mod = rsnd_mod_get(ssi);
|
2015-09-10 14:03:48 +07:00
|
|
|
int j, ret;
|
2013-07-22 11:36:57 +07:00
|
|
|
int ssi_clk_mul_table[] = {
|
|
|
|
1, 2, 4, 8, 16, 6, 12,
|
|
|
|
};
|
|
|
|
unsigned int main_rate;
|
2014-03-04 11:51:21 +07:00
|
|
|
unsigned int rate = rsnd_src_get_ssi_rate(priv, io, runtime);
|
2013-07-22 11:36:57 +07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Find best clock, and try to start ADG
|
|
|
|
*/
|
2015-09-10 14:03:48 +07:00
|
|
|
for (j = 0; j < ARRAY_SIZE(ssi_clk_mul_table); j++) {
|
|
|
|
|
|
|
|
/*
|
|
|
|
* this driver is assuming that
|
|
|
|
* system word is 64fs (= 2 x 32bit)
|
|
|
|
* see rsnd_ssi_init()
|
|
|
|
*/
|
|
|
|
main_rate = rate * 32 * 2 * 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);
|
|
|
|
|
|
|
|
dev_dbg(dev, "%s[%d] outputs %u Hz\n",
|
|
|
|
rsnd_mod_name(mod),
|
|
|
|
rsnd_mod_id(mod), rate);
|
|
|
|
|
|
|
|
return 0;
|
2013-07-22 11:36:57 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
dev_err(dev, "unsupported clock rate\n");
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void rsnd_ssi_master_clk_stop(struct rsnd_ssi *ssi)
|
|
|
|
{
|
2015-09-10 14:02:21 +07:00
|
|
|
struct rsnd_mod *mod = rsnd_mod_get(ssi);
|
|
|
|
|
2013-07-22 11:36:57 +07:00
|
|
|
ssi->cr_clk = 0;
|
2015-09-10 14:02:21 +07:00
|
|
|
rsnd_adg_ssi_clk_stop(mod);
|
2013-07-22 11:36:57 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi,
|
|
|
|
struct rsnd_dai_stream *io)
|
|
|
|
{
|
2015-01-15 15:08:34 +07:00
|
|
|
struct rsnd_priv *priv = rsnd_io_to_priv(io);
|
2015-01-15 15:07:19 +07:00
|
|
|
struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
|
2013-07-22 11:36:57 +07:00
|
|
|
struct device *dev = rsnd_priv_to_dev(priv);
|
2015-09-10 14:02:21 +07:00
|
|
|
struct rsnd_mod *mod = rsnd_mod_get(ssi);
|
2014-11-27 15:05:01 +07:00
|
|
|
u32 cr_mode;
|
2013-07-22 11:36:57 +07:00
|
|
|
u32 cr;
|
|
|
|
|
|
|
|
if (0 == ssi->usrcnt) {
|
2015-10-22 10:15:04 +07:00
|
|
|
rsnd_mod_power_on(mod);
|
2013-07-22 11:36:57 +07:00
|
|
|
|
2015-01-15 15:04:51 +07:00
|
|
|
if (rsnd_rdai_is_clk_master(rdai)) {
|
2015-04-10 15:50:12 +07:00
|
|
|
struct rsnd_ssi *ssi_parent = rsnd_ssi_parent(ssi);
|
|
|
|
|
|
|
|
if (ssi_parent)
|
|
|
|
rsnd_ssi_hw_start(ssi_parent, io);
|
2013-07-22 11:36:57 +07:00
|
|
|
else
|
2013-12-20 10:28:39 +07:00
|
|
|
rsnd_ssi_master_clk_start(ssi, io);
|
2013-07-22 11:36:57 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-09-10 14:02:21 +07:00
|
|
|
if (rsnd_ssi_is_dma_mode(mod)) {
|
2015-06-15 13:20:54 +07:00
|
|
|
cr_mode = UIEN | OIEN | /* over/under run */
|
|
|
|
DMEN; /* DMA : enable DMA */
|
|
|
|
} else {
|
|
|
|
cr_mode = DIEN; /* PIO : enable Data interrupt */
|
|
|
|
}
|
2014-11-27 15:05:01 +07:00
|
|
|
|
2013-07-22 11:36:57 +07:00
|
|
|
cr = ssi->cr_own |
|
|
|
|
ssi->cr_clk |
|
2014-11-27 15:05:01 +07:00
|
|
|
cr_mode |
|
2015-06-15 13:20:54 +07:00
|
|
|
EN;
|
2013-07-22 11:36:57 +07:00
|
|
|
|
2015-09-10 14:02:21 +07:00
|
|
|
rsnd_mod_write(mod, SSICR, cr);
|
2013-07-22 11:36:57 +07:00
|
|
|
|
2014-11-27 15:05:34 +07:00
|
|
|
/* enable WS continue */
|
2015-01-15 15:04:51 +07:00
|
|
|
if (rsnd_rdai_is_clk_master(rdai))
|
2015-09-10 14:02:21 +07:00
|
|
|
rsnd_mod_write(mod, SSIWSR, CONT);
|
2014-11-27 15:05:34 +07:00
|
|
|
|
2014-11-27 15:05:44 +07:00
|
|
|
/* clear error status */
|
2015-10-26 15:42:09 +07:00
|
|
|
rsnd_ssi_status_clear(mod);
|
2014-11-27 15:05:44 +07:00
|
|
|
|
2013-07-22 11:36:57 +07:00
|
|
|
ssi->usrcnt++;
|
|
|
|
|
2014-11-10 11:00:30 +07:00
|
|
|
dev_dbg(dev, "%s[%d] hw started\n",
|
2015-09-10 14:02:21 +07:00
|
|
|
rsnd_mod_name(mod), rsnd_mod_id(mod));
|
2013-07-22 11:36:57 +07:00
|
|
|
}
|
|
|
|
|
2015-06-15 13:26:56 +07:00
|
|
|
static void rsnd_ssi_hw_stop(struct rsnd_dai_stream *io, struct rsnd_ssi *ssi)
|
2013-07-22 11:36:57 +07:00
|
|
|
{
|
2015-09-10 14:02:21 +07:00
|
|
|
struct rsnd_mod *mod = rsnd_mod_get(ssi);
|
|
|
|
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
|
2015-01-15 15:07:19 +07:00
|
|
|
struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
|
2013-07-22 11:36:57 +07:00
|
|
|
struct device *dev = rsnd_priv_to_dev(priv);
|
|
|
|
u32 cr;
|
|
|
|
|
2015-05-21 10:49:13 +07:00
|
|
|
if (0 == ssi->usrcnt) {
|
|
|
|
dev_err(dev, "%s called without starting\n", __func__);
|
2013-07-22 11:36:57 +07:00
|
|
|
return;
|
2015-05-21 10:49:13 +07:00
|
|
|
}
|
2013-07-22 11:36:57 +07:00
|
|
|
|
|
|
|
ssi->usrcnt--;
|
|
|
|
|
|
|
|
if (0 == ssi->usrcnt) {
|
|
|
|
/*
|
|
|
|
* disable all IRQ,
|
|
|
|
* and, wait all data was sent
|
|
|
|
*/
|
|
|
|
cr = ssi->cr_own |
|
|
|
|
ssi->cr_clk;
|
|
|
|
|
2015-09-10 14:02:21 +07:00
|
|
|
rsnd_mod_write(mod, SSICR, cr | EN);
|
|
|
|
rsnd_ssi_status_check(mod, DIRQ);
|
2013-07-22 11:36:57 +07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* disable SSI,
|
|
|
|
* and, wait idle state
|
|
|
|
*/
|
2015-09-10 14:02:21 +07:00
|
|
|
rsnd_mod_write(mod, SSICR, cr); /* disabled all */
|
|
|
|
rsnd_ssi_status_check(mod, IIRQ);
|
2013-07-22 11:36:57 +07:00
|
|
|
|
2015-01-15 15:04:51 +07:00
|
|
|
if (rsnd_rdai_is_clk_master(rdai)) {
|
2015-04-10 15:50:12 +07:00
|
|
|
struct rsnd_ssi *ssi_parent = rsnd_ssi_parent(ssi);
|
|
|
|
|
|
|
|
if (ssi_parent)
|
2015-06-15 13:26:56 +07:00
|
|
|
rsnd_ssi_hw_stop(io, ssi_parent);
|
2013-07-22 11:36:57 +07:00
|
|
|
else
|
|
|
|
rsnd_ssi_master_clk_stop(ssi);
|
|
|
|
}
|
|
|
|
|
2015-10-22 10:15:04 +07:00
|
|
|
rsnd_mod_power_off(mod);
|
2015-04-10 15:50:30 +07:00
|
|
|
|
|
|
|
ssi->chan = 0;
|
2013-07-22 11:36:57 +07:00
|
|
|
}
|
|
|
|
|
2014-11-10 11:00:30 +07:00
|
|
|
dev_dbg(dev, "%s[%d] hw stopped\n",
|
2015-09-10 14:02:21 +07:00
|
|
|
rsnd_mod_name(mod), rsnd_mod_id(mod));
|
2013-07-22 11:36:57 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* SSI mod common functions
|
|
|
|
*/
|
|
|
|
static int rsnd_ssi_init(struct rsnd_mod *mod,
|
2015-06-15 13:25:20 +07:00
|
|
|
struct rsnd_dai_stream *io,
|
2015-01-15 15:07:47 +07:00
|
|
|
struct rsnd_priv *priv)
|
2013-07-22 11:36:57 +07:00
|
|
|
{
|
|
|
|
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
|
2015-01-15 15:07:47 +07:00
|
|
|
struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
|
2013-07-22 11:36:57 +07:00
|
|
|
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;
|
2015-01-15 15:06:49 +07:00
|
|
|
if (rsnd_io_is_play(io))
|
2013-07-22 11:36:57 +07:00
|
|
|
cr |= TRMD;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* set ssi parameter
|
|
|
|
*/
|
|
|
|
ssi->cr_own = cr;
|
|
|
|
ssi->err = -1; /* ignore 1st error */
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int rsnd_ssi_quit(struct rsnd_mod *mod,
|
2015-06-15 13:25:20 +07:00
|
|
|
struct rsnd_dai_stream *io,
|
2015-01-15 15:07:47 +07:00
|
|
|
struct rsnd_priv *priv)
|
2013-07-22 11:36:57 +07:00
|
|
|
{
|
|
|
|
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
|
|
|
|
struct device *dev = rsnd_priv_to_dev(priv);
|
|
|
|
|
|
|
|
if (ssi->err > 0)
|
2015-01-15 15:08:57 +07:00
|
|
|
dev_warn(dev, "%s[%d] under/over flow err = %d\n",
|
|
|
|
rsnd_mod_name(mod), rsnd_mod_id(mod), ssi->err);
|
2013-07-22 11:36:57 +07:00
|
|
|
|
|
|
|
ssi->cr_own = 0;
|
|
|
|
ssi->err = 0;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-04-10 15:50:30 +07:00
|
|
|
static int rsnd_ssi_hw_params(struct rsnd_mod *mod,
|
2015-06-15 13:25:20 +07:00
|
|
|
struct rsnd_dai_stream *io,
|
2015-04-10 15:50:30 +07:00
|
|
|
struct snd_pcm_substream *substream,
|
|
|
|
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) {
|
|
|
|
/*
|
|
|
|
* it is error if child <-> parent SSI uses
|
|
|
|
* different channels.
|
|
|
|
*/
|
|
|
|
if (ssi->chan != chan)
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* It will be removed on rsnd_ssi_hw_stop */
|
|
|
|
ssi->chan = chan;
|
|
|
|
if (ssi_parent)
|
2015-09-10 14:02:21 +07:00
|
|
|
return rsnd_ssi_hw_params(rsnd_mod_get(ssi_parent), io,
|
2015-06-15 13:25:20 +07:00
|
|
|
substream, params);
|
2015-04-10 15:50:30 +07:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-10-26 15:42:09 +07:00
|
|
|
static u32 rsnd_ssi_record_error(struct rsnd_ssi *ssi)
|
2013-07-22 11:36:57 +07:00
|
|
|
{
|
2015-09-10 14:02:21 +07:00
|
|
|
struct rsnd_mod *mod = rsnd_mod_get(ssi);
|
2015-10-26 15:42:09 +07:00
|
|
|
u32 status = rsnd_ssi_status_get(mod);
|
2015-09-10 14:02:21 +07:00
|
|
|
|
2013-07-22 11:36:57 +07:00
|
|
|
/* under/over flow error */
|
|
|
|
if (status & (UIRQ | OIRQ)) {
|
|
|
|
ssi->err++;
|
|
|
|
|
|
|
|
/* clear error status */
|
2015-10-26 15:42:09 +07:00
|
|
|
rsnd_ssi_status_clear(mod);
|
2013-07-22 11:36:57 +07:00
|
|
|
}
|
2015-10-26 15:42:09 +07:00
|
|
|
|
|
|
|
return status;
|
2013-07-22 11:36:57 +07:00
|
|
|
}
|
|
|
|
|
2014-11-27 15:07:47 +07:00
|
|
|
static int rsnd_ssi_start(struct rsnd_mod *mod,
|
2015-06-15 13:25:20 +07:00
|
|
|
struct rsnd_dai_stream *io,
|
2015-01-15 15:07:47 +07:00
|
|
|
struct rsnd_priv *priv)
|
2014-11-27 15:07:47 +07:00
|
|
|
{
|
|
|
|
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
|
|
|
|
|
2015-10-22 10:15:46 +07:00
|
|
|
rsnd_src_ssiu_start(mod, io, rsnd_ssi_use_busif(io));
|
2014-11-27 15:07:47 +07:00
|
|
|
|
2015-01-15 15:07:19 +07:00
|
|
|
rsnd_ssi_hw_start(ssi, io);
|
2014-11-27 15:07:47 +07:00
|
|
|
|
2015-10-26 15:40:41 +07:00
|
|
|
rsnd_ssi_irq_enable(mod);
|
2014-11-27 15:07:47 +07:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int rsnd_ssi_stop(struct rsnd_mod *mod,
|
2015-06-15 13:25:20 +07:00
|
|
|
struct rsnd_dai_stream *io,
|
2015-01-15 15:07:47 +07:00
|
|
|
struct rsnd_priv *priv)
|
2014-11-27 15:07:47 +07:00
|
|
|
{
|
|
|
|
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
|
|
|
|
|
2015-10-26 15:40:41 +07:00
|
|
|
rsnd_ssi_irq_disable(mod);
|
2014-11-27 15:07:47 +07:00
|
|
|
|
2015-10-26 15:42:09 +07:00
|
|
|
rsnd_ssi_record_error(ssi);
|
2014-11-27 15:07:47 +07:00
|
|
|
|
2015-06-15 13:26:56 +07:00
|
|
|
rsnd_ssi_hw_stop(io, ssi);
|
2014-11-27 15:07:47 +07:00
|
|
|
|
2015-06-15 13:26:08 +07:00
|
|
|
rsnd_src_ssiu_stop(mod, io);
|
2014-11-27 15:07:47 +07:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-06-15 13:26:56 +07:00
|
|
|
static void __rsnd_ssi_interrupt(struct rsnd_mod *mod,
|
|
|
|
struct rsnd_dai_stream *io)
|
2013-07-22 11:36:57 +07:00
|
|
|
{
|
2015-06-15 13:26:56 +07:00
|
|
|
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
|
2015-01-15 15:07:47 +07:00
|
|
|
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
|
2015-10-26 15:41:36 +07:00
|
|
|
struct device *dev = rsnd_priv_to_dev(priv);
|
2015-01-15 15:09:13 +07:00
|
|
|
int is_dma = rsnd_ssi_is_dma_mode(mod);
|
2015-05-21 10:50:23 +07:00
|
|
|
u32 status;
|
2015-06-15 13:21:15 +07:00
|
|
|
bool elapsed = false;
|
2015-05-21 10:50:23 +07:00
|
|
|
|
|
|
|
spin_lock(&priv->lock);
|
2013-07-22 11:36:57 +07:00
|
|
|
|
2015-05-21 10:50:23 +07:00
|
|
|
/* ignore all cases if not working */
|
2015-06-15 13:27:47 +07:00
|
|
|
if (!rsnd_io_is_working(io))
|
2015-05-21 10:50:23 +07:00
|
|
|
goto rsnd_ssi_interrupt_out;
|
|
|
|
|
2015-10-26 15:42:09 +07:00
|
|
|
status = rsnd_ssi_record_error(ssi);
|
2014-11-27 15:07:47 +07:00
|
|
|
|
|
|
|
/* PIO only */
|
2015-01-15 15:09:13 +07:00
|
|
|
if (!is_dma && (status & DIRQ)) {
|
2013-07-22 11:36:57 +07: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 15:06:49 +07:00
|
|
|
if (rsnd_io_is_play(io))
|
2014-01-24 09:41:44 +07:00
|
|
|
rsnd_mod_write(mod, SSITDR, *buf);
|
2013-07-22 11:36:57 +07:00
|
|
|
else
|
2014-01-24 09:41:44 +07:00
|
|
|
*buf = rsnd_mod_read(mod, SSIRDR);
|
2013-07-22 11:36:57 +07:00
|
|
|
|
2015-06-15 13:21:15 +07:00
|
|
|
elapsed = rsnd_dai_pointer_update(io, sizeof(*buf));
|
2014-11-27 15:07:47 +07:00
|
|
|
}
|
2013-07-22 11:36:57 +07:00
|
|
|
|
2015-06-15 13:20:54 +07:00
|
|
|
/* DMA only */
|
|
|
|
if (is_dma && (status & (UIRQ | OIRQ))) {
|
2014-11-27 15:07:47 +07:00
|
|
|
/*
|
|
|
|
* restart SSI
|
|
|
|
*/
|
|
|
|
dev_dbg(dev, "%s[%d] restart\n",
|
|
|
|
rsnd_mod_name(mod), rsnd_mod_id(mod));
|
2015-03-19 11:13:47 +07:00
|
|
|
|
2015-06-15 13:25:20 +07:00
|
|
|
rsnd_ssi_stop(mod, io, priv);
|
2015-10-26 15:41:36 +07:00
|
|
|
rsnd_ssi_start(mod, io, priv);
|
2013-07-22 11:36:57 +07:00
|
|
|
}
|
|
|
|
|
2015-10-26 15:41:36 +07: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-05-21 10:50:23 +07:00
|
|
|
rsnd_ssi_interrupt_out:
|
|
|
|
spin_unlock(&priv->lock);
|
|
|
|
|
2015-06-15 13:21:15 +07:00
|
|
|
if (elapsed)
|
|
|
|
rsnd_dai_period_elapsed(io);
|
2015-06-15 13:26:56 +07: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 13:21:15 +07:00
|
|
|
|
2014-11-27 15:07:47 +07:00
|
|
|
return IRQ_HANDLED;
|
2013-07-22 11:36:57 +07:00
|
|
|
}
|
|
|
|
|
2014-11-27 15:08:10 +07:00
|
|
|
/*
|
|
|
|
* SSI PIO
|
|
|
|
*/
|
2014-03-04 11:50:49 +07:00
|
|
|
static int rsnd_ssi_pio_probe(struct rsnd_mod *mod,
|
2015-06-15 13:25:20 +07:00
|
|
|
struct rsnd_dai_stream *io,
|
2015-01-15 15:07:47 +07:00
|
|
|
struct rsnd_priv *priv)
|
2014-03-04 11:50:49 +07:00
|
|
|
{
|
|
|
|
struct device *dev = rsnd_priv_to_dev(priv);
|
|
|
|
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
|
|
|
|
int ret;
|
|
|
|
|
2014-11-27 15:08:10 +07:00
|
|
|
ret = devm_request_irq(dev, ssi->info->irq,
|
|
|
|
rsnd_ssi_interrupt,
|
2014-03-04 11:50:49 +07:00
|
|
|
IRQF_SHARED,
|
2015-06-15 13:26:56 +07:00
|
|
|
dev_name(dev), mod);
|
2014-05-23 13:25:43 +07:00
|
|
|
|
2014-03-04 11:50:49 +07:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2013-07-22 11:36:57 +07:00
|
|
|
static struct rsnd_mod_ops rsnd_ssi_pio_ops = {
|
2014-05-23 13:25:43 +07:00
|
|
|
.name = SSI_NAME,
|
2014-03-04 11:50:49 +07:00
|
|
|
.probe = rsnd_ssi_pio_probe,
|
2013-07-22 11:36:57 +07:00
|
|
|
.init = rsnd_ssi_init,
|
|
|
|
.quit = rsnd_ssi_quit,
|
2014-11-27 15:07:17 +07:00
|
|
|
.start = rsnd_ssi_start,
|
|
|
|
.stop = rsnd_ssi_stop,
|
2015-04-10 15:50:30 +07:00
|
|
|
.hw_params = rsnd_ssi_hw_params,
|
2013-07-22 11:36:57 +07:00
|
|
|
};
|
|
|
|
|
2014-03-04 11:50:49 +07:00
|
|
|
static int rsnd_ssi_dma_probe(struct rsnd_mod *mod,
|
2015-06-15 13:25:20 +07:00
|
|
|
struct rsnd_dai_stream *io,
|
2015-01-15 15:07:47 +07:00
|
|
|
struct rsnd_priv *priv)
|
2014-03-04 11:50:49 +07:00
|
|
|
{
|
|
|
|
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 ret;
|
|
|
|
|
2014-11-27 15:08:10 +07:00
|
|
|
ret = devm_request_irq(dev, ssi->info->irq,
|
|
|
|
rsnd_ssi_interrupt,
|
2014-11-27 15:07:47 +07:00
|
|
|
IRQF_SHARED,
|
2015-06-15 13:26:56 +07:00
|
|
|
dev_name(dev), mod);
|
2014-11-27 15:07:47 +07:00
|
|
|
if (ret)
|
2015-03-26 11:02:32 +07:00
|
|
|
return ret;
|
2014-11-27 15:07:47 +07:00
|
|
|
|
2015-10-26 15:39:20 +07:00
|
|
|
ssi->dma = rsnd_dma_attach(io, mod, dma_id);
|
2015-10-26 15:38:26 +07:00
|
|
|
if (IS_ERR(ssi->dma))
|
|
|
|
return PTR_ERR(ssi->dma);
|
2014-05-23 13:25:43 +07:00
|
|
|
|
2014-03-04 11:50:49 +07:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int rsnd_ssi_dma_remove(struct rsnd_mod *mod,
|
2015-06-15 13:25:20 +07:00
|
|
|
struct rsnd_dai_stream *io,
|
2015-01-15 15:07:47 +07:00
|
|
|
struct rsnd_priv *priv)
|
2014-11-27 15:02:43 +07:00
|
|
|
{
|
2014-11-27 15:07:47 +07:00
|
|
|
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
|
|
|
|
struct device *dev = rsnd_priv_to_dev(priv);
|
2014-11-27 15:08:10 +07:00
|
|
|
int irq = ssi->info->irq;
|
2014-11-27 15:07:47 +07:00
|
|
|
|
|
|
|
/* PIO will request IRQ again */
|
2015-10-22 10:13:44 +07:00
|
|
|
devm_free_irq(dev, irq, mod);
|
2014-11-27 15:07:47 +07:00
|
|
|
|
2014-11-27 15:02:43 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int rsnd_ssi_fallback(struct rsnd_mod *mod,
|
2015-06-15 13:25:20 +07:00
|
|
|
struct rsnd_dai_stream *io,
|
2015-01-15 15:07:47 +07:00
|
|
|
struct rsnd_priv *priv)
|
2014-03-04 11:50:49 +07:00
|
|
|
{
|
2014-11-10 11:00:58 +07: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 11:50:49 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-06-15 13:26:25 +07:00
|
|
|
static struct dma_chan *rsnd_ssi_dma_req(struct rsnd_dai_stream *io,
|
|
|
|
struct rsnd_mod *mod)
|
2014-06-23 07:56:23 +07: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 17:31:23 +07:00
|
|
|
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
|
|
|
|
int is_play = rsnd_io_is_play(io);
|
|
|
|
char *name;
|
|
|
|
|
2015-10-22 10:15:46 +07: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 17:31:23 +07: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 07:56:23 +07:00
|
|
|
}
|
|
|
|
|
2013-07-29 08:59:02 +07:00
|
|
|
static struct rsnd_mod_ops rsnd_ssi_dma_ops = {
|
2014-05-23 13:25:43 +07: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 17:31:23 +07:00
|
|
|
.dma_req = rsnd_ssi_dma_req,
|
2014-03-04 11:50:49 +07:00
|
|
|
.probe = rsnd_ssi_dma_probe,
|
|
|
|
.remove = rsnd_ssi_dma_remove,
|
2013-07-29 08:59:02 +07:00
|
|
|
.init = rsnd_ssi_init,
|
|
|
|
.quit = rsnd_ssi_quit,
|
2015-10-26 15:43:01 +07:00
|
|
|
.start = rsnd_ssi_start,
|
|
|
|
.stop = rsnd_ssi_stop,
|
2014-11-27 15:02:43 +07:00
|
|
|
.fallback = rsnd_ssi_fallback,
|
2015-04-10 15:50:30 +07:00
|
|
|
.hw_params = rsnd_ssi_hw_params,
|
2013-07-29 08:59:02 +07:00
|
|
|
};
|
|
|
|
|
2014-11-27 15:05:01 +07:00
|
|
|
int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod)
|
|
|
|
{
|
|
|
|
return mod->ops == &rsnd_ssi_dma_ops;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-07-22 11:36:57 +07:00
|
|
|
/*
|
|
|
|
* Non SSI
|
|
|
|
*/
|
|
|
|
static struct rsnd_mod_ops rsnd_ssi_non_ops = {
|
2014-05-23 13:25:43 +07:00
|
|
|
.name = SSI_NAME,
|
2013-07-22 11:36:57 +07:00
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ssi mod function
|
|
|
|
*/
|
|
|
|
struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id)
|
|
|
|
{
|
2013-11-06 00:40:05 +07:00
|
|
|
if (WARN_ON(id < 0 || id >= rsnd_ssi_nr(priv)))
|
|
|
|
id = 0;
|
2013-07-22 11:36:57 +07:00
|
|
|
|
2015-09-10 14:02:21 +07:00
|
|
|
return rsnd_mod_get((struct rsnd_ssi *)(priv->ssi) + id);
|
2013-07-22 11:36:57 +07:00
|
|
|
}
|
|
|
|
|
2015-10-22 10:15:46 +07:00
|
|
|
int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod)
|
2014-01-24 09:39:32 +07:00
|
|
|
{
|
|
|
|
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
|
|
|
|
|
|
|
|
return !!(rsnd_ssi_mode_flags(ssi) & RSND_SSI_CLK_PIN_SHARE);
|
|
|
|
}
|
|
|
|
|
2015-04-10 15:50:12 +07:00
|
|
|
static void rsnd_ssi_parent_setup(struct rsnd_priv *priv, struct rsnd_ssi *ssi)
|
2014-01-24 09:39:32 +07:00
|
|
|
{
|
2015-09-10 14:02:21 +07:00
|
|
|
struct rsnd_mod *mod = rsnd_mod_get(ssi);
|
|
|
|
|
2015-10-22 10:15:46 +07:00
|
|
|
if (!__rsnd_ssi_is_pin_sharing(mod))
|
2014-01-24 09:39:32 +07:00
|
|
|
return;
|
|
|
|
|
2015-09-10 14:02:21 +07:00
|
|
|
switch (rsnd_mod_id(mod)) {
|
2014-01-24 09:39:32 +07:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-18 09:29:55 +07:00
|
|
|
|
|
|
|
static void rsnd_of_parse_ssi(struct platform_device *pdev,
|
|
|
|
const struct rsnd_of_data *of_data,
|
|
|
|
struct rsnd_priv *priv)
|
|
|
|
{
|
|
|
|
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;
|
|
|
|
|
2015-02-20 17:30:02 +07:00
|
|
|
node = rsnd_ssi_of_node(priv);
|
2014-03-18 09:29:55 +07:00
|
|
|
if (!node)
|
|
|
|
return;
|
|
|
|
|
|
|
|
nr = of_get_child_count(node);
|
|
|
|
if (!nr)
|
2014-05-23 13:24:59 +07:00
|
|
|
goto rsnd_of_parse_ssi_end;
|
2014-03-18 09:29:55 +07:00
|
|
|
|
|
|
|
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");
|
2014-05-23 13:24:59 +07:00
|
|
|
goto rsnd_of_parse_ssi_end;
|
2014-03-18 09:29:55 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
info->ssi_info = ssi_info;
|
|
|
|
info->ssi_info_nr = nr;
|
|
|
|
|
|
|
|
i = -1;
|
|
|
|
for_each_child_of_node(node, np) {
|
|
|
|
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;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* irq
|
|
|
|
*/
|
2014-11-27 15:08:10 +07:00
|
|
|
ssi_info->irq = irq_of_parse_and_map(np, 0);
|
2014-05-23 13:25:49 +07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* DMA
|
|
|
|
*/
|
|
|
|
ssi_info->dma_id = of_get_property(np, "pio-transfer", NULL) ?
|
|
|
|
0 : 1;
|
2014-06-23 07:56:23 +07:00
|
|
|
|
|
|
|
if (of_get_property(np, "no-busif", NULL))
|
|
|
|
ssi_info->flags |= RSND_SSI_NO_BUSIF;
|
2014-03-18 09:29:55 +07:00
|
|
|
}
|
2014-05-23 13:24:59 +07:00
|
|
|
|
|
|
|
rsnd_of_parse_ssi_end:
|
|
|
|
of_node_put(node);
|
2014-03-18 09:29:55 +07:00
|
|
|
}
|
|
|
|
|
2013-07-22 11:36:57 +07:00
|
|
|
int rsnd_ssi_probe(struct platform_device *pdev,
|
2014-03-18 09:29:55 +07:00
|
|
|
const struct rsnd_of_data *of_data,
|
2013-07-22 11:36:57 +07:00
|
|
|
struct rsnd_priv *priv)
|
|
|
|
{
|
2014-02-25 13:15:00 +07:00
|
|
|
struct rcar_snd_info *info = rsnd_priv_to_info(priv);
|
2013-07-22 11:36:57 +07:00
|
|
|
struct rsnd_ssi_platform_info *pinfo;
|
|
|
|
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 11:02:09 +07:00
|
|
|
int i, nr, ret;
|
2013-07-22 11:36:57 +07:00
|
|
|
|
2014-03-18 09:29:55 +07:00
|
|
|
rsnd_of_parse_ssi(pdev, of_data, priv);
|
|
|
|
|
2013-07-22 11:36:57 +07:00
|
|
|
/*
|
|
|
|
* init SSI
|
|
|
|
*/
|
|
|
|
nr = info->ssi_info_nr;
|
2014-01-24 09:39:40 +07:00
|
|
|
ssi = devm_kzalloc(dev, sizeof(*ssi) * nr, GFP_KERNEL);
|
2015-07-15 14:08:24 +07:00
|
|
|
if (!ssi)
|
2013-07-22 11:36:57 +07:00
|
|
|
return -ENOMEM;
|
|
|
|
|
2014-01-24 09:39:40 +07:00
|
|
|
priv->ssi = ssi;
|
|
|
|
priv->ssi_nr = nr;
|
2013-07-22 11:36:57 +07:00
|
|
|
|
|
|
|
for_each_rsnd_ssi(ssi, priv, i) {
|
|
|
|
pinfo = &info->ssi_info[i];
|
|
|
|
|
2014-05-23 13:25:43 +07:00
|
|
|
snprintf(name, RSND_SSI_NAME_SIZE, "%s.%d",
|
|
|
|
SSI_NAME, i);
|
2013-07-22 11:36:57 +07:00
|
|
|
|
2013-12-04 13:09:33 +07:00
|
|
|
clk = devm_clk_get(dev, name);
|
2013-07-22 11:36:57 +07:00
|
|
|
if (IS_ERR(clk))
|
|
|
|
return PTR_ERR(clk);
|
|
|
|
|
|
|
|
ssi->info = pinfo;
|
|
|
|
|
|
|
|
ops = &rsnd_ssi_non_ops;
|
2014-03-04 11:50:49 +07:00
|
|
|
if (pinfo->dma_id > 0)
|
|
|
|
ops = &rsnd_ssi_dma_ops;
|
|
|
|
else if (rsnd_ssi_pio_available(ssi))
|
|
|
|
ops = &rsnd_ssi_pio_ops;
|
2013-07-22 11:36:57 +07:00
|
|
|
|
2015-09-10 14:02:21 +07:00
|
|
|
ret = rsnd_mod_init(priv, rsnd_mod_get(ssi), ops, clk,
|
|
|
|
RSND_MOD_SSI, i);
|
2015-03-26 11:02:09 +07:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
2014-01-24 09:39:32 +07:00
|
|
|
|
2015-04-10 15:50:12 +07:00
|
|
|
rsnd_ssi_parent_setup(priv, ssi);
|
2013-07-22 11:36:57 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2015-03-26 11:02:09 +07:00
|
|
|
|
|
|
|
void rsnd_ssi_remove(struct platform_device *pdev,
|
|
|
|
struct rsnd_priv *priv)
|
|
|
|
{
|
|
|
|
struct rsnd_ssi *ssi;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for_each_rsnd_ssi(ssi, priv, i) {
|
2015-09-10 14:02:21 +07:00
|
|
|
rsnd_mod_quit(rsnd_mod_get(ssi));
|
2015-03-26 11:02:09 +07:00
|
|
|
}
|
|
|
|
}
|