aboutsummaryrefslogtreecommitdiff
path: root/sound/soc/sh/fsi.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/sh/fsi.c')
-rw-r--r--sound/soc/sh/fsi.c1722
1 files changed, 1236 insertions, 486 deletions
diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c
index ea4a82d0116..710a079a737 100644
--- a/sound/soc/sh/fsi.c
+++ b/sound/soc/sh/fsi.c
@@ -13,11 +13,18 @@
*/
#include <linux/delay.h>
+#include <linux/dma-mapping.h>
#include <linux/pm_runtime.h>
#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/scatterlist.h>
+#include <linux/sh_dma.h>
#include <linux/slab.h>
#include <linux/module.h>
+#include <linux/workqueue.h>
#include <sound/soc.h>
+#include <sound/pcm_params.h>
#include <sound/sh_fsi.h>
/* PortA/PortB register */
@@ -53,6 +60,7 @@
/* DO_FMT */
/* DI_FMT */
+#define CR_BWS_MASK (0x3 << 20) /* FSI2 */
#define CR_BWS_24 (0x0 << 20) /* FSI2 */
#define CR_BWS_16 (0x1 << 20) /* FSI2 */
#define CR_BWS_20 (0x2 << 20) /* FSI2 */
@@ -68,6 +76,15 @@
#define CR_TDM (0x4 << 4)
#define CR_TDM_D (0x5 << 4)
+/* OUT_DMAC */
+/* IN_DMAC */
+#define VDMD_MASK (0x3 << 4)
+#define VDMD_FRONT (0x0 << 4) /* Package in front */
+#define VDMD_BACK (0x1 << 4) /* Package in back */
+#define VDMD_STREAM (0x2 << 4) /* Stream mode(16bit * 2) */
+
+#define DMA_ON (0x1 << 0)
+
/* DOFF_CTL */
/* DIFF_CTL */
#define IRQ_HALF 0x00100000
@@ -116,7 +133,24 @@
#define FSI_FMTS (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE)
-typedef int (*set_rate_func)(struct device *dev, int is_porta, int rate, int enable);
+/*
+ * bus options
+ *
+ * 0x000000BA
+ *
+ * A : sample widtht 16bit setting
+ * B : sample widtht 24bit setting
+ */
+
+#define SHIFT_16DATA 0
+#define SHIFT_24DATA 4
+
+#define PACKAGE_24BITBUS_BACK 0
+#define PACKAGE_24BITBUS_FRONT 1
+#define PACKAGE_16BITBUS_STREAM 2
+
+#define BUSOP_SET(s, a) ((a) << SHIFT_ ## s ## DATA)
+#define BUSOP_GET(s, a) (((a) >> SHIFT_ ## s ## DATA) & 0xF)
/*
* FSI driver use below type name for variable
@@ -156,20 +190,66 @@ typedef int (*set_rate_func)(struct device *dev, int is_porta, int rate, int ena
*/
/*
+ * FSI clock
+ *
+ * FSIxCLK [CPG] (ick) -------> |
+ * |-> FSI_DIV (div)-> FSI2
+ * FSIxCK [external] (xck) ---> |
+ */
+
+/*
* struct
*/
+struct fsi_stream_handler;
struct fsi_stream {
- struct snd_pcm_substream *substream;
+ /*
+ * these are initialized by fsi_stream_init()
+ */
+ struct snd_pcm_substream *substream;
int fifo_sample_capa; /* sample capacity of FSI FIFO */
int buff_sample_capa; /* sample capacity of ALSA buffer */
int buff_sample_pos; /* sample position of ALSA buffer */
int period_samples; /* sample number / 1 period */
int period_pos; /* current period position */
-
+ int sample_width; /* sample width */
int uerr_num;
int oerr_num;
+
+ /*
+ * bus options
+ */
+ u32 bus_option;
+
+ /*
+ * thse are initialized by fsi_handler_init()
+ */
+ struct fsi_stream_handler *handler;
+ struct fsi_priv *priv;
+
+ /*
+ * these are for DMAEngine
+ */
+ struct dma_chan *chan;
+ struct work_struct work;
+ dma_addr_t dma;
+ int dma_id;
+ int loop_cnt;
+ int additional_pos;
+};
+
+struct fsi_clk {
+ /* see [FSI clock] */
+ struct clk *own;
+ struct clk *xck;
+ struct clk *ick;
+ struct clk *div;
+ int (*set_rate)(struct device *dev,
+ struct fsi_priv *fsi);
+
+ unsigned long rate;
+ unsigned int count;
};
struct fsi_priv {
@@ -179,15 +259,32 @@ struct fsi_priv {
struct fsi_stream playback;
struct fsi_stream capture;
- u32 do_fmt;
- u32 di_fmt;
+ struct fsi_clk clock;
+
+ u32 fmt;
int chan_num:16;
int clk_master:1;
+ int clk_cpg:1;
int spdif:1;
+ int enable_stream:1;
+ int bit_clk_inv:1;
+ int lr_clk_inv:1;
+};
- long rate;
+struct fsi_stream_handler {
+ int (*init)(struct fsi_priv *fsi, struct fsi_stream *io);
+ int (*quit)(struct fsi_priv *fsi, struct fsi_stream *io);
+ int (*probe)(struct fsi_priv *fsi, struct fsi_stream *io, struct device *dev);
+ int (*transfer)(struct fsi_priv *fsi, struct fsi_stream *io);
+ int (*remove)(struct fsi_priv *fsi, struct fsi_stream *io);
+ int (*start_stop)(struct fsi_priv *fsi, struct fsi_stream *io,
+ int enable);
};
+#define fsi_stream_handler_call(io, func, args...) \
+ (!(io) ? -ENODEV : \
+ !((io)->handler->func) ? 0 : \
+ (io)->handler->func(args))
struct fsi_core {
int ver;
@@ -201,14 +298,14 @@ struct fsi_core {
struct fsi_master {
void __iomem *base;
- int irq;
struct fsi_priv fsia;
struct fsi_priv fsib;
- struct fsi_core *core;
- struct sh_fsi_platform_info *info;
+ const struct fsi_core *core;
spinlock_t lock;
};
+static int fsi_stream_is_play(struct fsi_priv *fsi, struct fsi_stream *io);
+
/*
* basic read write function
*/
@@ -274,6 +371,10 @@ static void _fsi_master_mask_set(struct fsi_master *master,
/*
* basic function
*/
+static int fsi_version(struct fsi_master *master)
+{
+ return master->core->ver;
+}
static struct fsi_master *fsi_get_master(struct fsi_priv *fsi)
{
@@ -295,6 +396,16 @@ static int fsi_is_spdif(struct fsi_priv *fsi)
return fsi->spdif;
}
+static int fsi_is_enable_stream(struct fsi_priv *fsi)
+{
+ return fsi->enable_stream;
+}
+
+static int fsi_is_play(struct snd_pcm_substream *substream)
+{
+ return substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+}
+
static struct snd_soc_dai *fsi_get_dai(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
@@ -317,85 +428,105 @@ static struct fsi_priv *fsi_get_priv(struct snd_pcm_substream *substream)
return fsi_get_priv_frm_dai(fsi_get_dai(substream));
}
-static set_rate_func fsi_get_info_set_rate(struct fsi_master *master)
-{
- if (!master->info)
- return NULL;
-
- return master->info->set_rate;
-}
-
-static u32 fsi_get_info_flags(struct fsi_priv *fsi)
+static u32 fsi_get_port_shift(struct fsi_priv *fsi, struct fsi_stream *io)
{
+ int is_play = fsi_stream_is_play(fsi, io);
int is_porta = fsi_is_port_a(fsi);
- struct fsi_master *master = fsi_get_master(fsi);
+ u32 shift;
- if (!master->info)
- return 0;
+ if (is_porta)
+ shift = is_play ? AO_SHIFT : AI_SHIFT;
+ else
+ shift = is_play ? BO_SHIFT : BI_SHIFT;
- return is_porta ? master->info->porta_flags :
- master->info->portb_flags;
+ return shift;
}
-static inline int fsi_stream_is_play(int stream)
+static int fsi_frame2sample(struct fsi_priv *fsi, int frames)
{
- return stream == SNDRV_PCM_STREAM_PLAYBACK;
+ return frames * fsi->chan_num;
}
-static inline int fsi_is_play(struct snd_pcm_substream *substream)
+static int fsi_sample2frame(struct fsi_priv *fsi, int samples)
{
- return fsi_stream_is_play(substream->stream);
+ return samples / fsi->chan_num;
}
-static inline struct fsi_stream *fsi_get_stream(struct fsi_priv *fsi,
- int is_play)
+static int fsi_get_current_fifo_samples(struct fsi_priv *fsi,
+ struct fsi_stream *io)
{
- return is_play ? &fsi->playback : &fsi->capture;
+ int is_play = fsi_stream_is_play(fsi, io);
+ u32 status;
+ int frames;
+
+ status = is_play ?
+ fsi_reg_read(fsi, DOFF_ST) :
+ fsi_reg_read(fsi, DIFF_ST);
+
+ frames = 0x1ff & (status >> 8);
+
+ return fsi_frame2sample(fsi, frames);
}
-static u32 fsi_get_port_shift(struct fsi_priv *fsi, int is_play)
+static void fsi_count_fifo_err(struct fsi_priv *fsi)
{
- int is_porta = fsi_is_port_a(fsi);
- u32 shift;
+ u32 ostatus = fsi_reg_read(fsi, DOFF_ST);
+ u32 istatus = fsi_reg_read(fsi, DIFF_ST);
- if (is_porta)
- shift = is_play ? AO_SHIFT : AI_SHIFT;
- else
- shift = is_play ? BO_SHIFT : BI_SHIFT;
+ if (ostatus & ERR_OVER)
+ fsi->playback.oerr_num++;
- return shift;
+ if (ostatus & ERR_UNDER)
+ fsi->playback.uerr_num++;
+
+ if (istatus & ERR_OVER)
+ fsi->capture.oerr_num++;
+
+ if (istatus & ERR_UNDER)
+ fsi->capture.uerr_num++;
+
+ fsi_reg_write(fsi, DOFF_ST, 0);
+ fsi_reg_write(fsi, DIFF_ST, 0);
}
-static int fsi_frame2sample(struct fsi_priv *fsi, int frames)
+/*
+ * fsi_stream_xx() function
+ */
+static inline int fsi_stream_is_play(struct fsi_priv *fsi,
+ struct fsi_stream *io)
{
- return frames * fsi->chan_num;
+ return &fsi->playback == io;
}
-static int fsi_sample2frame(struct fsi_priv *fsi, int samples)
+static inline struct fsi_stream *fsi_stream_get(struct fsi_priv *fsi,
+ struct snd_pcm_substream *substream)
{
- return samples / fsi->chan_num;
+ return fsi_is_play(substream) ? &fsi->playback : &fsi->capture;
}
static int fsi_stream_is_working(struct fsi_priv *fsi,
- int is_play)
+ struct fsi_stream *io)
{
- struct fsi_stream *io = fsi_get_stream(fsi, is_play);
struct fsi_master *master = fsi_get_master(fsi);
unsigned long flags;
int ret;
spin_lock_irqsave(&master->lock, flags);
- ret = !!io->substream;
+ ret = !!(io->substream && io->substream->runtime);
spin_unlock_irqrestore(&master->lock, flags);
return ret;
}
-static void fsi_stream_push(struct fsi_priv *fsi,
- int is_play,
+static struct fsi_priv *fsi_stream_to_priv(struct fsi_stream *io)
+{
+ return io->priv;
+}
+
+static void fsi_stream_init(struct fsi_priv *fsi,
+ struct fsi_stream *io,
struct snd_pcm_substream *substream)
{
- struct fsi_stream *io = fsi_get_stream(fsi, is_play);
struct snd_pcm_runtime *runtime = substream->runtime;
struct fsi_master *master = fsi_get_master(fsi);
unsigned long flags;
@@ -406,14 +537,16 @@ static void fsi_stream_push(struct fsi_priv *fsi,
io->buff_sample_pos = 0;
io->period_samples = fsi_frame2sample(fsi, runtime->period_size);
io->period_pos = 0;
+ io->sample_width = samples_to_bytes(runtime, 1);
+ io->bus_option = 0;
io->oerr_num = -1; /* ignore 1st err */
io->uerr_num = -1; /* ignore 1st err */
+ fsi_stream_handler_call(io, init, fsi, io);
spin_unlock_irqrestore(&master->lock, flags);
}
-static void fsi_stream_pop(struct fsi_priv *fsi, int is_play)
+static void fsi_stream_quit(struct fsi_priv *fsi, struct fsi_stream *io)
{
- struct fsi_stream *io = fsi_get_stream(fsi, is_play);
struct snd_soc_dai *dai = fsi_get_dai(io->substream);
struct fsi_master *master = fsi_get_master(fsi);
unsigned long flags;
@@ -426,127 +559,135 @@ static void fsi_stream_pop(struct fsi_priv *fsi, int is_play)
if (io->uerr_num > 0)
dev_err(dai->dev, "under_run = %d\n", io->uerr_num);
+ fsi_stream_handler_call(io, quit, fsi, io);
io->substream = NULL;
io->buff_sample_capa = 0;
io->buff_sample_pos = 0;
io->period_samples = 0;
io->period_pos = 0;
+ io->sample_width = 0;
+ io->bus_option = 0;
io->oerr_num = 0;
io->uerr_num = 0;
spin_unlock_irqrestore(&master->lock, flags);
}
-static int fsi_get_current_fifo_samples(struct fsi_priv *fsi, int is_play)
+static int fsi_stream_transfer(struct fsi_stream *io)
{
- u32 status;
- int frames;
+ struct fsi_priv *fsi = fsi_stream_to_priv(io);
+ if (!fsi)
+ return -EIO;
- status = is_play ?
- fsi_reg_read(fsi, DOFF_ST) :
- fsi_reg_read(fsi, DIFF_ST);
-
- frames = 0x1ff & (status >> 8);
-
- return fsi_frame2sample(fsi, frames);
+ return fsi_stream_handler_call(io, transfer, fsi, io);
}
-static void fsi_count_fifo_err(struct fsi_priv *fsi)
-{
- u32 ostatus = fsi_reg_read(fsi, DOFF_ST);
- u32 istatus = fsi_reg_read(fsi, DIFF_ST);
-
- if (ostatus & ERR_OVER)
- fsi->playback.oerr_num++;
-
- if (ostatus & ERR_UNDER)
- fsi->playback.uerr_num++;
-
- if (istatus & ERR_OVER)
- fsi->capture.oerr_num++;
-
- if (istatus & ERR_UNDER)
- fsi->capture.uerr_num++;
-
- fsi_reg_write(fsi, DOFF_ST, 0);
- fsi_reg_write(fsi, DIFF_ST, 0);
-}
+#define fsi_stream_start(fsi, io)\
+ fsi_stream_handler_call(io, start_stop, fsi, io, 1)
-/*
- * dma function
- */
+#define fsi_stream_stop(fsi, io)\
+ fsi_stream_handler_call(io, start_stop, fsi, io, 0)
-static u8 *fsi_dma_get_area(struct fsi_priv *fsi, int stream)
+static int fsi_stream_probe(struct fsi_priv *fsi, struct device *dev)
{
- int is_play = fsi_stream_is_play(stream);
- struct fsi_stream *io = fsi_get_stream(fsi, is_play);
- struct snd_pcm_runtime *runtime = io->substream->runtime;
+ struct fsi_stream *io;
+ int ret1, ret2;
- return runtime->dma_area +
- samples_to_bytes(runtime, io->buff_sample_pos);
-}
+ io = &fsi->playback;
+ ret1 = fsi_stream_handler_call(io, probe, fsi, io, dev);
-static void fsi_dma_soft_push16(struct fsi_priv *fsi, int num)
-{
- u16 *start;
- int i;
+ io = &fsi->capture;
+ ret2 = fsi_stream_handler_call(io, probe, fsi, io, dev);
- start = (u16 *)fsi_dma_get_area(fsi, SNDRV_PCM_STREAM_PLAYBACK);
+ if (ret1 < 0)
+ return ret1;
+ if (ret2 < 0)
+ return ret2;
- for (i = 0; i < num; i++)
- fsi_reg_write(fsi, DODT, ((u32)*(start + i) << 8));
+ return 0;
}
-static void fsi_dma_soft_pop16(struct fsi_priv *fsi, int num)
+static int fsi_stream_remove(struct fsi_priv *fsi)
{
- u16 *start;
- int i;
+ struct fsi_stream *io;
+ int ret1, ret2;
+
+ io = &fsi->playback;
+ ret1 = fsi_stream_handler_call(io, remove, fsi, io);
- start = (u16 *)fsi_dma_get_area(fsi, SNDRV_PCM_STREAM_CAPTURE);
+ io = &fsi->capture;
+ ret2 = fsi_stream_handler_call(io, remove, fsi, io);
+ if (ret1 < 0)
+ return ret1;
+ if (ret2 < 0)
+ return ret2;
- for (i = 0; i < num; i++)
- *(start + i) = (u16)(fsi_reg_read(fsi, DIDT) >> 8);
+ return 0;
}
-static void fsi_dma_soft_push32(struct fsi_priv *fsi, int num)
+/*
+ * format/bus/dma setting
+ */
+static void fsi_format_bus_setup(struct fsi_priv *fsi, struct fsi_stream *io,
+ u32 bus, struct device *dev)
{
- u32 *start;
- int i;
-
- start = (u32 *)fsi_dma_get_area(fsi, SNDRV_PCM_STREAM_PLAYBACK);
-
+ struct fsi_master *master = fsi_get_master(fsi);
+ int is_play = fsi_stream_is_play(fsi, io);
+ u32 fmt = fsi->fmt;
- for (i = 0; i < num; i++)
- fsi_reg_write(fsi, DODT, *(start + i));
-}
+ if (fsi_version(master) >= 2) {
+ u32 dma = 0;
-static void fsi_dma_soft_pop32(struct fsi_priv *fsi, int num)
-{
- u32 *start;
- int i;
+ /*
+ * FSI2 needs DMA/Bus setting
+ */
+ switch (bus) {
+ case PACKAGE_24BITBUS_FRONT:
+ fmt |= CR_BWS_24;
+ dma |= VDMD_FRONT;
+ dev_dbg(dev, "24bit bus / package in front\n");
+ break;
+ case PACKAGE_16BITBUS_STREAM:
+ fmt |= CR_BWS_16;
+ dma |= VDMD_STREAM;
+ dev_dbg(dev, "16bit bus / stream mode\n");
+ break;
+ case PACKAGE_24BITBUS_BACK:
+ default:
+ fmt |= CR_BWS_24;
+ dma |= VDMD_BACK;
+ dev_dbg(dev, "24bit bus / package in back\n");
+ break;
+ }
- start = (u32 *)fsi_dma_get_area(fsi, SNDRV_PCM_STREAM_CAPTURE);
+ if (is_play)
+ fsi_reg_write(fsi, OUT_DMAC, dma);
+ else
+ fsi_reg_write(fsi, IN_DMAC, dma);
+ }
- for (i = 0; i < num; i++)
- *(start + i) = fsi_reg_read(fsi, DIDT);
+ if (is_play)
+ fsi_reg_write(fsi, DO_FMT, fmt);
+ else
+ fsi_reg_write(fsi, DI_FMT, fmt);
}
/*
* irq function
*/
-static void fsi_irq_enable(struct fsi_priv *fsi, int is_play)
+static void fsi_irq_enable(struct fsi_priv *fsi, struct fsi_stream *io)
{
- u32 data = AB_IO(1, fsi_get_port_shift(fsi, is_play));
+ u32 data = AB_IO(1, fsi_get_port_shift(fsi, io));
struct fsi_master *master = fsi_get_master(fsi);
fsi_core_mask_set(master, imsk, data, data);
fsi_core_mask_set(master, iemsk, data, data);
}
-static void fsi_irq_disable(struct fsi_priv *fsi, int is_play)
+static void fsi_irq_disable(struct fsi_priv *fsi, struct fsi_stream *io)
{
- u32 data = AB_IO(1, fsi_get_port_shift(fsi, is_play));
+ u32 data = AB_IO(1, fsi_get_port_shift(fsi, io));
struct fsi_master *master = fsi_get_master(fsi);
fsi_core_mask_set(master, imsk, data, 0);
@@ -563,8 +704,8 @@ static void fsi_irq_clear_status(struct fsi_priv *fsi)
u32 data = 0;
struct fsi_master *master = fsi_get_master(fsi);
- data |= AB_IO(1, fsi_get_port_shift(fsi, 0));
- data |= AB_IO(1, fsi_get_port_shift(fsi, 1));
+ data |= AB_IO(1, fsi_get_port_shift(fsi, &fsi->playback));
+ data |= AB_IO(1, fsi_get_port_shift(fsi, &fsi->capture));
/* clear interrupt factor */
fsi_core_mask_set(master, int_st, data, 0);
@@ -580,11 +721,6 @@ static void fsi_spdif_clk_ctrl(struct fsi_priv *fsi, int enable)
struct fsi_master *master = fsi_get_master(fsi);
u32 mask, val;
- if (master->core->ver < 2) {
- pr_err("fsi: register access err (%s)\n", __func__);
- return;
- }
-
mask = BP | SE;
val = enable ? mask : 0;
@@ -596,171 +732,388 @@ static void fsi_spdif_clk_ctrl(struct fsi_priv *fsi, int enable)
/*
* clock function
*/
-static int fsi_set_master_clk(struct device *dev, struct fsi_priv *fsi,
- long rate, int enable)
-{
- struct fsi_master *master = fsi_get_master(fsi);
- set_rate_func set_rate = fsi_get_info_set_rate(master);
- int fsi_ver = master->core->ver;
- int ret;
+static int fsi_clk_init(struct device *dev,
+ struct fsi_priv *fsi,
+ int xck,
+ int ick,
+ int div,
+ int (*set_rate)(struct device *dev,
+ struct fsi_priv *fsi))
+{
+ struct fsi_clk *clock = &fsi->clock;
+ int is_porta = fsi_is_port_a(fsi);
- ret = set_rate(dev, fsi_is_port_a(fsi), rate, enable);
- if (ret < 0) /* error */
- return ret;
+ clock->xck = NULL;
+ clock->ick = NULL;
+ clock->div = NULL;
+ clock->rate = 0;
+ clock->count = 0;
+ clock->set_rate = set_rate;
+
+ clock->own = devm_clk_get(dev, NULL);
+ if (IS_ERR(clock->own))
+ return -EINVAL;
- if (!enable)
- return 0;
+ /* external clock */
+ if (xck) {
+ clock->xck = devm_clk_get(dev, is_porta ? "xcka" : "xckb");
+ if (IS_ERR(clock->xck)) {
+ dev_err(dev, "can't get xck clock\n");
+ return -EINVAL;
+ }
+ if (clock->xck == clock->own) {
+ dev_err(dev, "cpu doesn't support xck clock\n");
+ return -EINVAL;
+ }
+ }
- if (ret > 0) {
- u32 data = 0;
+ /* FSIACLK/FSIBCLK */
+ if (ick) {
+ clock->ick = devm_clk_get(dev, is_porta ? "icka" : "ickb");
+ if (IS_ERR(clock->ick)) {
+ dev_err(dev, "can't get ick clock\n");
+ return -EINVAL;
+ }
+ if (clock->ick == clock->own) {
+ dev_err(dev, "cpu doesn't support ick clock\n");
+ return -EINVAL;
+ }
+ }
- switch (ret & SH_FSI_ACKMD_MASK) {
- default:
- /* FALL THROUGH */
- case SH_FSI_ACKMD_512:
- data |= (0x0 << 12);
- break;
- case SH_FSI_ACKMD_256:
- data |= (0x1 << 12);
- break;
- case SH_FSI_ACKMD_128:
- data |= (0x2 << 12);
- break;
- case SH_FSI_ACKMD_64:
- data |= (0x3 << 12);
- break;
- case SH_FSI_ACKMD_32:
- if (fsi_ver < 2)
- dev_err(dev, "unsupported ACKMD\n");
- else
- data |= (0x4 << 12);
- break;
+ /* FSI-DIV */
+ if (div) {
+ clock->div = devm_clk_get(dev, is_porta ? "diva" : "divb");
+ if (IS_ERR(clock->div)) {
+ dev_err(dev, "can't get div clock\n");
+ return -EINVAL;
}
+ if (clock->div == clock->own) {
+ dev_err(dev, "cpu doens't support div clock\n");
+ return -EINVAL;
+ }
+ }
- switch (ret & SH_FSI_BPFMD_MASK) {
- default:
- /* FALL THROUGH */
- case SH_FSI_BPFMD_32:
- data |= (0x0 << 8);
- break;
- case SH_FSI_BPFMD_64:
- data |= (0x1 << 8);
- break;
- case SH_FSI_BPFMD_128:
- data |= (0x2 << 8);
- break;
- case SH_FSI_BPFMD_256:
- data |= (0x3 << 8);
- break;
- case SH_FSI_BPFMD_512:
- data |= (0x4 << 8);
- break;
- case SH_FSI_BPFMD_16:
- if (fsi_ver < 2)
- dev_err(dev, "unsupported ACKMD\n");
- else
- data |= (0x7 << 8);
- break;
+ return 0;
+}
+
+#define fsi_clk_invalid(fsi) fsi_clk_valid(fsi, 0)
+static void fsi_clk_valid(struct fsi_priv *fsi, unsigned long rate)
+{
+ fsi->clock.rate = rate;
+}
+
+static int fsi_clk_is_valid(struct fsi_priv *fsi)
+{
+ return fsi->clock.set_rate &&
+ fsi->clock.rate;
+}
+
+static int fsi_clk_enable(struct device *dev,
+ struct fsi_priv *fsi)
+{
+ struct fsi_clk *clock = &fsi->clock;
+ int ret = -EINVAL;
+
+ if (!fsi_clk_is_valid(fsi))
+ return ret;
+
+ if (0 == clock->count) {
+ ret = clock->set_rate(dev, fsi);
+ if (ret < 0) {
+ fsi_clk_invalid(fsi);
+ return ret;
}
- fsi_reg_mask_set(fsi, CKG1, (ACKMD_MASK | BPFMD_MASK) , data);
- udelay(10);
- ret = 0;
+ if (clock->xck)
+ clk_enable(clock->xck);
+ if (clock->ick)
+ clk_enable(clock->ick);
+ if (clock->div)
+ clk_enable(clock->div);
+
+ clock->count++;
}
return ret;
}
-#define fsi_port_start(f, i) __fsi_port_clk_ctrl(f, i, 1)
-#define fsi_port_stop(f, i) __fsi_port_clk_ctrl(f, i, 0)
-static void __fsi_port_clk_ctrl(struct fsi_priv *fsi, int is_play, int enable)
+static int fsi_clk_disable(struct device *dev,
+ struct fsi_priv *fsi)
{
- struct fsi_master *master = fsi_get_master(fsi);
- u32 clk = fsi_is_port_a(fsi) ? CRA : CRB;
+ struct fsi_clk *clock = &fsi->clock;
- if (enable)
- fsi_irq_enable(fsi, is_play);
- else
- fsi_irq_disable(fsi, is_play);
+ if (!fsi_clk_is_valid(fsi))
+ return -EINVAL;
- if (fsi_is_clk_master(fsi))
- fsi_master_mask_set(master, CLK_RST, clk, (enable) ? clk : 0);
+ if (1 == clock->count--) {
+ if (clock->xck)
+ clk_disable(clock->xck);
+ if (clock->ick)
+ clk_disable(clock->ick);
+ if (clock->div)
+ clk_disable(clock->div);
+ }
+
+ return 0;
}
-/*
- * ctrl function
- */
-static void fsi_fifo_init(struct fsi_priv *fsi,
- int is_play,
- struct device *dev)
+static int fsi_clk_set_ackbpf(struct device *dev,
+ struct fsi_priv *fsi,
+ int ackmd, int bpfmd)
{
- struct fsi_master *master = fsi_get_master(fsi);
- struct fsi_stream *io = fsi_get_stream(fsi, is_play);
- u32 shift, i;
- int frame_capa;
+ u32 data = 0;
- /* get on-chip RAM capacity */
- shift = fsi_master_read(master, FIFO_SZ);
- shift >>= fsi_get_port_shift(fsi, is_play);
- shift &= FIFO_SZ_MASK;
- frame_capa = 256 << shift;
- dev_dbg(dev, "fifo = %d words\n", frame_capa);
+ /* check ackmd/bpfmd relationship */
+ if (bpfmd > ackmd) {
+ dev_err(dev, "unsupported rate (%d/%d)\n", ackmd, bpfmd);
+ return -EINVAL;
+ }
+
+ /* ACKMD */
+ switch (ackmd) {
+ case 512:
+ data |= (0x0 << 12);
+ break;
+ case 256:
+ data |= (0x1 << 12);
+ break;
+ case 128:
+ data |= (0x2 << 12);
+ break;
+ case 64:
+ data |= (0x3 << 12);
+ break;
+ case 32:
+ data |= (0x4 << 12);
+ break;
+ default:
+ dev_err(dev, "unsupported ackmd (%d)\n", ackmd);
+ return -EINVAL;
+ }
+
+ /* BPFMD */
+ switch (bpfmd) {
+ case 32:
+ data |= (0x0 << 8);
+ break;
+ case 64:
+ data |= (0x1 << 8);
+ break;
+ case 128:
+ data |= (0x2 << 8);
+ break;
+ case 256:
+ data |= (0x3 << 8);
+ break;
+ case 512:
+ data |= (0x4 << 8);
+ break;
+ case 16:
+ data |= (0x7 << 8);
+ break;
+ default:
+ dev_err(dev, "unsupported bpfmd (%d)\n", bpfmd);
+ return -EINVAL;
+ }
+
+ dev_dbg(dev, "ACKMD/BPFMD = %d/%d\n", ackmd, bpfmd);
+
+ fsi_reg_mask_set(fsi, CKG1, (ACKMD_MASK | BPFMD_MASK) , data);
+ udelay(10);
+
+ return 0;
+}
+
+static int fsi_clk_set_rate_external(struct device *dev,
+ struct fsi_priv *fsi)
+{
+ struct clk *xck = fsi->clock.xck;
+ struct clk *ick = fsi->clock.ick;
+ unsigned long rate = fsi->clock.rate;
+ unsigned long xrate;
+ int ackmd, bpfmd;
+ int ret = 0;
+
+ /* check clock rate */
+ xrate = clk_get_rate(xck);
+ if (xrate % rate) {
+ dev_err(dev, "unsupported clock rate\n");
+ return -EINVAL;
+ }
+
+ clk_set_parent(ick, xck);
+ clk_set_rate(ick, xrate);
+
+ bpfmd = fsi->chan_num * 32;
+ ackmd = xrate / rate;
+
+ dev_dbg(dev, "external/rate = %ld/%ld\n", xrate, rate);
+
+ ret = fsi_clk_set_ackbpf(dev, fsi, ackmd, bpfmd);
+ if (ret < 0)
+ dev_err(dev, "%s failed", __func__);
+
+ return ret;
+}
+
+static int fsi_clk_set_rate_cpg(struct device *dev,
+ struct fsi_priv *fsi)
+{
+ struct clk *ick = fsi->clock.ick;
+ struct clk *div = fsi->clock.div;
+ unsigned long rate = fsi->clock.rate;
+ unsigned long target = 0; /* 12288000 or 11289600 */
+ unsigned long actual, cout;
+ unsigned long diff, min;
+ unsigned long best_cout, best_act;
+ int adj;
+ int ackmd, bpfmd;
+ int ret = -EINVAL;
+
+ if (!(12288000 % rate))
+ target = 12288000;
+ if (!(11289600 % rate))
+ target = 11289600;
+ if (!target) {
+ dev_err(dev, "unsupported rate\n");
+ return ret;
+ }
+
+ bpfmd = fsi->chan_num * 32;
+ ackmd = target / rate;
+ ret = fsi_clk_set_ackbpf(dev, fsi, ackmd, bpfmd);
+ if (ret < 0) {
+ dev_err(dev, "%s failed", __func__);
+ return ret;
+ }
/*
- * The maximum number of sample data varies depending
- * on the number of channels selected for the format.
+ * The clock flow is
*
- * FIFOs are used in 4-channel units in 3-channel mode
- * and in 8-channel units in 5- to 7-channel mode
- * meaning that more FIFOs than the required size of DPRAM
- * are used.
+ * [CPG] = cout => [FSI_DIV] = audio => [FSI] => [codec]
*
- * ex) if 256 words of DP-RAM is connected
- * 1 channel: 256 (256 x 1 = 256)
- * 2 channels: 128 (128 x 2 = 256)
- * 3 channels: 64 ( 64 x 3 = 192)
- * 4 channels: 64 ( 64 x 4 = 256)
- * 5 channels: 32 ( 32 x 5 = 160)
- * 6 channels: 32 ( 32 x 6 = 192)
- * 7 channels: 32 ( 32 x 7 = 224)
- * 8 channels: 32 ( 32 x 8 = 256)
+ * But, it needs to find best match of CPG and FSI_DIV
+ * combination, since it is difficult to generate correct
+ * frequency of audio clock from ick clock only.
+ * Because ick is created from its parent clock.
+ *
+ * target = rate x [512/256/128/64]fs
+ * cout = round(target x adjustment)
+ * actual = cout / adjustment (by FSI-DIV) ~= target
+ * audio = actual
*/
- for (i = 1; i < fsi->chan_num; i <<= 1)
- frame_capa >>= 1;
- dev_dbg(dev, "%d channel %d store\n",
- fsi->chan_num, frame_capa);
+ min = ~0;
+ best_cout = 0;
+ best_act = 0;
+ for (adj = 1; adj < 0xffff; adj++) {
- io->fifo_sample_capa = fsi_frame2sample(fsi, frame_capa);
+ cout = target * adj;
+ if (cout > 100000000) /* max clock = 100MHz */
+ break;
- /*
- * set interrupt generation factor
- * clear FIFO
- */
- if (is_play) {
- fsi_reg_write(fsi, DOFF_CTL, IRQ_HALF);
- fsi_reg_mask_set(fsi, DOFF_CTL, FIFO_CLR, FIFO_CLR);
+ /* cout/actual audio clock */
+ cout = clk_round_rate(ick, cout);
+ actual = cout / adj;
+
+ /* find best frequency */
+ diff = abs(actual - target);
+ if (diff < min) {
+ min = diff;
+ best_cout = cout;
+ best_act = actual;
+ }
+ }
+
+ ret = clk_set_rate(ick, best_cout);
+ if (ret < 0) {
+ dev_err(dev, "ick clock failed\n");
+ return -EIO;
+ }
+
+ ret = clk_set_rate(div, clk_round_rate(div, best_act));
+ if (ret < 0) {
+ dev_err(dev, "div clock failed\n");
+ return -EIO;
+ }
+
+ dev_dbg(dev, "ick/div = %ld/%ld\n",
+ clk_get_rate(ick), clk_get_rate(div));
+
+ return ret;
+}
+
+/*
+ * pio data transfer handler
+ */
+static void fsi_pio_push16(struct fsi_priv *fsi, u8 *_buf, int samples)
+{
+ int i;
+
+ if (fsi_is_enable_stream(fsi)) {
+ /*
+ * stream mode
+ * see
+ * fsi_pio_push_init()
+ */
+ u32 *buf = (u32 *)_buf;
+
+ for (i = 0; i < samples / 2; i++)
+ fsi_reg_write(fsi, DODT, buf[i]);
} else {
- fsi_reg_write(fsi, DIFF_CTL, IRQ_HALF);
- fsi_reg_mask_set(fsi, DIFF_CTL, FIFO_CLR, FIFO_CLR);
+ /* normal mode */
+ u16 *buf = (u16 *)_buf;
+
+ for (i = 0; i < samples; i++)
+ fsi_reg_write(fsi, DODT, ((u32)*(buf + i) << 8));
}
}
-static int fsi_fifo_data_ctrl(struct fsi_priv *fsi, int stream)
+static void fsi_pio_pop16(struct fsi_priv *fsi, u8 *_buf, int samples)
+{
+ u16 *buf = (u16 *)_buf;
+ int i;
+
+ for (i = 0; i < samples; i++)
+ *(buf + i) = (u16)(fsi_reg_read(fsi, DIDT) >> 8);
+}
+
+static void fsi_pio_push32(struct fsi_priv *fsi, u8 *_buf, int samples)
+{
+ u32 *buf = (u32 *)_buf;
+ int i;
+
+ for (i = 0; i < samples; i++)
+ fsi_reg_write(fsi, DODT, *(buf + i));
+}
+
+static void fsi_pio_pop32(struct fsi_priv *fsi, u8 *_buf, int samples)
+{
+ u32 *buf = (u32 *)_buf;
+ int i;
+
+ for (i = 0; i < samples; i++)
+ *(buf + i) = fsi_reg_read(fsi, DIDT);
+}
+
+static u8 *fsi_pio_get_area(struct fsi_priv *fsi, struct fsi_stream *io)
+{
+ struct snd_pcm_runtime *runtime = io->substream->runtime;
+
+ return runtime->dma_area +
+ samples_to_bytes(runtime, io->buff_sample_pos);
+}
+
+static int fsi_pio_transfer(struct fsi_priv *fsi, struct fsi_stream *io,
+ void (*run16)(struct fsi_priv *fsi, u8 *buf, int samples),
+ void (*run32)(struct fsi_priv *fsi, u8 *buf, int samples),
+ int samples)
{
struct snd_pcm_runtime *runtime;
- struct snd_pcm_substream *substream = NULL;
- int is_play = fsi_stream_is_play(stream);
- struct fsi_stream *io = fsi_get_stream(fsi, is_play);
- int sample_residues;
- int sample_width;
- int samples;
- int samples_max;
+ struct snd_pcm_substream *substream;
+ u8 *buf;
int over_period;
- void (*fn)(struct fsi_priv *fsi, int size);
- if (!fsi ||
- !io->substream ||
- !io->substream->runtime)
+ if (!fsi_stream_is_working(fsi, io))
return -EINVAL;
over_period = 0;
@@ -780,79 +1133,120 @@ static int fsi_fifo_data_ctrl(struct fsi_priv *fsi, int stream)
io->buff_sample_pos = 0;
}
- /* get 1 sample data width */
- sample_width = samples_to_bytes(runtime, 1);
+ buf = fsi_pio_get_area(fsi, io);
- /* get number of residue samples */
- sample_residues = io->buff_sample_capa - io->buff_sample_pos;
+ switch (io->sample_width) {
+ case 2:
+ run16(fsi, buf, samples);
+ break;
+ case 4:
+ run32(fsi, buf, samples);
+ break;
+ default:
+ return -EINVAL;
+ }
- if (is_play) {
- /*
- * for play-back
- *
- * samples_max : number of FSI fifo free samples space
- * samples : number of ALSA residue samples
- */
- samples_max = io->fifo_sample_capa;
- samples_max -= fsi_get_current_fifo_samples(fsi, is_play);
+ /* update buff_sample_pos */
+ io->buff_sample_pos += samples;
- samples = sample_residues;
+ if (over_period)
+ snd_pcm_period_elapsed(substream);
- switch (sample_width) {
- case 2:
- fn = fsi_dma_soft_push16;
- break;
- case 4:
- fn = fsi_dma_soft_push32;
- break;
- default:
- return -EINVAL;
- }
- } else {
- /*
- * for capture
- *
- * samples_max : number of ALSA free samples space
- * samples : number of samples in FSI fifo
- */
- samples_max = sample_residues;
- samples = fsi_get_current_fifo_samples(fsi, is_play);
+ return 0;
+}
- switch (sample_width) {
- case 2:
- fn = fsi_dma_soft_pop16;
- break;
- case 4:
- fn = fsi_dma_soft_pop32;
- break;
- default:
- return -EINVAL;
- }
- }
+static int fsi_pio_pop(struct fsi_priv *fsi, struct fsi_stream *io)
+{
+ int sample_residues; /* samples in FSI fifo */
+ int sample_space; /* ALSA free samples space */
+ int samples;
- samples = min(samples, samples_max);
+ sample_residues = fsi_get_current_fifo_samples(fsi, io);
+ sample_space = io->buff_sample_capa - io->buff_sample_pos;
- fn(fsi, samples);
+ samples = min(sample_residues, sample_space);
- /* update buff_sample_pos */
- io->buff_sample_pos += samples;
+ return fsi_pio_transfer(fsi, io,
+ fsi_pio_pop16,
+ fsi_pio_pop32,
+ samples);
+}
- if (over_period)
- snd_pcm_period_elapsed(substream);
+static int fsi_pio_push(struct fsi_priv *fsi, struct fsi_stream *io)
+{
+ int sample_residues; /* ALSA residue samples */
+ int sample_space; /* FSI fifo free samples space */
+ int samples;
+
+ sample_residues = io->buff_sample_capa - io->buff_sample_pos;
+ sample_space = io->fifo_sample_capa -
+ fsi_get_current_fifo_samples(fsi, io);
+
+ samples = min(sample_residues, sample_space);
+
+ return fsi_pio_transfer(fsi, io,
+ fsi_pio_push16,
+ fsi_pio_push32,
+ samples);
+}
+
+static int fsi_pio_start_stop(struct fsi_priv *fsi, struct fsi_stream *io,
+ int enable)
+{
+ struct fsi_master *master = fsi_get_master(fsi);
+ u32 clk = fsi_is_port_a(fsi) ? CRA : CRB;
+
+ if (enable)
+ fsi_irq_enable(fsi, io);
+ else
+ fsi_irq_disable(fsi, io);
+
+ if (fsi_is_clk_master(fsi))
+ fsi_master_mask_set(master, CLK_RST, clk, (enable) ? clk : 0);
return 0;
}
-static int fsi_data_pop(struct fsi_priv *fsi)
+static int fsi_pio_push_init(struct fsi_priv *fsi, struct fsi_stream *io)
{
- return fsi_fifo_data_ctrl(fsi, SNDRV_PCM_STREAM_CAPTURE);
+ /*
+ * we can use 16bit stream mode
+ * when "playback" and "16bit data"
+ * and platform allows "stream mode"
+ * see
+ * fsi_pio_push16()
+ */
+ if (fsi_is_enable_stream(fsi))
+ io->bus_option = BUSOP_SET(24, PACKAGE_24BITBUS_BACK) |
+ BUSOP_SET(16, PACKAGE_16BITBUS_STREAM);
+ else
+ io->bus_option = BUSOP_SET(24, PACKAGE_24BITBUS_BACK) |
+ BUSOP_SET(16, PACKAGE_24BITBUS_BACK);
+ return 0;
}
-static int fsi_data_push(struct fsi_priv *fsi)
+static int fsi_pio_pop_init(struct fsi_priv *fsi, struct fsi_stream *io)
{
- return fsi_fifo_data_ctrl(fsi, SNDRV_PCM_STREAM_PLAYBACK);
+ /*
+ * always 24bit bus, package back when "capture"
+ */
+ io->bus_option = BUSOP_SET(24, PACKAGE_24BITBUS_BACK) |
+ BUSOP_SET(16, PACKAGE_24BITBUS_BACK);
+ return 0;
}
+static struct fsi_stream_handler fsi_pio_push_handler = {
+ .init = fsi_pio_push_init,
+ .transfer = fsi_pio_push,
+ .start_stop = fsi_pio_start_stop,
+};
+
+static struct fsi_stream_handler fsi_pio_pop_handler = {
+ .init = fsi_pio_pop_init,
+ .transfer = fsi_pio_pop,
+ .start_stop = fsi_pio_start_stop,
+};
+
static irqreturn_t fsi_interrupt(int irq, void *data)
{
struct fsi_master *master = data;
@@ -863,13 +1257,13 @@ static irqreturn_t fsi_interrupt(int irq, void *data)
fsi_master_mask_set(master, SOFT_RST, IR, IR);
if (int_st & AB_IO(1, AO_SHIFT))
- fsi_data_push(&master->fsia);
+ fsi_stream_transfer(&master->fsia.playback);
if (int_st & AB_IO(1, BO_SHIFT))
- fsi_data_push(&master->fsib);
+ fsi_stream_transfer(&master->fsib.playback);
if (int_st & AB_IO(1, AI_SHIFT))
- fsi_data_pop(&master->fsia);
+ fsi_stream_transfer(&master->fsia.capture);
if (int_st & AB_IO(1, BI_SHIFT))
- fsi_data_pop(&master->fsib);
+ fsi_stream_transfer(&master->fsib.capture);
fsi_count_fifo_err(&master->fsia);
fsi_count_fifo_err(&master->fsib);
@@ -881,16 +1275,295 @@ static irqreturn_t fsi_interrupt(int irq, void *data)
}
/*
+ * dma data transfer handler
+ */
+static int fsi_dma_init(struct fsi_priv *fsi, struct fsi_stream *io)
+{
+ struct snd_pcm_runtime *runtime = io->substream->runtime;
+ struct snd_soc_dai *dai = fsi_get_dai(io->substream);
+ enum dma_data_direction dir = fsi_stream_is_play(fsi, io) ?
+ DMA_TO_DEVICE : DMA_FROM_DEVICE;
+
+ /*
+ * 24bit data : 24bit bus / package in back
+ * 16bit data : 16bit bus / stream mode
+ */
+ io->bus_option = BUSOP_SET(24, PACKAGE_24BITBUS_BACK) |
+ BUSOP_SET(16, PACKAGE_16BITBUS_STREAM);
+
+ io->loop_cnt = 2; /* push 1st, 2nd period first, then 3rd, 4th... */
+ io->additional_pos = 0;
+ io->dma = dma_map_single(dai->dev, runtime->dma_area,
+ snd_pcm_lib_buffer_bytes(io->substream), dir);
+ return 0;
+}
+
+static int fsi_dma_quit(struct fsi_priv *fsi, struct fsi_stream *io)
+{
+ struct snd_soc_dai *dai = fsi_get_dai(io->substream);
+ enum dma_data_direction dir = fsi_stream_is_play(fsi, io) ?
+ DMA_TO_DEVICE : DMA_FROM_DEVICE;
+
+ dma_unmap_single(dai->dev, io->dma,
+ snd_pcm_lib_buffer_bytes(io->substream), dir);
+ return 0;
+}
+
+static dma_addr_t fsi_dma_get_area(struct fsi_stream *io, int additional)
+{
+ struct snd_pcm_runtime *runtime = io->substream->runtime;
+ int period = io->period_pos + additional;
+
+ if (period >= runtime->periods)
+ period = 0;
+
+ return io->dma + samples_to_bytes(runtime, period * io->period_samples);
+}
+
+static void fsi_dma_complete(void *data)
+{
+ struct fsi_stream *io = (struct fsi_stream *)data;
+ struct fsi_priv *fsi = fsi_stream_to_priv(io);
+ struct snd_pcm_runtime *runtime = io->substream->runtime;
+ struct snd_soc_dai *dai = fsi_get_dai(io->substream);
+ enum dma_data_direction dir = fsi_stream_is_play(fsi, io) ?
+ DMA_TO_DEVICE : DMA_FROM_DEVICE;
+
+ dma_sync_single_for_cpu(dai->dev, fsi_dma_get_area(io, 0),
+ samples_to_bytes(runtime, io->period_samples), dir);
+
+ io->buff_sample_pos += io->period_samples;
+ io->period_pos++;
+
+ if (io->period_pos >= runtime->periods) {
+ io->period_pos = 0;
+ io->buff_sample_pos = 0;
+ }
+
+ fsi_count_fifo_err(fsi);
+ fsi_stream_transfer(io);
+
+ snd_pcm_period_elapsed(io->substream);
+}
+
+static void fsi_dma_do_work(struct work_struct *work)
+{
+ struct fsi_stream *io = container_of(work, struct fsi_stream, work);
+ struct fsi_priv *fsi = fsi_stream_to_priv(io);
+ struct snd_soc_dai *dai;
+ struct dma_async_tx_descriptor *desc;
+ struct snd_pcm_runtime *runtime;
+ enum dma_data_direction dir;
+ int is_play = fsi_stream_is_play(fsi, io);
+ int len, i;
+ dma_addr_t buf;
+
+ if (!fsi_stream_is_working(fsi, io))
+ return;
+
+ dai = fsi_get_dai(io->substream);
+ runtime = io->substream->runtime;
+ dir = is_play ? DMA_TO_DEVICE : DMA_FROM_DEVICE;
+ len = samples_to_bytes(runtime, io->period_samples);
+
+ for (i = 0; i < io->loop_cnt; i++) {
+ buf = fsi_dma_get_area(io, io->additional_pos);
+
+ dma_sync_single_for_device(dai->dev, buf, len, dir);
+
+ desc = dmaengine_prep_slave_single(io->chan, buf, len, dir,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (!desc) {
+ dev_err(dai->dev, "dmaengine_prep_slave_sg() fail\n");
+ return;
+ }
+
+ desc->callback = fsi_dma_complete;
+ desc->callback_param = io;
+
+ if (dmaengine_submit(desc) < 0) {
+ dev_err(dai->dev, "tx_submit() fail\n");
+ return;
+ }
+
+ dma_async_issue_pending(io->chan);
+
+ io->additional_pos = 1;
+ }
+
+ io->loop_cnt = 1;
+
+ /*
+ * FIXME
+ *
+ * In DMAEngine case, codec and FSI cannot be started simultaneously
+ * since FSI is using the scheduler work queue.
+ * Therefore, in capture case, probably FSI FIFO will have got
+ * overflow error in this point.
+ * in that case, DMA cannot start transfer until error was cleared.
+ */
+ if (!is_play) {
+ if (ERR_OVER & fsi_reg_read(fsi, DIFF_ST)) {
+ fsi_reg_mask_set(fsi, DIFF_CTL, FIFO_CLR, FIFO_CLR);
+ fsi_reg_write(fsi, DIFF_ST, 0);
+ }
+ }
+}
+
+static int fsi_dma_transfer(struct fsi_priv *fsi, struct fsi_stream *io)
+{
+ schedule_work(&io->work);
+
+ return 0;
+}
+
+static int fsi_dma_push_start_stop(struct fsi_priv *fsi, struct fsi_stream *io,
+ int start)
+{
+ struct fsi_master *master = fsi_get_master(fsi);
+ u32 clk = fsi_is_port_a(fsi) ? CRA : CRB;
+ u32 enable = start ? DMA_ON : 0;
+
+ fsi_reg_mask_set(fsi, OUT_DMAC, DMA_ON, enable);
+
+ dmaengine_terminate_all(io->chan);
+
+ if (fsi_is_clk_master(fsi))
+ fsi_master_mask_set(master, CLK_RST, clk, (enable) ? clk : 0);
+
+ return 0;
+}
+
+static int fsi_dma_probe(struct fsi_priv *fsi, struct fsi_stream *io, struct device *dev)
+{
+ dma_cap_mask_t mask;
+ int is_play = fsi_stream_is_play(fsi, io);
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+
+ io->chan = dma_request_slave_channel_compat(mask,
+ shdma_chan_filter, (void *)io->dma_id,
+ dev, is_play ? "tx" : "rx");
+ if (io->chan) {
+ struct dma_slave_config cfg;
+ int ret;
+
+ cfg.slave_id = io->dma_id;
+ cfg.dst_addr = 0; /* use default addr */
+ cfg.src_addr = 0; /* use default addr */
+ cfg.direction = is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM;
+
+ ret = dmaengine_slave_config(io->chan, &cfg);
+ if (ret < 0) {
+ dma_release_channel(io->chan);
+ io->chan = NULL;
+ }
+ }
+
+ if (!io->chan) {
+
+ /* switch to PIO handler */
+ if (is_play)
+ fsi->playback.handler = &fsi_pio_push_handler;
+ else
+ fsi->capture.handler = &fsi_pio_pop_handler;
+
+ dev_info(dev, "switch handler (dma => pio)\n");
+
+ /* probe again */
+ return fsi_stream_probe(fsi, dev);
+ }
+
+ INIT_WORK(&io->work, fsi_dma_do_work);
+
+ return 0;
+}
+
+static int fsi_dma_remove(struct fsi_priv *fsi, struct fsi_stream *io)
+{
+ cancel_work_sync(&io->work);
+
+ fsi_stream_stop(fsi, io);
+
+ if (io->chan)
+ dma_release_channel(io->chan);
+
+ io->chan = NULL;
+ return 0;
+}
+
+static struct fsi_stream_handler fsi_dma_push_handler = {
+ .init = fsi_dma_init,
+ .quit = fsi_dma_quit,
+ .probe = fsi_dma_probe,
+ .transfer = fsi_dma_transfer,
+ .remove = fsi_dma_remove,
+ .start_stop = fsi_dma_push_start_stop,
+};
+
+/*
* dai ops
*/
+static void fsi_fifo_init(struct fsi_priv *fsi,
+ struct fsi_stream *io,
+ struct device *dev)
+{
+ struct fsi_master *master = fsi_get_master(fsi);
+ int is_play = fsi_stream_is_play(fsi, io);
+ u32 shift, i;
+ int frame_capa;
+
+ /* get on-chip RAM capacity */
+ shift = fsi_master_read(master, FIFO_SZ);
+ shift >>= fsi_get_port_shift(fsi, io);
+ shift &= FIFO_SZ_MASK;
+ frame_capa = 256 << shift;
+ dev_dbg(dev, "fifo = %d words\n", frame_capa);
+
+ /*
+ * The maximum number of sample data varies depending
+ * on the number of channels selected for the format.
+ *
+ * FIFOs are used in 4-channel units in 3-channel mode
+ * and in 8-channel units in 5- to 7-channel mode
+ * meaning that more FIFOs than the required size of DPRAM
+ * are used.
+ *
+ * ex) if 256 words of DP-RAM is connected
+ * 1 channel: 256 (256 x 1 = 256)
+ * 2 channels: 128 (128 x 2 = 256)
+ * 3 channels: 64 ( 64 x 3 = 192)
+ * 4 channels: 64 ( 64 x 4 = 256)
+ * 5 channels: 32 ( 32 x 5 = 160)
+ * 6 channels: 32 ( 32 x 6 = 192)
+ * 7 channels: 32 ( 32 x 7 = 224)
+ * 8 channels: 32 ( 32 x 8 = 256)
+ */
+ for (i = 1; i < fsi->chan_num; i <<= 1)
+ frame_capa >>= 1;
+ dev_dbg(dev, "%d channel %d store\n",
+ fsi->chan_num, frame_capa);
+
+ io->fifo_sample_capa = fsi_frame2sample(fsi, frame_capa);
+
+ /*
+ * set interrupt generation factor
+ * clear FIFO
+ */
+ if (is_play) {
+ fsi_reg_write(fsi, DOFF_CTL, IRQ_HALF);
+ fsi_reg_mask_set(fsi, DOFF_CTL, FIFO_CLR, FIFO_CLR);
+ } else {
+ fsi_reg_write(fsi, DIFF_CTL, IRQ_HALF);
+ fsi_reg_mask_set(fsi, DIFF_CTL, FIFO_CLR, FIFO_CLR);
+ }
+}
static int fsi_hw_startup(struct fsi_priv *fsi,
- int is_play,
+ struct fsi_stream *io,
struct device *dev)
{
- struct fsi_master *master = fsi_get_master(fsi);
- int fsi_ver = master->core->ver;
- u32 flags = fsi_get_info_flags(fsi);
u32 data = 0;
/* clock setting */
@@ -901,21 +1574,14 @@ static int fsi_hw_startup(struct fsi_priv *fsi,
/* clock inversion (CKG2) */
data = 0;
- if (SH_FSI_LRM_INV & flags)
- data |= 1 << 12;
- if (SH_FSI_BRM_INV & flags)
- data |= 1 << 8;
- if (SH_FSI_LRS_INV & flags)
- data |= 1 << 4;
- if (SH_FSI_BRS_INV & flags)
- data |= 1 << 0;
-
+ if (fsi->bit_clk_inv)
+ data |= (1 << 0);
+ if (fsi->lr_clk_inv)
+ data |= (1 << 4);
+ if (fsi_is_clk_master(fsi))
+ data <<= 8;
fsi_reg_write(fsi, CKG2, data);
- /* set format */
- fsi_reg_write(fsi, DO_FMT, fsi->do_fmt);
- fsi_reg_write(fsi, DI_FMT, fsi->di_fmt);
-
/* spdif ? */
if (fsi_is_spdif(fsi)) {
fsi_spdif_clk_ctrl(fsi, 1);
@@ -923,69 +1589,83 @@ static int fsi_hw_startup(struct fsi_priv *fsi,
}
/*
- * FIXME
- *
- * FSI driver assumed that data package is in-back.
- * FSI2 chip can select it.
+ * get bus settings
*/
- if (fsi_ver >= 2) {
- fsi_reg_write(fsi, OUT_DMAC, (1 << 4));
- fsi_reg_write(fsi, IN_DMAC, (1 << 4));
+ data = 0;
+ switch (io->sample_width) {
+ case 2:
+ data = BUSOP_GET(16, io->bus_option);
+ break;
+ case 4:
+ data = BUSOP_GET(24, io->bus_option);
+ break;
}
+ fsi_format_bus_setup(fsi, io, data, dev);
/* irq clear */
- fsi_irq_disable(fsi, is_play);
+ fsi_irq_disable(fsi, io);
fsi_irq_clear_status(fsi);
/* fifo init */
- fsi_fifo_init(fsi, is_play, dev);
+ fsi_fifo_init(fsi, io, dev);
+
+ /* start master clock */
+ if (fsi_is_clk_master(fsi))
+ return fsi_clk_enable(dev, fsi);
return 0;
}
-static void fsi_hw_shutdown(struct fsi_priv *fsi,
- int is_play,
+static int fsi_hw_shutdown(struct fsi_priv *fsi,
struct device *dev)
{
+ /* stop master clock */
if (fsi_is_clk_master(fsi))
- fsi_set_master_clk(dev, fsi, fsi->rate, 0);
+ return fsi_clk_disable(dev, fsi);
+
+ return 0;
}
static int fsi_dai_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct fsi_priv *fsi = fsi_get_priv(substream);
- int is_play = fsi_is_play(substream);
- return fsi_hw_startup(fsi, is_play, dai->dev);
+ fsi_clk_invalid(fsi);
+
+ return 0;
}
static void fsi_dai_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct fsi_priv *fsi = fsi_get_priv(substream);
- int is_play = fsi_is_play(substream);
- fsi_hw_shutdown(fsi, is_play, dai->dev);
- fsi->rate = 0;
+ fsi_clk_invalid(fsi);
}
static int fsi_dai_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai)
{
struct fsi_priv *fsi = fsi_get_priv(substream);
- int is_play = fsi_is_play(substream);
+ struct fsi_stream *io = fsi_stream_get(fsi, substream);
int ret = 0;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
- fsi_stream_push(fsi, is_play, substream);
- ret = is_play ? fsi_data_push(fsi) : fsi_data_pop(fsi);
- fsi_port_start(fsi, is_play);
+ fsi_stream_init(fsi, io, substream);
+ if (!ret)
+ ret = fsi_hw_startup(fsi, io, dai->dev);
+ if (!ret)
+ ret = fsi_stream_transfer(io);
+ if (!ret)
+ fsi_stream_start(fsi, io);
break;
case SNDRV_PCM_TRIGGER_STOP:
- fsi_port_stop(fsi, is_play);
- fsi_stream_pop(fsi, is_play);
+ if (!ret)
+ ret = fsi_hw_shutdown(fsi, dai->dev);
+ fsi_stream_stop(fsi, io);
+ fsi_stream_quit(fsi, io);
break;
}
@@ -994,41 +1674,31 @@ static int fsi_dai_trigger(struct snd_pcm_substream *substream, int cmd,
static int fsi_set_fmt_dai(struct fsi_priv *fsi, unsigned int fmt)
{
- u32 data = 0;
-
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
- data = CR_I2S;
+ fsi->fmt = CR_I2S;
fsi->chan_num = 2;
break;
case SND_SOC_DAIFMT_LEFT_J:
- data = CR_PCM;
+ fsi->fmt = CR_PCM;
fsi->chan_num = 2;
break;
default:
return -EINVAL;
}
- fsi->do_fmt = data;
- fsi->di_fmt = data;
-
return 0;
}
static int fsi_set_fmt_spdif(struct fsi_priv *fsi)
{
struct fsi_master *master = fsi_get_master(fsi);
- u32 data = 0;
- if (master->core->ver < 2)
+ if (fsi_version(master) < 2)
return -EINVAL;
- data = CR_BWS_16 | CR_DTMD_SPDIF_PCM | CR_PCM;
+ fsi->fmt = CR_DTMD_SPDIF_PCM | CR_PCM;
fsi->chan_num = 2;
- fsi->spdif = 1;
-
- fsi->do_fmt = data;
- fsi->di_fmt = data;
return 0;
}
@@ -1036,39 +1706,55 @@ static int fsi_set_fmt_spdif(struct fsi_priv *fsi)
static int fsi_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
struct fsi_priv *fsi = fsi_get_priv_frm_dai(dai);
- struct fsi_master *master = fsi_get_master(fsi);
- set_rate_func set_rate = fsi_get_info_set_rate(master);
- u32 flags = fsi_get_info_flags(fsi);
int ret;
/* set master/slave audio interface */
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBM_CFM:
- fsi->clk_master = 1;
break;
case SND_SOC_DAIFMT_CBS_CFS:
+ fsi->clk_master = 1; /* codec is slave, cpu is master */
break;
default:
return -EINVAL;
}
- if (fsi_is_clk_master(fsi) && !set_rate) {
- dev_err(dai->dev, "platform doesn't have set_rate\n");
- return -EINVAL;
- }
-
- /* set format */
- switch (flags & SH_FSI_FMT_MASK) {
- case SH_FSI_FMT_DAI:
- ret = fsi_set_fmt_dai(fsi, fmt & SND_SOC_DAIFMT_FORMAT_MASK);
+ /* set clock inversion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_IF:
+ fsi->bit_clk_inv = 0;
+ fsi->lr_clk_inv = 1;
break;
- case SH_FSI_FMT_SPDIF:
- ret = fsi_set_fmt_spdif(fsi);
+ case SND_SOC_DAIFMT_IB_NF:
+ fsi->bit_clk_inv = 1;
+ fsi->lr_clk_inv = 0;
break;
+ case SND_SOC_DAIFMT_IB_IF:
+ fsi->bit_clk_inv = 1;
+ fsi->lr_clk_inv = 1;
+ break;
+ case SND_SOC_DAIFMT_NB_NF:
default:
- ret = -EINVAL;
+ fsi->bit_clk_inv = 0;
+ fsi->lr_clk_inv = 0;
+ break;
}
+ if (fsi_is_clk_master(fsi)) {
+ if (fsi->clk_cpg)
+ fsi_clk_init(dai->dev, fsi, 0, 1, 1,
+ fsi_clk_set_rate_cpg);
+ else
+ fsi_clk_init(dai->dev, fsi, 1, 1, 0,
+ fsi_clk_set_rate_external);
+ }
+
+ /* set format */
+ if (fsi_is_spdif(fsi))
+ ret = fsi_set_fmt_spdif(fsi);
+ else
+ ret = fsi_set_fmt_dai(fsi, fmt & SND_SOC_DAIFMT_FORMAT_MASK);
+
return ret;
}
@@ -1077,19 +1763,11 @@ static int fsi_dai_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct fsi_priv *fsi = fsi_get_priv(substream);
- long rate = params_rate(params);
- int ret;
- if (!fsi_is_clk_master(fsi))
- return 0;
-
- ret = fsi_set_master_clk(dai->dev, fsi, rate, 1);
- if (ret < 0)
- return ret;
-
- fsi->rate = rate;
+ if (fsi_is_clk_master(fsi))
+ fsi_clk_valid(fsi, params_rate(params));
- return ret;
+ return 0;
}
static const struct snd_soc_dai_ops fsi_dai_ops = {
@@ -1109,12 +1787,6 @@ static struct snd_pcm_hardware fsi_pcm_hardware = {
SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_PAUSE,
- .formats = FSI_FMTS,
- .rates = FSI_RATES,
- .rate_min = 8000,
- .rate_max = 192000,
- .channels_min = 1,
- .channels_max = 2,
.buffer_bytes_max = 64 * 1024,
.period_bytes_min = 32,
.period_bytes_max = 8192,
@@ -1151,7 +1823,7 @@ static int fsi_hw_free(struct snd_pcm_substream *substream)
static snd_pcm_uframes_t fsi_pointer(struct snd_pcm_substream *substream)
{
struct fsi_priv *fsi = fsi_get_priv(substream);
- struct fsi_stream *io = fsi_get_stream(fsi, fsi_is_play(substream));
+ struct fsi_stream *io = fsi_stream_get(fsi, substream);
return fsi_sample2frame(fsi, io->buff_sample_pos);
}
@@ -1201,14 +1873,14 @@ static struct snd_soc_dai_driver fsi_soc_dai[] = {
.playback = {
.rates = FSI_RATES,
.formats = FSI_FMTS,
- .channels_min = 1,
- .channels_max = 8,
+ .channels_min = 2,
+ .channels_max = 2,
},
.capture = {
.rates = FSI_RATES,
.formats = FSI_FMTS,
- .channels_min = 1,
- .channels_max = 8,
+ .channels_min = 2,
+ .channels_max = 2,
},
.ops = &fsi_dai_ops,
},
@@ -1217,14 +1889,14 @@ static struct snd_soc_dai_driver fsi_soc_dai[] = {
.playback = {
.rates = FSI_RATES,
.formats = FSI_FMTS,
- .channels_min = 1,
- .channels_max = 8,
+ .channels_min = 2,
+ .channels_max = 2,
},
.capture = {
.rates = FSI_RATES,
.formats = FSI_FMTS,
- .channels_min = 1,
- .channels_max = 8,
+ .channels_min = 2,
+ .channels_max = 2,
},
.ops = &fsi_dai_ops,
},
@@ -1236,20 +1908,101 @@ static struct snd_soc_platform_driver fsi_soc_platform = {
.pcm_free = fsi_pcm_free,
};
+static const struct snd_soc_component_driver fsi_soc_component = {
+ .name = "fsi",
+};
+
/*
* platform function
*/
+static void fsi_of_parse(char *name,
+ struct device_node *np,
+ struct sh_fsi_port_info *info,
+ struct device *dev)
+{
+ int i;
+ char prop[128];
+ unsigned long flags = 0;
+ struct {
+ char *name;
+ unsigned int val;
+ } of_parse_property[] = {
+ { "spdif-connection", SH_FSI_FMT_SPDIF },
+ { "stream-mode-support", SH_FSI_ENABLE_STREAM_MODE },
+ { "use-internal-clock", SH_FSI_CLK_CPG },
+ };
+
+ for (i = 0; i < ARRAY_SIZE(of_parse_property); i++) {
+ sprintf(prop, "%s,%s", name, of_parse_property[i].name);
+ if (of_get_property(np, prop, NULL))
+ flags |= of_parse_property[i].val;
+ }
+ info->flags = flags;
+ dev_dbg(dev, "%s flags : %lx\n", name, info->flags);
+}
+
+static void fsi_port_info_init(struct fsi_priv *fsi,
+ struct sh_fsi_port_info *info)
+{
+ if (info->flags & SH_FSI_FMT_SPDIF)
+ fsi->spdif = 1;
+
+ if (info->flags & SH_FSI_CLK_CPG)
+ fsi->clk_cpg = 1;
+
+ if (info->flags & SH_FSI_ENABLE_STREAM_MODE)
+ fsi->enable_stream = 1;
+}
+
+static void fsi_handler_init(struct fsi_priv *fsi,
+ struct sh_fsi_port_info *info)
+{
+ fsi->playback.handler = &fsi_pio_push_handler; /* default PIO */
+ fsi->playback.priv = fsi;
+ fsi->capture.handler = &fsi_pio_pop_handler; /* default PIO */
+ fsi->capture.priv = fsi;
+
+ if (info->tx_id) {
+ fsi->playback.dma_id = info->tx_id;
+ fsi->playback.handler = &fsi_dma_push_handler;
+ }
+}
+
+static struct of_device_id fsi_of_match[];
static int fsi_probe(struct platform_device *pdev)
{
struct fsi_master *master;
- const struct platform_device_id *id_entry;
+ struct device_node *np = pdev->dev.of_node;
+ struct sh_fsi_platform_info info;
+ const struct fsi_core *core;
+ struct fsi_priv *fsi;
struct resource *res;
unsigned int irq;
int ret;
- id_entry = pdev->id_entry;
- if (!id_entry) {
+ memset(&info, 0, sizeof(info));
+
+ core = NULL;
+ if (np) {
+ const struct of_device_id *of_id;
+
+ of_id = of_match_device(fsi_of_match, &pdev->dev);
+ if (of_id) {
+ core = of_id->data;
+ fsi_of_parse("fsia", np, &info.port_a, &pdev->dev);
+ fsi_of_parse("fsib", np, &info.port_b, &pdev->dev);
+ }
+ } else {
+ const struct platform_device_id *id_entry = pdev->id_entry;
+ if (id_entry)
+ core = (struct fsi_core *)id_entry->driver_data;
+
+ if (pdev->dev.platform_data)
+ memcpy(&info, pdev->dev.platform_data, sizeof(info));
+ }
+
+ if (!core) {
dev_err(&pdev->dev, "unknown fsi device\n");
return -ENODEV;
}
@@ -1258,58 +2011,70 @@ static int fsi_probe(struct platform_device *pdev)
irq = platform_get_irq(pdev, 0);
if (!res || (int)irq <= 0) {
dev_err(&pdev->dev, "Not enough FSI platform resources.\n");
- ret = -ENODEV;
- goto exit;
+ return -ENODEV;
}
- master = kzalloc(sizeof(*master), GFP_KERNEL);
+ master = devm_kzalloc(&pdev->dev, sizeof(*master), GFP_KERNEL);
if (!master) {
dev_err(&pdev->dev, "Could not allocate master\n");
- ret = -ENOMEM;
- goto exit;
+ return -ENOMEM;
}
- master->base = ioremap_nocache(res->start, resource_size(res));
+ master->base = devm_ioremap_nocache(&pdev->dev,
+ res->start, resource_size(res));
if (!master->base) {
- ret = -ENXIO;
dev_err(&pdev->dev, "Unable to ioremap FSI registers.\n");
- goto exit_kfree;
+ return -ENXIO;
}
/* master setting */
- master->irq = irq;
- master->info = pdev->dev.platform_data;
- master->core = (struct fsi_core *)id_entry->driver_data;
+ master->core = core;
spin_lock_init(&master->lock);
/* FSI A setting */
- master->fsia.base = master->base;
- master->fsia.master = master;
+ fsi = &master->fsia;
+ fsi->base = master->base;
+ fsi->master = master;
+ fsi_port_info_init(fsi, &info.port_a);
+ fsi_handler_init(fsi, &info.port_a);
+ ret = fsi_stream_probe(fsi, &pdev->dev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "FSIA stream probe failed\n");
+ return ret;
+ }
/* FSI B setting */
- master->fsib.base = master->base + 0x40;
- master->fsib.master = master;
+ fsi = &master->fsib;
+ fsi->base = master->base + 0x40;
+ fsi->master = master;
+ fsi_port_info_init(fsi, &info.port_b);
+ fsi_handler_init(fsi, &info.port_b);
+ ret = fsi_stream_probe(fsi, &pdev->dev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "FSIB stream probe failed\n");
+ goto exit_fsia;
+ }
pm_runtime_enable(&pdev->dev);
dev_set_drvdata(&pdev->dev, master);
- ret = request_irq(irq, &fsi_interrupt, 0,
- id_entry->name, master);
+ ret = devm_request_irq(&pdev->dev, irq, &fsi_interrupt, 0,
+ dev_name(&pdev->dev), master);
if (ret) {
dev_err(&pdev->dev, "irq request err\n");
- goto exit_iounmap;
+ goto exit_fsib;
}
ret = snd_soc_register_platform(&pdev->dev, &fsi_soc_platform);
if (ret < 0) {
dev_err(&pdev->dev, "cannot snd soc register\n");
- goto exit_free_irq;
+ goto exit_fsib;
}
- ret = snd_soc_register_dais(&pdev->dev, fsi_soc_dai,
- ARRAY_SIZE(fsi_soc_dai));
+ ret = snd_soc_register_component(&pdev->dev, &fsi_soc_component,
+ fsi_soc_dai, ARRAY_SIZE(fsi_soc_dai));
if (ret < 0) {
- dev_err(&pdev->dev, "cannot snd dai register\n");
+ dev_err(&pdev->dev, "cannot snd component register\n");
goto exit_snd_soc;
}
@@ -1317,15 +2082,12 @@ static int fsi_probe(struct platform_device *pdev)
exit_snd_soc:
snd_soc_unregister_platform(&pdev->dev);
-exit_free_irq:
- free_irq(irq, master);
-exit_iounmap:
- iounmap(master->base);
+exit_fsib:
pm_runtime_disable(&pdev->dev);
-exit_kfree:
- kfree(master);
- master = NULL;
-exit:
+ fsi_stream_remove(&master->fsib);
+exit_fsia:
+ fsi_stream_remove(&master->fsia);
+
return ret;
}
@@ -1335,43 +2097,37 @@ static int fsi_remove(struct platform_device *pdev)
master = dev_get_drvdata(&pdev->dev);
- free_irq(master->irq, master);
pm_runtime_disable(&pdev->dev);
- snd_soc_unregister_dais(&pdev->dev, ARRAY_SIZE(fsi_soc_dai));
+ snd_soc_unregister_component(&pdev->dev);
snd_soc_unregister_platform(&pdev->dev);
- iounmap(master->base);
- kfree(master);
+ fsi_stream_remove(&master->fsia);
+ fsi_stream_remove(&master->fsib);
return 0;
}
static void __fsi_suspend(struct fsi_priv *fsi,
- int is_play,
+ struct fsi_stream *io,
struct device *dev)
{
- if (!fsi_stream_is_working(fsi, is_play))
+ if (!fsi_stream_is_working(fsi, io))
return;
- fsi_port_stop(fsi, is_play);
- fsi_hw_shutdown(fsi, is_play, dev);
+ fsi_stream_stop(fsi, io);
+ fsi_hw_shutdown(fsi, dev);
}
static void __fsi_resume(struct fsi_priv *fsi,
- int is_play,
+ struct fsi_stream *io,
struct device *dev)
{
- if (!fsi_stream_is_working(fsi, is_play))
+ if (!fsi_stream_is_working(fsi, io))
return;
- fsi_hw_startup(fsi, is_play, dev);
-
- if (fsi_is_clk_master(fsi) && fsi->rate)
- fsi_set_master_clk(dev, fsi, fsi->rate, 1);
-
- fsi_port_start(fsi, is_play);
-
+ fsi_hw_startup(fsi, io, dev);
+ fsi_stream_start(fsi, io);
}
static int fsi_suspend(struct device *dev)
@@ -1380,11 +2136,11 @@ static int fsi_suspend(struct device *dev)
struct fsi_priv *fsia = &master->fsia;
struct fsi_priv *fsib = &master->fsib;
- __fsi_suspend(fsia, 1, dev);
- __fsi_suspend(fsia, 0, dev);
+ __fsi_suspend(fsia, &fsia->playback, dev);
+ __fsi_suspend(fsia, &fsia->capture, dev);
- __fsi_suspend(fsib, 1, dev);
- __fsi_suspend(fsib, 0, dev);
+ __fsi_suspend(fsib, &fsib->playback, dev);
+ __fsi_suspend(fsib, &fsib->capture, dev);
return 0;
}
@@ -1395,32 +2151,18 @@ static int fsi_resume(struct device *dev)
struct fsi_priv *fsia = &master->fsia;
struct fsi_priv *fsib = &master->fsib;
- __fsi_resume(fsia, 1, dev);
- __fsi_resume(fsia, 0, dev);
+ __fsi_resume(fsia, &fsia->playback, dev);
+ __fsi_resume(fsia, &fsia->capture, dev);
- __fsi_resume(fsib, 1, dev);
- __fsi_resume(fsib, 0, dev);
+ __fsi_resume(fsib, &fsib->playback, dev);
+ __fsi_resume(fsib, &fsib->capture, dev);
return 0;
}
-static int fsi_runtime_nop(struct device *dev)
-{
- /* Runtime PM callback shared between ->runtime_suspend()
- * and ->runtime_resume(). Simply returns success.
- *
- * This driver re-initializes all registers after
- * pm_runtime_get_sync() anyway so there is no need
- * to save and restore registers here.
- */
- return 0;
-}
-
static struct dev_pm_ops fsi_pm_ops = {
.suspend = fsi_suspend,
.resume = fsi_resume,
- .runtime_suspend = fsi_runtime_nop,
- .runtime_resume = fsi_runtime_nop,
};
static struct fsi_core fsi1_core = {
@@ -1443,6 +2185,13 @@ static struct fsi_core fsi2_core = {
.b_mclk = B_MST_CTLR,
};
+static struct of_device_id fsi_of_match[] = {
+ { .compatible = "renesas,sh_fsi", .data = &fsi1_core},
+ { .compatible = "renesas,sh_fsi2", .data = &fsi2_core},
+ {},
+};
+MODULE_DEVICE_TABLE(of, fsi_of_match);
+
static struct platform_device_id fsi_id_table[] = {
{ "sh_fsi", (kernel_ulong_t)&fsi1_core },
{ "sh_fsi2", (kernel_ulong_t)&fsi2_core },
@@ -1454,6 +2203,7 @@ static struct platform_driver fsi_driver = {
.driver = {
.name = "fsi-pcm-audio",
.pm = &fsi_pm_ops,
+ .of_match_table = fsi_of_match,
},
.probe = fsi_probe,
.remove = fsi_remove,