/*
* Renesas R-Car SRC support
*
* Copyright (C) 2013 Renesas Solutions Corp.
* Kuninori Morimoto <kuninori.morimoto.gx@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 "rsnd.h"
#define SRC_NAME "src"
/* SRCx_STATUS */
#define OUF_SRCO ((1 << 12) | (1 << 13))
#define OUF_SRCI ((1 << 9) | (1 << 8))
/* SCU_SYSTEM_STATUS0/1 */
#define OUF_SRC(id) ((1 << (id + 16)) | (1 << id))
struct rsnd_src {
struct rsnd_src_platform_info *info; /* rcar_snd.h */
struct rsnd_mod mod;
struct rsnd_kctrl_cfg_s sen; /* sync convert enable */
struct rsnd_kctrl_cfg_s sync; /* sync convert */
u32 convert_rate; /* sampling rate convert */
int err;
};
#define RSND_SRC_NAME_SIZE 16
#define rsnd_enable_sync_convert(src) ((src)->sen.val)
#define rsnd_src_of_node(priv) \
of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,src")
#define rsnd_mod_to_src(_mod) \
container_of((_mod), struct rsnd_src, mod)
#define for_each_rsnd_src(pos, priv, i) \
for ((i) = 0; \
((i) < rsnd_src_nr(priv)) && \
((pos) = (struct rsnd_src *)(priv)->src + i); \
i++)
/*
* image of SRC (Sampling Rate Converter)
*
* 96kHz <-> +-----+ 48kHz +-----+ 48kHz +-------+
* 48kHz <-> | SRC | <------> | SSI | <-----> | codec |
* 44.1kHz <-> +-----+ +-----+ +-------+
* ...
*
*/
/*
* src.c is caring...
*
* Gen1
*
* [mem] -> [SRU] -> [SSI]
* |--------|
*
* Gen2
*
* [mem] -> [SRC] -> [SSIU] -> [SSI]
* |-----------------|
*/
/*
* How to use SRC bypass mode for debugging
*
* SRC has bypass mode, and it is useful for debugging.
* In Gen2 case,
* SRCm_MODE controls whether SRC is used or not
* SSI_MODE0 controls whether SSIU which receives SRC data
* is used or not.
* Both SRCm_MODE/SSI_MODE0 settings are needed if you use SRC,
* but SRC bypass mode needs SSI_MODE0 only.
*
* This driver request
* struct rsnd_src_platform_info {
* u32 convert_rate;
* int dma_id;
* }
*
* rsnd_src_convert_rate() indicates
* above convert_rate, and it controls
* whether SRC is used or not.
*
* ex) doesn't use SRC
* static struct rsnd_dai_platform_info rsnd_dai = {
* .playback = { .ssi = &rsnd_ssi[0], },
* };
*
* ex) uses SRC
* static struct rsnd_src_platform_info rsnd_src[] = {
* RSND_SCU(48000, 0),
* ...
* };
* static struct rsnd_dai_platform_info rsnd_dai = {
* .playback = { .ssi = &rsnd_ssi[0], .src = &rsnd_src[0] },
* };
*
* ex) uses SRC bypass mode
* static struct rsnd_src_platform_info rsnd_src[] = {
* RSND_SCU(0, 0),
* ...
* };
* static struct rsnd_dai_platform_info rsnd_dai = {
* .playback = { .ssi = &rsnd_ssi[0], .src = &rsnd_src[0] },
* };
*
*/
/*
* Gen1/Gen2 common functions
*/
static struct dma_chan *rsnd_src_dma_req(struct rsnd_dai_stream *io,
struct rsnd_mod *mod)
{
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
int is_play = rsnd_io_is_play(io);
return rsnd_dma_request_channel(rsnd_src_of_node(priv),
mod,
is_play ? "rx" : "tx");
}
int rsnd_src_ssiu_start(struct rsnd_mod *ssi_mod,
struct rsnd_dai_stream *io,
int use_busif)
{
struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
int ssi_id = rsnd_mod_id(ssi_mod);
/*
* SSI_MODE0
*/
rsnd_mod_bset(ssi_mod, SSI_MODE0, (1 << ssi_id),
!use_busif << ssi_id);
/*
* SSI_MODE1
*/
if (rsnd_ssi_is_pin_sharing(ssi_mod)) {
int shift = -1;
switch (ssi_id) {
case 1:
shift = 0;
break;
case 2:
shift = 2;
break;
case 4:
shift = 16;
break;
}
if (shift >= 0)
rsnd_mod_bset(ssi_mod, SSI_MODE1,
0x3 << shift,
rsnd_rdai_is_clk_master(rdai) ?
0x2 << shift : 0x1 << shift);
}
/*
* DMA settings for SSIU
*/
if (use_busif) {
u32 val = 0x76543210;
u32 mask = ~0;
rsnd_mod_write(ssi_mod, SSI_BUSIF_ADINR,
rsnd_get_adinr(ssi_mod, io));
rsnd_mod_write(ssi_mod, SSI_BUSIF_MODE, 1);
rsnd_mod_write(ssi_mod, SSI_CTRL, 0x1);
mask <<= runtime->channels * 4;
val = val & mask;
switch (runtime->sample_bits) {
case 16:
val |= 0x67452301 & ~mask;
break;
case 32:
val |= 0x76543210 & ~mask;
break;
}
rsnd_mod_write(ssi_mod, BUSIF_DALIGN, val);
}
return 0;
}
int rsnd_src_ssiu_stop(struct rsnd_mod *ssi_mod,
struct rsnd_dai_stream *io)
{
/*
* DMA settings for SSIU
*/
rsnd_mod_write(ssi_mod, SSI_CTRL,