Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * IMG SPDIF output controller driver
0004  *
0005  * Copyright (C) 2015 Imagination Technologies Ltd.
0006  *
0007  * Author: Damien Horsley <Damien.Horsley@imgtec.com>
0008  */
0009 
0010 #include <linux/clk.h>
0011 #include <linux/init.h>
0012 #include <linux/kernel.h>
0013 #include <linux/module.h>
0014 #include <linux/of.h>
0015 #include <linux/platform_device.h>
0016 #include <linux/pm_runtime.h>
0017 #include <linux/reset.h>
0018 
0019 #include <sound/core.h>
0020 #include <sound/dmaengine_pcm.h>
0021 #include <sound/initval.h>
0022 #include <sound/pcm.h>
0023 #include <sound/pcm_params.h>
0024 #include <sound/soc.h>
0025 
0026 #define IMG_SPDIF_OUT_TX_FIFO       0x0
0027 
0028 #define IMG_SPDIF_OUT_CTL       0x4
0029 #define IMG_SPDIF_OUT_CTL_FS_MASK   BIT(4)
0030 #define IMG_SPDIF_OUT_CTL_CLK_MASK  BIT(2)
0031 #define IMG_SPDIF_OUT_CTL_SRT_MASK  BIT(0)
0032 
0033 #define IMG_SPDIF_OUT_CSL       0x14
0034 
0035 #define IMG_SPDIF_OUT_CSH_UV        0x18
0036 #define IMG_SPDIF_OUT_CSH_UV_CSH_SHIFT  0
0037 #define IMG_SPDIF_OUT_CSH_UV_CSH_MASK   0xff
0038 
0039 struct img_spdif_out {
0040     spinlock_t lock;
0041     void __iomem *base;
0042     struct clk *clk_sys;
0043     struct clk *clk_ref;
0044     struct snd_dmaengine_dai_dma_data dma_data;
0045     struct device *dev;
0046     struct reset_control *rst;
0047     u32 suspend_ctl;
0048     u32 suspend_csl;
0049     u32 suspend_csh;
0050 };
0051 
0052 static int img_spdif_out_runtime_suspend(struct device *dev)
0053 {
0054     struct img_spdif_out *spdif = dev_get_drvdata(dev);
0055 
0056     clk_disable_unprepare(spdif->clk_ref);
0057     clk_disable_unprepare(spdif->clk_sys);
0058 
0059     return 0;
0060 }
0061 
0062 static int img_spdif_out_runtime_resume(struct device *dev)
0063 {
0064     struct img_spdif_out *spdif = dev_get_drvdata(dev);
0065     int ret;
0066 
0067     ret = clk_prepare_enable(spdif->clk_sys);
0068     if (ret) {
0069         dev_err(dev, "clk_enable failed: %d\n", ret);
0070         return ret;
0071     }
0072 
0073     ret = clk_prepare_enable(spdif->clk_ref);
0074     if (ret) {
0075         dev_err(dev, "clk_enable failed: %d\n", ret);
0076         clk_disable_unprepare(spdif->clk_sys);
0077         return ret;
0078     }
0079 
0080     return 0;
0081 }
0082 
0083 static inline void img_spdif_out_writel(struct img_spdif_out *spdif, u32 val,
0084                 u32 reg)
0085 {
0086     writel(val, spdif->base + reg);
0087 }
0088 
0089 static inline u32 img_spdif_out_readl(struct img_spdif_out *spdif, u32 reg)
0090 {
0091     return readl(spdif->base + reg);
0092 }
0093 
0094 static void img_spdif_out_reset(struct img_spdif_out *spdif)
0095 {
0096     u32 ctl, status_low, status_high;
0097 
0098     ctl = img_spdif_out_readl(spdif, IMG_SPDIF_OUT_CTL) &
0099             ~IMG_SPDIF_OUT_CTL_SRT_MASK;
0100     status_low = img_spdif_out_readl(spdif, IMG_SPDIF_OUT_CSL);
0101     status_high = img_spdif_out_readl(spdif, IMG_SPDIF_OUT_CSH_UV);
0102 
0103     reset_control_assert(spdif->rst);
0104     reset_control_deassert(spdif->rst);
0105 
0106     img_spdif_out_writel(spdif, ctl, IMG_SPDIF_OUT_CTL);
0107     img_spdif_out_writel(spdif, status_low, IMG_SPDIF_OUT_CSL);
0108     img_spdif_out_writel(spdif, status_high, IMG_SPDIF_OUT_CSH_UV);
0109 }
0110 
0111 static int img_spdif_out_info(struct snd_kcontrol *kcontrol,
0112                     struct snd_ctl_elem_info *uinfo)
0113 {
0114     uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
0115     uinfo->count = 1;
0116 
0117     return 0;
0118 }
0119 
0120 static int img_spdif_out_get_status_mask(struct snd_kcontrol *kcontrol,
0121                        struct snd_ctl_elem_value *ucontrol)
0122 {
0123     ucontrol->value.iec958.status[0] = 0xff;
0124     ucontrol->value.iec958.status[1] = 0xff;
0125     ucontrol->value.iec958.status[2] = 0xff;
0126     ucontrol->value.iec958.status[3] = 0xff;
0127     ucontrol->value.iec958.status[4] = 0xff;
0128 
0129     return 0;
0130 }
0131 
0132 static int img_spdif_out_get_status(struct snd_kcontrol *kcontrol,
0133                   struct snd_ctl_elem_value *ucontrol)
0134 {
0135     struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
0136     struct img_spdif_out *spdif = snd_soc_dai_get_drvdata(cpu_dai);
0137     u32 reg;
0138     unsigned long flags;
0139 
0140     spin_lock_irqsave(&spdif->lock, flags);
0141 
0142     reg = img_spdif_out_readl(spdif, IMG_SPDIF_OUT_CSL);
0143     ucontrol->value.iec958.status[0] = reg & 0xff;
0144     ucontrol->value.iec958.status[1] = (reg >> 8) & 0xff;
0145     ucontrol->value.iec958.status[2] = (reg >> 16) & 0xff;
0146     ucontrol->value.iec958.status[3] = (reg >> 24) & 0xff;
0147 
0148     reg = img_spdif_out_readl(spdif, IMG_SPDIF_OUT_CSH_UV);
0149     ucontrol->value.iec958.status[4] =
0150         (reg & IMG_SPDIF_OUT_CSH_UV_CSH_MASK) >>
0151         IMG_SPDIF_OUT_CSH_UV_CSH_SHIFT;
0152 
0153     spin_unlock_irqrestore(&spdif->lock, flags);
0154 
0155     return 0;
0156 }
0157 
0158 static int img_spdif_out_set_status(struct snd_kcontrol *kcontrol,
0159                   struct snd_ctl_elem_value *ucontrol)
0160 {
0161     struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
0162     struct img_spdif_out *spdif = snd_soc_dai_get_drvdata(cpu_dai);
0163     u32 reg;
0164     unsigned long flags;
0165 
0166     reg = ((u32)ucontrol->value.iec958.status[3] << 24);
0167     reg |= ((u32)ucontrol->value.iec958.status[2] << 16);
0168     reg |= ((u32)ucontrol->value.iec958.status[1] << 8);
0169     reg |= (u32)ucontrol->value.iec958.status[0];
0170 
0171     spin_lock_irqsave(&spdif->lock, flags);
0172 
0173     img_spdif_out_writel(spdif, reg, IMG_SPDIF_OUT_CSL);
0174 
0175     reg = img_spdif_out_readl(spdif, IMG_SPDIF_OUT_CSH_UV);
0176     reg &= ~IMG_SPDIF_OUT_CSH_UV_CSH_MASK;
0177     reg |= (u32)ucontrol->value.iec958.status[4] <<
0178             IMG_SPDIF_OUT_CSH_UV_CSH_SHIFT;
0179     img_spdif_out_writel(spdif, reg, IMG_SPDIF_OUT_CSH_UV);
0180 
0181     spin_unlock_irqrestore(&spdif->lock, flags);
0182 
0183     return 0;
0184 }
0185 
0186 static struct snd_kcontrol_new img_spdif_out_controls[] = {
0187     {
0188         .access = SNDRV_CTL_ELEM_ACCESS_READ,
0189         .iface = SNDRV_CTL_ELEM_IFACE_PCM,
0190         .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, MASK),
0191         .info = img_spdif_out_info,
0192         .get = img_spdif_out_get_status_mask
0193     },
0194     {
0195         .iface = SNDRV_CTL_ELEM_IFACE_PCM,
0196         .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
0197         .info = img_spdif_out_info,
0198         .get = img_spdif_out_get_status,
0199         .put = img_spdif_out_set_status
0200     }
0201 };
0202 
0203 static int img_spdif_out_trigger(struct snd_pcm_substream *substream, int cmd,
0204             struct snd_soc_dai *dai)
0205 {
0206     struct img_spdif_out *spdif = snd_soc_dai_get_drvdata(dai);
0207     u32 reg;
0208     unsigned long flags;
0209 
0210     switch (cmd) {
0211     case SNDRV_PCM_TRIGGER_START:
0212     case SNDRV_PCM_TRIGGER_RESUME:
0213     case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
0214         reg = img_spdif_out_readl(spdif, IMG_SPDIF_OUT_CTL);
0215         reg |= IMG_SPDIF_OUT_CTL_SRT_MASK;
0216         img_spdif_out_writel(spdif, reg, IMG_SPDIF_OUT_CTL);
0217         break;
0218     case SNDRV_PCM_TRIGGER_STOP:
0219     case SNDRV_PCM_TRIGGER_SUSPEND:
0220     case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
0221         spin_lock_irqsave(&spdif->lock, flags);
0222         img_spdif_out_reset(spdif);
0223         spin_unlock_irqrestore(&spdif->lock, flags);
0224         break;
0225     default:
0226         return -EINVAL;
0227     }
0228 
0229     return 0;
0230 }
0231 
0232 static int img_spdif_out_hw_params(struct snd_pcm_substream *substream,
0233     struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
0234 {
0235     struct img_spdif_out *spdif = snd_soc_dai_get_drvdata(dai);
0236     unsigned int channels;
0237     long pre_div_a, pre_div_b, diff_a, diff_b, rate, clk_rate;
0238     u32 reg;
0239     snd_pcm_format_t format;
0240 
0241     rate = params_rate(params);
0242     format = params_format(params);
0243     channels = params_channels(params);
0244 
0245     dev_dbg(spdif->dev, "hw_params rate %ld channels %u format %u\n",
0246             rate, channels, format);
0247 
0248     if (format != SNDRV_PCM_FORMAT_S32_LE)
0249         return -EINVAL;
0250 
0251     if (channels != 2)
0252         return -EINVAL;
0253 
0254     pre_div_a = clk_round_rate(spdif->clk_ref, rate * 256);
0255     if (pre_div_a < 0)
0256         return pre_div_a;
0257     pre_div_b = clk_round_rate(spdif->clk_ref, rate * 384);
0258     if (pre_div_b < 0)
0259         return pre_div_b;
0260 
0261     diff_a = abs((pre_div_a / 256) - rate);
0262     diff_b = abs((pre_div_b / 384) - rate);
0263 
0264     /* If diffs are equal, use lower clock rate */
0265     if (diff_a > diff_b)
0266         clk_set_rate(spdif->clk_ref, pre_div_b);
0267     else
0268         clk_set_rate(spdif->clk_ref, pre_div_a);
0269 
0270     /*
0271      * Another driver (eg machine driver) may have rejected the above
0272      * change. Get the current rate and set the register bit according to
0273      * the new min diff
0274      */
0275     clk_rate = clk_get_rate(spdif->clk_ref);
0276 
0277     diff_a = abs((clk_rate / 256) - rate);
0278     diff_b = abs((clk_rate / 384) - rate);
0279 
0280     reg = img_spdif_out_readl(spdif, IMG_SPDIF_OUT_CTL);
0281     if (diff_a <= diff_b)
0282         reg &= ~IMG_SPDIF_OUT_CTL_CLK_MASK;
0283     else
0284         reg |= IMG_SPDIF_OUT_CTL_CLK_MASK;
0285     img_spdif_out_writel(spdif, reg, IMG_SPDIF_OUT_CTL);
0286 
0287     return 0;
0288 }
0289 
0290 static const struct snd_soc_dai_ops img_spdif_out_dai_ops = {
0291     .trigger = img_spdif_out_trigger,
0292     .hw_params = img_spdif_out_hw_params
0293 };
0294 
0295 static int img_spdif_out_dai_probe(struct snd_soc_dai *dai)
0296 {
0297     struct img_spdif_out *spdif = snd_soc_dai_get_drvdata(dai);
0298 
0299     snd_soc_dai_init_dma_data(dai, &spdif->dma_data, NULL);
0300 
0301     snd_soc_add_dai_controls(dai, img_spdif_out_controls,
0302             ARRAY_SIZE(img_spdif_out_controls));
0303 
0304     return 0;
0305 }
0306 
0307 static struct snd_soc_dai_driver img_spdif_out_dai = {
0308     .probe = img_spdif_out_dai_probe,
0309     .playback = {
0310         .channels_min = 2,
0311         .channels_max = 2,
0312         .rates = SNDRV_PCM_RATE_8000_192000,
0313         .formats = SNDRV_PCM_FMTBIT_S32_LE
0314     },
0315     .ops = &img_spdif_out_dai_ops
0316 };
0317 
0318 static const struct snd_soc_component_driver img_spdif_out_component = {
0319     .name = "img-spdif-out",
0320     .legacy_dai_naming = 1,
0321 };
0322 
0323 static int img_spdif_out_probe(struct platform_device *pdev)
0324 {
0325     struct img_spdif_out *spdif;
0326     struct resource *res;
0327     void __iomem *base;
0328     int ret;
0329     struct device *dev = &pdev->dev;
0330 
0331     spdif = devm_kzalloc(&pdev->dev, sizeof(*spdif), GFP_KERNEL);
0332     if (!spdif)
0333         return -ENOMEM;
0334 
0335     platform_set_drvdata(pdev, spdif);
0336 
0337     spdif->dev = &pdev->dev;
0338 
0339     base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
0340     if (IS_ERR(base))
0341         return PTR_ERR(base);
0342 
0343     spdif->base = base;
0344 
0345     spdif->rst = devm_reset_control_get_exclusive(&pdev->dev, "rst");
0346     if (IS_ERR(spdif->rst))
0347         return dev_err_probe(&pdev->dev, PTR_ERR(spdif->rst),
0348                      "No top level reset found\n");
0349 
0350     spdif->clk_sys = devm_clk_get(&pdev->dev, "sys");
0351     if (IS_ERR(spdif->clk_sys))
0352         return dev_err_probe(dev, PTR_ERR(spdif->clk_sys),
0353                      "Failed to acquire clock 'sys'\n");
0354 
0355     spdif->clk_ref = devm_clk_get(&pdev->dev, "ref");
0356     if (IS_ERR(spdif->clk_ref))
0357         return dev_err_probe(dev, PTR_ERR(spdif->clk_ref),
0358                      "Failed to acquire clock 'ref'\n");
0359 
0360     pm_runtime_enable(&pdev->dev);
0361     if (!pm_runtime_enabled(&pdev->dev)) {
0362         ret = img_spdif_out_runtime_resume(&pdev->dev);
0363         if (ret)
0364             goto err_pm_disable;
0365     }
0366     ret = pm_runtime_resume_and_get(&pdev->dev);
0367     if (ret < 0)
0368         goto err_suspend;
0369 
0370     img_spdif_out_writel(spdif, IMG_SPDIF_OUT_CTL_FS_MASK,
0371                  IMG_SPDIF_OUT_CTL);
0372 
0373     img_spdif_out_reset(spdif);
0374     pm_runtime_put(&pdev->dev);
0375 
0376     spin_lock_init(&spdif->lock);
0377 
0378     spdif->dma_data.addr = res->start + IMG_SPDIF_OUT_TX_FIFO;
0379     spdif->dma_data.addr_width = 4;
0380     spdif->dma_data.maxburst = 4;
0381 
0382     ret = devm_snd_soc_register_component(&pdev->dev,
0383             &img_spdif_out_component,
0384             &img_spdif_out_dai, 1);
0385     if (ret)
0386         goto err_suspend;
0387 
0388     ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
0389     if (ret)
0390         goto err_suspend;
0391 
0392     dev_dbg(&pdev->dev, "Probe successful\n");
0393 
0394     return 0;
0395 
0396 err_suspend:
0397     if (!pm_runtime_status_suspended(&pdev->dev))
0398         img_spdif_out_runtime_suspend(&pdev->dev);
0399 err_pm_disable:
0400     pm_runtime_disable(&pdev->dev);
0401 
0402     return ret;
0403 }
0404 
0405 static int img_spdif_out_dev_remove(struct platform_device *pdev)
0406 {
0407     pm_runtime_disable(&pdev->dev);
0408     if (!pm_runtime_status_suspended(&pdev->dev))
0409         img_spdif_out_runtime_suspend(&pdev->dev);
0410 
0411     return 0;
0412 }
0413 
0414 #ifdef CONFIG_PM_SLEEP
0415 static int img_spdif_out_suspend(struct device *dev)
0416 {
0417     struct img_spdif_out *spdif = dev_get_drvdata(dev);
0418     int ret;
0419 
0420     if (pm_runtime_status_suspended(dev)) {
0421         ret = img_spdif_out_runtime_resume(dev);
0422         if (ret)
0423             return ret;
0424     }
0425 
0426     spdif->suspend_ctl = img_spdif_out_readl(spdif, IMG_SPDIF_OUT_CTL);
0427     spdif->suspend_csl = img_spdif_out_readl(spdif, IMG_SPDIF_OUT_CSL);
0428     spdif->suspend_csh = img_spdif_out_readl(spdif, IMG_SPDIF_OUT_CSH_UV);
0429 
0430     img_spdif_out_runtime_suspend(dev);
0431 
0432     return 0;
0433 }
0434 
0435 static int img_spdif_out_resume(struct device *dev)
0436 {
0437     struct img_spdif_out *spdif = dev_get_drvdata(dev);
0438     int ret;
0439 
0440     ret = img_spdif_out_runtime_resume(dev);
0441     if (ret)
0442         return ret;
0443 
0444     img_spdif_out_writel(spdif, spdif->suspend_ctl, IMG_SPDIF_OUT_CTL);
0445     img_spdif_out_writel(spdif, spdif->suspend_csl, IMG_SPDIF_OUT_CSL);
0446     img_spdif_out_writel(spdif, spdif->suspend_csh, IMG_SPDIF_OUT_CSH_UV);
0447 
0448     if (pm_runtime_status_suspended(dev))
0449         img_spdif_out_runtime_suspend(dev);
0450 
0451     return 0;
0452 }
0453 #endif
0454 static const struct of_device_id img_spdif_out_of_match[] = {
0455     { .compatible = "img,spdif-out" },
0456     {}
0457 };
0458 MODULE_DEVICE_TABLE(of, img_spdif_out_of_match);
0459 
0460 static const struct dev_pm_ops img_spdif_out_pm_ops = {
0461     SET_RUNTIME_PM_OPS(img_spdif_out_runtime_suspend,
0462                img_spdif_out_runtime_resume, NULL)
0463     SET_SYSTEM_SLEEP_PM_OPS(img_spdif_out_suspend, img_spdif_out_resume)
0464 };
0465 
0466 static struct platform_driver img_spdif_out_driver = {
0467     .driver = {
0468         .name = "img-spdif-out",
0469         .of_match_table = img_spdif_out_of_match,
0470         .pm = &img_spdif_out_pm_ops
0471     },
0472     .probe = img_spdif_out_probe,
0473     .remove = img_spdif_out_dev_remove
0474 };
0475 module_platform_driver(img_spdif_out_driver);
0476 
0477 MODULE_AUTHOR("Damien Horsley <Damien.Horsley@imgtec.com>");
0478 MODULE_DESCRIPTION("IMG SPDIF Output driver");
0479 MODULE_LICENSE("GPL v2");