diff options
Diffstat (limited to 'sound/soc/sh/fsi.c')
-rw-r--r-- | sound/soc/sh/fsi.c | 203 |
1 files changed, 114 insertions, 89 deletions
diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index 2b06402801e..0c9997e2d8c 100644 --- a/sound/soc/sh/fsi.c +++ b/sound/soc/sh/fsi.c @@ -78,6 +78,8 @@ /* CKG1 */ #define ACKMD_MASK 0x00007000 #define BPFMD_MASK 0x00000700 +#define DIMD (1 << 4) +#define DOMD (1 << 0) /* A/B MST_CTLR */ #define BP (1 << 4) /* Fix the signal of Biphase output */ @@ -111,6 +113,8 @@ #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); + /* * FSI driver use below type name for variable * @@ -128,7 +132,6 @@ struct fsi_stream { struct snd_pcm_substream *substream; int fifo_max_num; - int chan_num; int buff_offset; int buff_len; @@ -143,6 +146,7 @@ struct fsi_priv { void __iomem *base; struct fsi_master *master; + int chan_num; struct fsi_stream playback; struct fsi_stream capture; @@ -252,9 +256,8 @@ static struct snd_soc_dai *fsi_get_dai(struct snd_pcm_substream *substream) return rtd->cpu_dai; } -static struct fsi_priv *fsi_get_priv(struct snd_pcm_substream *substream) +static struct fsi_priv *fsi_get_priv_frm_dai(struct snd_soc_dai *dai) { - struct snd_soc_dai *dai = fsi_get_dai(substream); struct fsi_master *master = snd_soc_dai_get_drvdata(dai); if (dai->id == 0) @@ -263,11 +266,27 @@ static struct fsi_priv *fsi_get_priv(struct snd_pcm_substream *substream) return &master->fsib; } +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) { int is_porta = fsi_is_port_a(fsi); struct fsi_master *master = fsi_get_master(fsi); + if (!master->info) + return 0; + return is_porta ? master->info->porta_flags : master->info->portb_flags; } @@ -288,21 +307,6 @@ static inline struct fsi_stream *fsi_get_stream(struct fsi_priv *fsi, return is_play ? &fsi->playback : &fsi->capture; } -static int fsi_is_master_mode(struct fsi_priv *fsi, int is_play) -{ - u32 mode; - u32 flags = fsi_get_info_flags(fsi); - - mode = is_play ? SH_FSI_OUT_SLAVE_MODE : SH_FSI_IN_SLAVE_MODE; - - /* return - * 1 : master mode - * 0 : slave mode - */ - - return (mode & flags) != mode; -} - static u32 fsi_get_port_shift(struct fsi_priv *fsi, int is_play) { int is_porta = fsi_is_port_a(fsi); @@ -357,7 +361,6 @@ static void fsi_stream_pop(struct fsi_priv *fsi, int is_play) static int fsi_get_fifo_data_num(struct fsi_priv *fsi, int is_play) { u32 status; - struct fsi_stream *io = fsi_get_stream(fsi, is_play); int data_num; status = is_play ? @@ -365,7 +368,7 @@ static int fsi_get_fifo_data_num(struct fsi_priv *fsi, int is_play) fsi_reg_read(fsi, DIFF_ST); data_num = 0x1ff & (status >> 8); - data_num *= io->chan_num; + data_num *= fsi->chan_num; return data_num; } @@ -387,7 +390,7 @@ static int fsi_get_frame_width(struct fsi_priv *fsi, int is_play) struct snd_pcm_substream *substream = io->substream; struct snd_pcm_runtime *runtime = substream->runtime; - return frames_to_bytes(runtime, 1) / io->chan_num; + return frames_to_bytes(runtime, 1) / fsi->chan_num; } static void fsi_count_fifo_err(struct fsi_priv *fsi) @@ -580,10 +583,10 @@ static void fsi_fifo_init(struct fsi_priv *fsi, * 7 channels: 32 ( 32 x 7 = 224) * 8 channels: 32 ( 32 x 8 = 256) */ - for (i = 1; i < io->chan_num; i <<= 1) + for (i = 1; i < fsi->chan_num; i <<= 1) io->fifo_max_num >>= 1; dev_dbg(dai->dev, "%d channel %d store\n", - io->chan_num, io->fifo_max_num); + fsi->chan_num, io->fifo_max_num); /* * set interrupt generation factor @@ -659,7 +662,7 @@ static int fsi_fifo_data_ctrl(struct fsi_priv *fsi, int stream) * data_num_max : number of FSI fifo free space * data_num : number of ALSA residue data */ - data_num_max = io->fifo_max_num * io->chan_num; + data_num_max = io->fifo_max_num * fsi->chan_num; data_num_max -= fsi_get_fifo_data_num(fsi, is_play); data_num = data_residue_num; @@ -754,25 +757,12 @@ static int fsi_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct fsi_priv *fsi = fsi_get_priv(substream); - struct fsi_master *master = fsi_get_master(fsi); - struct fsi_stream *io; u32 flags = fsi_get_info_flags(fsi); - u32 fmt; u32 data; int is_play = fsi_is_play(substream); - int is_master; - - io = fsi_get_stream(fsi, is_play); pm_runtime_get_sync(dai->dev); - /* CKG1 */ - data = is_play ? (1 << 0) : (1 << 4); - is_master = fsi_is_master_mode(fsi, is_play); - if (is_master) - fsi_reg_mask_set(fsi, CKG1, data, data); - else - fsi_reg_mask_set(fsi, CKG1, data, 0); /* clock inversion (CKG2) */ data = 0; @@ -787,54 +777,6 @@ static int fsi_dai_startup(struct snd_pcm_substream *substream, fsi_reg_write(fsi, CKG2, data); - /* do fmt, di fmt */ - data = 0; - fmt = is_play ? SH_FSI_GET_OFMT(flags) : SH_FSI_GET_IFMT(flags); - switch (fmt) { - case SH_FSI_FMT_MONO: - data = CR_MONO; - io->chan_num = 1; - break; - case SH_FSI_FMT_MONO_DELAY: - data = CR_MONO_D; - io->chan_num = 1; - break; - case SH_FSI_FMT_PCM: - data = CR_PCM; - io->chan_num = 2; - break; - case SH_FSI_FMT_I2S: - data = CR_I2S; - io->chan_num = 2; - break; - case SH_FSI_FMT_TDM: - io->chan_num = is_play ? - SH_FSI_GET_CH_O(flags) : SH_FSI_GET_CH_I(flags); - data = CR_TDM | (io->chan_num - 1); - break; - case SH_FSI_FMT_TDM_DELAY: - io->chan_num = is_play ? - SH_FSI_GET_CH_O(flags) : SH_FSI_GET_CH_I(flags); - data = CR_TDM_D | (io->chan_num - 1); - break; - case SH_FSI_FMT_SPDIF: - if (master->core->ver < 2) { - dev_err(dai->dev, "This FSI can not use SPDIF\n"); - return -EINVAL; - } - data = CR_BWS_16 | CR_DTMD_SPDIF_PCM | CR_PCM; - io->chan_num = 2; - fsi_spdif_clk_ctrl(fsi, 1); - fsi_reg_mask_set(fsi, OUT_SEL, DMMD, DMMD); - break; - default: - dev_err(dai->dev, "unknown format.\n"); - return -EINVAL; - } - is_play ? - fsi_reg_write(fsi, DO_FMT, data) : - fsi_reg_write(fsi, DI_FMT, data); - /* irq clear */ fsi_irq_disable(fsi, is_play); fsi_irq_clear_status(fsi); @@ -851,12 +793,12 @@ static void fsi_dai_shutdown(struct snd_pcm_substream *substream, struct fsi_priv *fsi = fsi_get_priv(substream); int is_play = fsi_is_play(substream); struct fsi_master *master = fsi_get_master(fsi); - int (*set_rate)(struct device *dev, int is_porta, int rate, int enable); + set_rate_func set_rate; fsi_irq_disable(fsi, is_play); fsi_clk_ctrl(fsi, 0); - set_rate = master->info->set_rate; + set_rate = fsi_get_info_set_rate(master); if (set_rate && fsi->rate) set_rate(dai->dev, fsi_is_port_a(fsi), fsi->rate, 0); fsi->rate = 0; @@ -889,18 +831,100 @@ static int fsi_dai_trigger(struct snd_pcm_substream *substream, int cmd, return ret; } +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->chan_num = 2; + break; + case SND_SOC_DAIFMT_LEFT_J: + data = CR_PCM; + fsi->chan_num = 2; + break; + default: + return -EINVAL; + } + + fsi_reg_write(fsi, DO_FMT, data); + fsi_reg_write(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) + return -EINVAL; + + data = CR_BWS_16 | CR_DTMD_SPDIF_PCM | CR_PCM; + fsi->chan_num = 2; + fsi_spdif_clk_ctrl(fsi, 1); + fsi_reg_mask_set(fsi, OUT_SEL, DMMD, DMMD); + + fsi_reg_write(fsi, DO_FMT, data); + fsi_reg_write(fsi, DI_FMT, data); + + return 0; +} + +static int fsi_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct fsi_priv *fsi = fsi_get_priv_frm_dai(dai); + u32 flags = fsi_get_info_flags(fsi); + u32 data = 0; + int ret; + + pm_runtime_get_sync(dai->dev); + + /* set master/slave audio interface */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + data = DIMD | DOMD; + break; + case SND_SOC_DAIFMT_CBS_CFS: + break; + default: + ret = -EINVAL; + goto set_fmt_exit; + } + fsi_reg_mask_set(fsi, CKG1, (DIMD | DOMD), data); + + /* 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); + break; + case SH_FSI_FMT_SPDIF: + ret = fsi_set_fmt_spdif(fsi); + break; + default: + ret = -EINVAL; + } + +set_fmt_exit: + pm_runtime_put_sync(dai->dev); + + return ret; +} + static int fsi_dai_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { struct fsi_priv *fsi = fsi_get_priv(substream); struct fsi_master *master = fsi_get_master(fsi); - int (*set_rate)(struct device *dev, int is_porta, int rate, int enable); + set_rate_func set_rate; int fsi_ver = master->core->ver; long rate = params_rate(params); int ret; - set_rate = master->info->set_rate; + set_rate = fsi_get_info_set_rate(master); if (!set_rate) return 0; @@ -975,6 +999,7 @@ static struct snd_soc_dai_ops fsi_dai_ops = { .startup = fsi_dai_startup, .shutdown = fsi_dai_shutdown, .trigger = fsi_dai_trigger, + .set_fmt = fsi_dai_set_fmt, .hw_params = fsi_dai_hw_params, }; |