Merge branch 'omap-fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[cascardo/linux.git] / sound / soc / sh / fsi.c
index 2b06402..0c9997e 100644 (file)
@@ -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 */
 
 #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,
 };