root/sound/soc/img/img-parallel-out.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. img_prl_out_suspend
  2. img_prl_out_resume
  3. img_prl_out_writel
  4. img_prl_out_readl
  5. img_prl_out_reset
  6. img_prl_out_trigger
  7. img_prl_out_hw_params
  8. img_prl_out_set_fmt
  9. img_prl_out_dai_probe
  10. img_prl_out_probe
  11. img_prl_out_dev_remove

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /*
   3  * IMG parallel output controller driver
   4  *
   5  * Copyright (C) 2015 Imagination Technologies Ltd.
   6  *
   7  * Author: Damien Horsley <Damien.Horsley@imgtec.com>
   8  */
   9 
  10 #include <linux/clk.h>
  11 #include <linux/init.h>
  12 #include <linux/kernel.h>
  13 #include <linux/module.h>
  14 #include <linux/of.h>
  15 #include <linux/platform_device.h>
  16 #include <linux/pm_runtime.h>
  17 #include <linux/reset.h>
  18 
  19 #include <sound/core.h>
  20 #include <sound/dmaengine_pcm.h>
  21 #include <sound/initval.h>
  22 #include <sound/pcm.h>
  23 #include <sound/pcm_params.h>
  24 #include <sound/soc.h>
  25 
  26 #define IMG_PRL_OUT_TX_FIFO             0
  27 
  28 #define IMG_PRL_OUT_CTL                 0x4
  29 #define IMG_PRL_OUT_CTL_CH_MASK         BIT(4)
  30 #define IMG_PRL_OUT_CTL_PACKH_MASK      BIT(3)
  31 #define IMG_PRL_OUT_CTL_EDGE_MASK       BIT(2)
  32 #define IMG_PRL_OUT_CTL_ME_MASK         BIT(1)
  33 #define IMG_PRL_OUT_CTL_SRST_MASK       BIT(0)
  34 
  35 struct img_prl_out {
  36         void __iomem *base;
  37         struct clk *clk_sys;
  38         struct clk *clk_ref;
  39         struct snd_dmaengine_dai_dma_data dma_data;
  40         struct device *dev;
  41         struct reset_control *rst;
  42 };
  43 
  44 static int img_prl_out_suspend(struct device *dev)
  45 {
  46         struct img_prl_out *prl = dev_get_drvdata(dev);
  47 
  48         clk_disable_unprepare(prl->clk_ref);
  49 
  50         return 0;
  51 }
  52 
  53 static int img_prl_out_resume(struct device *dev)
  54 {
  55         struct img_prl_out *prl = dev_get_drvdata(dev);
  56         int ret;
  57 
  58         ret = clk_prepare_enable(prl->clk_ref);
  59         if (ret) {
  60                 dev_err(dev, "clk_enable failed: %d\n", ret);
  61                 return ret;
  62         }
  63 
  64         return 0;
  65 }
  66 
  67 static inline void img_prl_out_writel(struct img_prl_out *prl,
  68                                 u32 val, u32 reg)
  69 {
  70         writel(val, prl->base + reg);
  71 }
  72 
  73 static inline u32 img_prl_out_readl(struct img_prl_out *prl, u32 reg)
  74 {
  75         return readl(prl->base + reg);
  76 }
  77 
  78 static void img_prl_out_reset(struct img_prl_out *prl)
  79 {
  80         u32 ctl;
  81 
  82         ctl = img_prl_out_readl(prl, IMG_PRL_OUT_CTL) &
  83                         ~IMG_PRL_OUT_CTL_ME_MASK;
  84 
  85         reset_control_assert(prl->rst);
  86         reset_control_deassert(prl->rst);
  87 
  88         img_prl_out_writel(prl, ctl, IMG_PRL_OUT_CTL);
  89 }
  90 
  91 static int img_prl_out_trigger(struct snd_pcm_substream *substream, int cmd,
  92                         struct snd_soc_dai *dai)
  93 {
  94         struct img_prl_out *prl = snd_soc_dai_get_drvdata(dai);
  95         u32 reg;
  96 
  97         switch (cmd) {
  98         case SNDRV_PCM_TRIGGER_START:
  99         case SNDRV_PCM_TRIGGER_RESUME:
 100         case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 101                 reg = img_prl_out_readl(prl, IMG_PRL_OUT_CTL);
 102                 reg |= IMG_PRL_OUT_CTL_ME_MASK;
 103                 img_prl_out_writel(prl, reg, IMG_PRL_OUT_CTL);
 104                 break;
 105         case SNDRV_PCM_TRIGGER_STOP:
 106         case SNDRV_PCM_TRIGGER_SUSPEND:
 107         case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 108                 img_prl_out_reset(prl);
 109                 break;
 110         default:
 111                 return -EINVAL;
 112         }
 113 
 114         return 0;
 115 }
 116 
 117 static int img_prl_out_hw_params(struct snd_pcm_substream *substream,
 118         struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
 119 {
 120         struct img_prl_out *prl = snd_soc_dai_get_drvdata(dai);
 121         unsigned int rate, channels;
 122         u32 reg, control_set = 0;
 123 
 124         rate = params_rate(params);
 125         channels = params_channels(params);
 126 
 127         switch (params_format(params)) {
 128         case SNDRV_PCM_FORMAT_S32_LE:
 129                 control_set |= IMG_PRL_OUT_CTL_PACKH_MASK;
 130                 break;
 131         case SNDRV_PCM_FORMAT_S24_LE:
 132                 break;
 133         default:
 134                 return -EINVAL;
 135         }
 136 
 137         if (channels != 2)
 138                 return -EINVAL;
 139 
 140         clk_set_rate(prl->clk_ref, rate * 256);
 141 
 142         reg = img_prl_out_readl(prl, IMG_PRL_OUT_CTL);
 143         reg = (reg & ~IMG_PRL_OUT_CTL_PACKH_MASK) | control_set;
 144         img_prl_out_writel(prl, reg, IMG_PRL_OUT_CTL);
 145 
 146         return 0;
 147 }
 148 
 149 static int img_prl_out_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
 150 {
 151         struct img_prl_out *prl = snd_soc_dai_get_drvdata(dai);
 152         u32 reg, control_set = 0;
 153         int ret;
 154 
 155         switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
 156         case SND_SOC_DAIFMT_NB_NF:
 157                 break;
 158         case SND_SOC_DAIFMT_NB_IF:
 159                 control_set |= IMG_PRL_OUT_CTL_EDGE_MASK;
 160                 break;
 161         default:
 162                 return -EINVAL;
 163         }
 164 
 165         ret = pm_runtime_get_sync(prl->dev);
 166         if (ret < 0)
 167                 return ret;
 168 
 169         reg = img_prl_out_readl(prl, IMG_PRL_OUT_CTL);
 170         reg = (reg & ~IMG_PRL_OUT_CTL_EDGE_MASK) | control_set;
 171         img_prl_out_writel(prl, reg, IMG_PRL_OUT_CTL);
 172         pm_runtime_put(prl->dev);
 173 
 174         return 0;
 175 }
 176 
 177 static const struct snd_soc_dai_ops img_prl_out_dai_ops = {
 178         .trigger = img_prl_out_trigger,
 179         .hw_params = img_prl_out_hw_params,
 180         .set_fmt = img_prl_out_set_fmt
 181 };
 182 
 183 static int img_prl_out_dai_probe(struct snd_soc_dai *dai)
 184 {
 185         struct img_prl_out *prl = snd_soc_dai_get_drvdata(dai);
 186 
 187         snd_soc_dai_init_dma_data(dai, &prl->dma_data, NULL);
 188 
 189         return 0;
 190 }
 191 
 192 static struct snd_soc_dai_driver img_prl_out_dai = {
 193         .probe = img_prl_out_dai_probe,
 194         .playback = {
 195                 .channels_min = 2,
 196                 .channels_max = 2,
 197                 .rates = SNDRV_PCM_RATE_8000_192000,
 198                 .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S24_LE
 199         },
 200         .ops = &img_prl_out_dai_ops
 201 };
 202 
 203 static const struct snd_soc_component_driver img_prl_out_component = {
 204         .name = "img-prl-out"
 205 };
 206 
 207 static int img_prl_out_probe(struct platform_device *pdev)
 208 {
 209         struct img_prl_out *prl;
 210         struct resource *res;
 211         void __iomem *base;
 212         int ret;
 213         struct device *dev = &pdev->dev;
 214 
 215         prl = devm_kzalloc(&pdev->dev, sizeof(*prl), GFP_KERNEL);
 216         if (!prl)
 217                 return -ENOMEM;
 218 
 219         platform_set_drvdata(pdev, prl);
 220 
 221         prl->dev = &pdev->dev;
 222 
 223         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 224         base = devm_ioremap_resource(&pdev->dev, res);
 225         if (IS_ERR(base))
 226                 return PTR_ERR(base);
 227 
 228         prl->base = base;
 229 
 230         prl->rst = devm_reset_control_get_exclusive(&pdev->dev, "rst");
 231         if (IS_ERR(prl->rst)) {
 232                 if (PTR_ERR(prl->rst) != -EPROBE_DEFER)
 233                         dev_err(&pdev->dev, "No top level reset found\n");
 234                 return PTR_ERR(prl->rst);
 235         }
 236 
 237         prl->clk_sys = devm_clk_get(&pdev->dev, "sys");
 238         if (IS_ERR(prl->clk_sys)) {
 239                 if (PTR_ERR(prl->clk_sys) != -EPROBE_DEFER)
 240                         dev_err(dev, "Failed to acquire clock 'sys'\n");
 241                 return PTR_ERR(prl->clk_sys);
 242         }
 243 
 244         prl->clk_ref = devm_clk_get(&pdev->dev, "ref");
 245         if (IS_ERR(prl->clk_ref)) {
 246                 if (PTR_ERR(prl->clk_ref) != -EPROBE_DEFER)
 247                         dev_err(dev, "Failed to acquire clock 'ref'\n");
 248                 return PTR_ERR(prl->clk_ref);
 249         }
 250 
 251         ret = clk_prepare_enable(prl->clk_sys);
 252         if (ret)
 253                 return ret;
 254 
 255         img_prl_out_writel(prl, IMG_PRL_OUT_CTL_EDGE_MASK, IMG_PRL_OUT_CTL);
 256         img_prl_out_reset(prl);
 257 
 258         pm_runtime_enable(&pdev->dev);
 259         if (!pm_runtime_enabled(&pdev->dev)) {
 260                 ret = img_prl_out_resume(&pdev->dev);
 261                 if (ret)
 262                         goto err_pm_disable;
 263         }
 264 
 265         prl->dma_data.addr = res->start + IMG_PRL_OUT_TX_FIFO;
 266         prl->dma_data.addr_width = 4;
 267         prl->dma_data.maxburst = 4;
 268 
 269         ret = devm_snd_soc_register_component(&pdev->dev,
 270                         &img_prl_out_component,
 271                         &img_prl_out_dai, 1);
 272         if (ret)
 273                 goto err_suspend;
 274 
 275         ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
 276         if (ret)
 277                 goto err_suspend;
 278 
 279         return 0;
 280 
 281 err_suspend:
 282         if (!pm_runtime_status_suspended(&pdev->dev))
 283                 img_prl_out_suspend(&pdev->dev);
 284 err_pm_disable:
 285         pm_runtime_disable(&pdev->dev);
 286         clk_disable_unprepare(prl->clk_sys);
 287 
 288         return ret;
 289 }
 290 
 291 static int img_prl_out_dev_remove(struct platform_device *pdev)
 292 {
 293         struct img_prl_out *prl = platform_get_drvdata(pdev);
 294 
 295         pm_runtime_disable(&pdev->dev);
 296         if (!pm_runtime_status_suspended(&pdev->dev))
 297                 img_prl_out_suspend(&pdev->dev);
 298 
 299         clk_disable_unprepare(prl->clk_sys);
 300 
 301         return 0;
 302 }
 303 
 304 static const struct of_device_id img_prl_out_of_match[] = {
 305         { .compatible = "img,parallel-out" },
 306         {}
 307 };
 308 MODULE_DEVICE_TABLE(of, img_prl_out_of_match);
 309 
 310 static const struct dev_pm_ops img_prl_out_pm_ops = {
 311         SET_RUNTIME_PM_OPS(img_prl_out_suspend,
 312                            img_prl_out_resume, NULL)
 313 };
 314 
 315 static struct platform_driver img_prl_out_driver = {
 316         .driver = {
 317                 .name = "img-parallel-out",
 318                 .of_match_table = img_prl_out_of_match,
 319                 .pm = &img_prl_out_pm_ops
 320         },
 321         .probe = img_prl_out_probe,
 322         .remove = img_prl_out_dev_remove
 323 };
 324 module_platform_driver(img_prl_out_driver);
 325 
 326 MODULE_AUTHOR("Damien Horsley <Damien.Horsley@imgtec.com>");
 327 MODULE_DESCRIPTION("IMG Parallel Output Driver");
 328 MODULE_LICENSE("GPL v2");

/* [<][>][^][v][top][bottom][index][help] */