diff options
Diffstat (limited to 'sound/soc/sh/fsi.c')
-rw-r--r-- | sound/soc/sh/fsi.c | 98 |
1 files changed, 92 insertions, 6 deletions
diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index e551ca45f03..a1ce6089177 100644 --- a/sound/soc/sh/fsi.c +++ b/sound/soc/sh/fsi.c @@ -65,6 +65,10 @@ #define ERR_UNDER 0x00000001 #define ST_ERR (ERR_OVER | ERR_UNDER) +/* CKG1 */ +#define ACKMD_MASK 0x00007000 +#define BPFMD_MASK 0x00000700 + /* CLK_RST */ #define B_CLK 0x00000010 #define A_CLK 0x00000001 @@ -734,12 +738,6 @@ static int fsi_dai_startup(struct snd_pcm_substream *substream, } fsi_reg_write(fsi, reg, data); - /* - * clear clk reset if master mode - */ - if (is_master) - fsi_clk_ctrl(fsi, 1); - /* irq clear */ fsi_irq_disable(fsi, is_play); fsi_irq_clear_status(fsi); @@ -786,10 +784,98 @@ static int fsi_dai_trigger(struct snd_pcm_substream *substream, int cmd, 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)(int is_porta, int rate) = master->info->set_rate; + int fsi_ver = master->core->ver; + int is_play = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK); + int ret; + + /* if slave mode, set_rate is not needed */ + if (!fsi_is_master_mode(fsi, is_play)) + return 0; + + /* it is error if no set_rate */ + if (!set_rate) + return -EIO; + + /* clock stop */ + pm_runtime_put_sync(dai->dev); + fsi_clk_ctrl(fsi, 0); + + ret = set_rate(fsi_is_port_a(fsi), params_rate(params)); + if (ret > 0) { + u32 data = 0; + + 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(dai->dev, "unsupported ACKMD\n"); + else + data |= (0x4 << 12); + break; + } + + 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(dai->dev, "unsupported ACKMD\n"); + else + data |= (0x7 << 8); + break; + } + + fsi_reg_mask_set(fsi, CKG1, (ACKMD_MASK | BPFMD_MASK) , data); + udelay(10); + fsi_clk_ctrl(fsi, 1); + ret = 0; + } + pm_runtime_get_sync(dai->dev); + + return ret; + +} + static struct snd_soc_dai_ops fsi_dai_ops = { .startup = fsi_dai_startup, .shutdown = fsi_dai_shutdown, .trigger = fsi_dai_trigger, + .hw_params = fsi_dai_hw_params, }; /************************************************************************ |