linux/sound/soc/zte/zx-tdm.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * ZTE's TDM driver
   4 *
   5 * Copyright (C) 2017 ZTE Ltd
   6 *
   7 * Author: Baoyou Xie <baoyou.xie@linaro.org>
   8 */
   9
  10#include <linux/clk.h>
  11#include <linux/io.h>
  12#include <linux/mfd/syscon.h>
  13#include <linux/module.h>
  14#include <sound/dmaengine_pcm.h>
  15#include <sound/pcm_params.h>
  16#include <sound/soc.h>
  17#include <sound/soc-dai.h>
  18
  19#define REG_TIMING_CTRL         0x04
  20#define REG_TX_FIFO_CTRL        0x0C
  21#define REG_RX_FIFO_CTRL        0x10
  22#define REG_INT_EN              0x1C
  23#define REG_INT_STATUS          0x20
  24#define REG_DATABUF             0x24
  25#define REG_TS_MASK0            0x44
  26#define REG_PROCESS_CTRL        0x54
  27
  28#define FIFO_CTRL_TX_RST        BIT(0)
  29#define FIFO_CTRL_RX_RST        BIT(0)
  30#define DEAGULT_FIFO_THRES      GENMASK(4, 2)
  31
  32#define FIFO_CTRL_TX_DMA_EN     BIT(1)
  33#define FIFO_CTRL_RX_DMA_EN     BIT(1)
  34
  35#define TX_FIFO_RST_MASK        BIT(0)
  36#define RX_FIFO_RST_MASK        BIT(0)
  37
  38#define FIFOCTRL_TX_FIFO_RST    BIT(0)
  39#define FIFOCTRL_RX_FIFO_RST    BIT(0)
  40
  41#define TXTH_MASK               GENMASK(5, 2)
  42#define RXTH_MASK               GENMASK(5, 2)
  43
  44#define FIFOCTRL_THRESHOLD(x)   ((x) << 2)
  45
  46#define TIMING_MS_MASK          BIT(1)
  47/*
  48 * 00: 8 clk cycles every timeslot
  49 * 01: 16 clk cycles every timeslot
  50 * 10: 32 clk cycles every timeslot
  51 */
  52#define TIMING_SYNC_WIDTH_MASK  GENMASK(6, 5)
  53#define TIMING_WIDTH_SHIFT      5
  54#define TIMING_DEFAULT_WIDTH    0
  55#define TIMING_TS_WIDTH(x)      ((x) << TIMING_WIDTH_SHIFT)
  56#define TIMING_WIDTH_FACTOR     8
  57
  58#define TIMING_MASTER_MODE      BIT(21)
  59#define TIMING_LSB_FIRST        BIT(20)
  60#define TIMING_TS_NUM(x)        (((x) - 1) << 7)
  61#define TIMING_CLK_SEL_MASK     GENMASK(2, 0)
  62#define TIMING_CLK_SEL_DEF      BIT(2)
  63
  64#define PROCESS_TX_EN           BIT(0)
  65#define PROCESS_RX_EN           BIT(1)
  66#define PROCESS_TDM_EN          BIT(2)
  67#define PROCESS_DISABLE_ALL     0
  68
  69#define INT_DISABLE_ALL         0
  70#define INT_STATUS_MASK         GENMASK(6, 0)
  71
  72struct zx_tdm_info {
  73        struct snd_dmaengine_dai_dma_data       dma_playback;
  74        struct snd_dmaengine_dai_dma_data       dma_capture;
  75        resource_size_t                         phy_addr;
  76        void __iomem                            *regbase;
  77        struct clk                              *dai_wclk;
  78        struct clk                              *dai_pclk;
  79        int                                     master;
  80        struct device                           *dev;
  81};
  82
  83static inline u32 zx_tdm_readl(struct zx_tdm_info *tdm, u16 reg)
  84{
  85        return readl_relaxed(tdm->regbase + reg);
  86}
  87
  88static inline void zx_tdm_writel(struct zx_tdm_info *tdm, u16 reg, u32 val)
  89{
  90        writel_relaxed(val, tdm->regbase + reg);
  91}
  92
  93static void zx_tdm_tx_en(struct zx_tdm_info *tdm, bool on)
  94{
  95        unsigned long val;
  96
  97        val = zx_tdm_readl(tdm, REG_PROCESS_CTRL);
  98        if (on)
  99                val |= PROCESS_TX_EN | PROCESS_TDM_EN;
 100        else
 101                val &= ~(PROCESS_TX_EN | PROCESS_TDM_EN);
 102        zx_tdm_writel(tdm, REG_PROCESS_CTRL, val);
 103}
 104
 105static void zx_tdm_rx_en(struct zx_tdm_info *tdm, bool on)
 106{
 107        unsigned long val;
 108
 109        val = zx_tdm_readl(tdm, REG_PROCESS_CTRL);
 110        if (on)
 111                val |= PROCESS_RX_EN | PROCESS_TDM_EN;
 112        else
 113                val &= ~(PROCESS_RX_EN | PROCESS_TDM_EN);
 114        zx_tdm_writel(tdm, REG_PROCESS_CTRL, val);
 115}
 116
 117static void zx_tdm_tx_dma_en(struct zx_tdm_info *tdm, bool on)
 118{
 119        unsigned long val;
 120
 121        val = zx_tdm_readl(tdm, REG_TX_FIFO_CTRL);
 122        val |= FIFO_CTRL_TX_RST | DEAGULT_FIFO_THRES;
 123        if (on)
 124                val |= FIFO_CTRL_TX_DMA_EN;
 125        else
 126                val &= ~FIFO_CTRL_TX_DMA_EN;
 127        zx_tdm_writel(tdm, REG_TX_FIFO_CTRL, val);
 128}
 129
 130static void zx_tdm_rx_dma_en(struct zx_tdm_info *tdm, bool on)
 131{
 132        unsigned long val;
 133
 134        val = zx_tdm_readl(tdm, REG_RX_FIFO_CTRL);
 135        val |= FIFO_CTRL_RX_RST | DEAGULT_FIFO_THRES;
 136        if (on)
 137                val |= FIFO_CTRL_RX_DMA_EN;
 138        else
 139                val &= ~FIFO_CTRL_RX_DMA_EN;
 140        zx_tdm_writel(tdm, REG_RX_FIFO_CTRL, val);
 141}
 142
 143#define ZX_TDM_RATES    (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000)
 144
 145#define ZX_TDM_FMTBIT \
 146        (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_MU_LAW | \
 147        SNDRV_PCM_FMTBIT_A_LAW)
 148
 149static int zx_tdm_dai_probe(struct snd_soc_dai *dai)
 150{
 151        struct zx_tdm_info *zx_tdm = dev_get_drvdata(dai->dev);
 152
 153        snd_soc_dai_set_drvdata(dai, zx_tdm);
 154        zx_tdm->dma_playback.addr = zx_tdm->phy_addr + REG_DATABUF;
 155        zx_tdm->dma_playback.maxburst = 16;
 156        zx_tdm->dma_capture.addr = zx_tdm->phy_addr + REG_DATABUF;
 157        zx_tdm->dma_capture.maxburst = 16;
 158        snd_soc_dai_init_dma_data(dai, &zx_tdm->dma_playback,
 159                                  &zx_tdm->dma_capture);
 160        return 0;
 161}
 162
 163static int zx_tdm_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
 164{
 165        struct zx_tdm_info *tdm = snd_soc_dai_get_drvdata(cpu_dai);
 166        unsigned long val;
 167
 168        val = zx_tdm_readl(tdm, REG_TIMING_CTRL);
 169        val &= ~(TIMING_SYNC_WIDTH_MASK | TIMING_MS_MASK);
 170        val |= TIMING_DEFAULT_WIDTH << TIMING_WIDTH_SHIFT;
 171
 172        switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
 173        case SND_SOC_DAIFMT_CBM_CFM:
 174                tdm->master = 1;
 175                val |= TIMING_MASTER_MODE;
 176                break;
 177        case SND_SOC_DAIFMT_CBS_CFS:
 178                tdm->master = 0;
 179                val &= ~TIMING_MASTER_MODE;
 180                break;
 181        default:
 182                dev_err(cpu_dai->dev, "Unknown master/slave format\n");
 183                return -EINVAL;
 184        }
 185
 186
 187        zx_tdm_writel(tdm, REG_TIMING_CTRL, val);
 188
 189        return 0;
 190}
 191
 192static int zx_tdm_hw_params(struct snd_pcm_substream *substream,
 193                            struct snd_pcm_hw_params *params,
 194                            struct snd_soc_dai *socdai)
 195{
 196        struct zx_tdm_info *tdm = snd_soc_dai_get_drvdata(socdai);
 197        struct snd_dmaengine_dai_dma_data *dma_data;
 198        unsigned int ts_width = TIMING_DEFAULT_WIDTH;
 199        unsigned int ch_num = 32;
 200        unsigned int mask = 0;
 201        unsigned int ret = 0;
 202        unsigned long val;
 203
 204        dma_data = snd_soc_dai_get_dma_data(socdai, substream);
 205        dma_data->addr_width = ch_num >> 3;
 206
 207        switch (params_format(params)) {
 208        case SNDRV_PCM_FORMAT_MU_LAW:
 209        case SNDRV_PCM_FORMAT_A_LAW:
 210        case SNDRV_PCM_FORMAT_S16_LE:
 211                ts_width = 1;
 212                break;
 213        default:
 214                ts_width = 0;
 215                dev_err(socdai->dev, "Unknown data format\n");
 216                return -EINVAL;
 217        }
 218
 219        val = zx_tdm_readl(tdm, REG_TIMING_CTRL);
 220        val |= TIMING_TS_WIDTH(ts_width) | TIMING_TS_NUM(1);
 221        zx_tdm_writel(tdm, REG_TIMING_CTRL, val);
 222        zx_tdm_writel(tdm, REG_TS_MASK0, mask);
 223
 224        if (tdm->master)
 225                ret = clk_set_rate(tdm->dai_wclk,
 226                        params_rate(params) * TIMING_WIDTH_FACTOR * ch_num);
 227
 228        return ret;
 229}
 230
 231static int zx_tdm_trigger(struct snd_pcm_substream *substream, int cmd,
 232                          struct snd_soc_dai *dai)
 233{
 234        int capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
 235        struct zx_tdm_info *zx_tdm = dev_get_drvdata(dai->dev);
 236        unsigned int val;
 237        int ret = 0;
 238
 239        switch (cmd) {
 240        case SNDRV_PCM_TRIGGER_START:
 241                if (capture) {
 242                        val = zx_tdm_readl(zx_tdm, REG_RX_FIFO_CTRL);
 243                        val |= FIFOCTRL_RX_FIFO_RST;
 244                        zx_tdm_writel(zx_tdm, REG_RX_FIFO_CTRL, val);
 245
 246                        zx_tdm_rx_dma_en(zx_tdm, true);
 247                } else {
 248                        val = zx_tdm_readl(zx_tdm, REG_TX_FIFO_CTRL);
 249                        val |= FIFOCTRL_TX_FIFO_RST;
 250                        zx_tdm_writel(zx_tdm, REG_TX_FIFO_CTRL, val);
 251
 252                        zx_tdm_tx_dma_en(zx_tdm, true);
 253                }
 254                break;
 255        case SNDRV_PCM_TRIGGER_RESUME:
 256        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 257                if (capture)
 258                        zx_tdm_rx_en(zx_tdm, true);
 259                else
 260                        zx_tdm_tx_en(zx_tdm, true);
 261                break;
 262        case SNDRV_PCM_TRIGGER_STOP:
 263                if (capture)
 264                        zx_tdm_rx_dma_en(zx_tdm, false);
 265                else
 266                        zx_tdm_tx_dma_en(zx_tdm, false);
 267                break;
 268        case SNDRV_PCM_TRIGGER_SUSPEND:
 269        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 270                if (capture)
 271                        zx_tdm_rx_en(zx_tdm, false);
 272                else
 273                        zx_tdm_tx_en(zx_tdm, false);
 274                break;
 275        default:
 276                ret = -EINVAL;
 277                break;
 278        }
 279
 280        return ret;
 281}
 282
 283static int zx_tdm_startup(struct snd_pcm_substream *substream,
 284                          struct snd_soc_dai *dai)
 285{
 286        struct zx_tdm_info *zx_tdm = dev_get_drvdata(dai->dev);
 287        int ret;
 288
 289        ret = clk_prepare_enable(zx_tdm->dai_wclk);
 290        if (ret)
 291                return ret;
 292
 293        ret = clk_prepare_enable(zx_tdm->dai_pclk);
 294        if (ret) {
 295                clk_disable_unprepare(zx_tdm->dai_wclk);
 296                return ret;
 297        }
 298
 299        return 0;
 300}
 301
 302static void zx_tdm_shutdown(struct snd_pcm_substream *substream,
 303                            struct snd_soc_dai *dai)
 304{
 305        struct zx_tdm_info *zx_tdm = dev_get_drvdata(dai->dev);
 306
 307        clk_disable_unprepare(zx_tdm->dai_pclk);
 308        clk_disable_unprepare(zx_tdm->dai_wclk);
 309}
 310
 311static const struct snd_soc_dai_ops zx_tdm_dai_ops = {
 312        .trigger        = zx_tdm_trigger,
 313        .hw_params      = zx_tdm_hw_params,
 314        .set_fmt        = zx_tdm_set_fmt,
 315        .startup        = zx_tdm_startup,
 316        .shutdown       = zx_tdm_shutdown,
 317};
 318
 319static const struct snd_soc_component_driver zx_tdm_component = {
 320        .name                   = "zx-tdm",
 321};
 322
 323static void zx_tdm_init_state(struct zx_tdm_info *tdm)
 324{
 325        unsigned int val;
 326
 327        zx_tdm_writel(tdm, REG_PROCESS_CTRL, PROCESS_DISABLE_ALL);
 328
 329        val = zx_tdm_readl(tdm, REG_TIMING_CTRL);
 330        val |= TIMING_LSB_FIRST;
 331        val &= ~TIMING_CLK_SEL_MASK;
 332        val |= TIMING_CLK_SEL_DEF;
 333        zx_tdm_writel(tdm, REG_TIMING_CTRL, val);
 334
 335        zx_tdm_writel(tdm, REG_INT_EN, INT_DISABLE_ALL);
 336        /*
 337         * write INT_STATUS register to clear it.
 338         */
 339        zx_tdm_writel(tdm, REG_INT_STATUS, INT_STATUS_MASK);
 340        zx_tdm_writel(tdm, REG_RX_FIFO_CTRL, FIFOCTRL_RX_FIFO_RST);
 341        zx_tdm_writel(tdm, REG_TX_FIFO_CTRL, FIFOCTRL_TX_FIFO_RST);
 342
 343        val = zx_tdm_readl(tdm, REG_RX_FIFO_CTRL);
 344        val &= ~(RXTH_MASK | RX_FIFO_RST_MASK);
 345        val |= FIFOCTRL_THRESHOLD(8);
 346        zx_tdm_writel(tdm, REG_RX_FIFO_CTRL, val);
 347
 348        val = zx_tdm_readl(tdm, REG_TX_FIFO_CTRL);
 349        val &= ~(TXTH_MASK | TX_FIFO_RST_MASK);
 350        val |= FIFOCTRL_THRESHOLD(8);
 351        zx_tdm_writel(tdm, REG_TX_FIFO_CTRL, val);
 352}
 353
 354static struct snd_soc_dai_driver zx_tdm_dai = {
 355        .name   = "zx-tdm-dai",
 356        .id     = 0,
 357        .probe  = zx_tdm_dai_probe,
 358        .playback   = {
 359                .channels_min   = 1,
 360                .channels_max   = 4,
 361                .rates          = ZX_TDM_RATES,
 362                .formats        = ZX_TDM_FMTBIT,
 363        },
 364        .capture = {
 365                .channels_min   = 1,
 366                .channels_max   = 4,
 367                .rates          = ZX_TDM_RATES,
 368                .formats        = ZX_TDM_FMTBIT,
 369        },
 370        .ops    = &zx_tdm_dai_ops,
 371};
 372
 373static int zx_tdm_probe(struct platform_device *pdev)
 374{
 375        struct device *dev = &pdev->dev;
 376        struct of_phandle_args out_args;
 377        unsigned int dma_reg_offset;
 378        struct zx_tdm_info *zx_tdm;
 379        unsigned int dma_mask;
 380        struct resource *res;
 381        struct regmap *regmap_sysctrl;
 382        int ret;
 383
 384        zx_tdm = devm_kzalloc(&pdev->dev, sizeof(*zx_tdm), GFP_KERNEL);
 385        if (!zx_tdm)
 386                return -ENOMEM;
 387
 388        zx_tdm->dev = dev;
 389
 390        zx_tdm->dai_wclk = devm_clk_get(&pdev->dev, "wclk");
 391        if (IS_ERR(zx_tdm->dai_wclk)) {
 392                dev_err(&pdev->dev, "Fail to get wclk\n");
 393                return PTR_ERR(zx_tdm->dai_wclk);
 394        }
 395
 396        zx_tdm->dai_pclk = devm_clk_get(&pdev->dev, "pclk");
 397        if (IS_ERR(zx_tdm->dai_pclk)) {
 398                dev_err(&pdev->dev, "Fail to get pclk\n");
 399                return PTR_ERR(zx_tdm->dai_pclk);
 400        }
 401
 402        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 403        zx_tdm->phy_addr = res->start;
 404        zx_tdm->regbase = devm_ioremap_resource(&pdev->dev, res);
 405        if (IS_ERR(zx_tdm->regbase))
 406                return PTR_ERR(zx_tdm->regbase);
 407
 408        ret = of_parse_phandle_with_fixed_args(pdev->dev.of_node,
 409                                "zte,tdm-dma-sysctrl", 2, 0, &out_args);
 410        if (ret) {
 411                dev_err(&pdev->dev, "Fail to get zte,tdm-dma-sysctrl\n");
 412                return ret;
 413        }
 414
 415        dma_reg_offset = out_args.args[0];
 416        dma_mask = out_args.args[1];
 417        regmap_sysctrl = syscon_node_to_regmap(out_args.np);
 418        if (IS_ERR(regmap_sysctrl)) {
 419                of_node_put(out_args.np);
 420                return PTR_ERR(regmap_sysctrl);
 421        }
 422
 423        regmap_update_bits(regmap_sysctrl, dma_reg_offset, dma_mask, dma_mask);
 424        of_node_put(out_args.np);
 425
 426        zx_tdm_init_state(zx_tdm);
 427        platform_set_drvdata(pdev, zx_tdm);
 428
 429        ret = devm_snd_soc_register_component(&pdev->dev, &zx_tdm_component,
 430                                                &zx_tdm_dai, 1);
 431        if (ret) {
 432                dev_err(&pdev->dev, "Register DAI failed: %d\n", ret);
 433                return ret;
 434        }
 435
 436        ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
 437        if (ret)
 438                dev_err(&pdev->dev, "Register platform PCM failed: %d\n", ret);
 439
 440        return ret;
 441}
 442
 443static const struct of_device_id zx_tdm_dt_ids[] = {
 444        { .compatible = "zte,zx296718-tdm", },
 445        {}
 446};
 447MODULE_DEVICE_TABLE(of, zx_tdm_dt_ids);
 448
 449static struct platform_driver tdm_driver = {
 450        .probe = zx_tdm_probe,
 451        .driver = {
 452                .name = "zx-tdm",
 453                .of_match_table = zx_tdm_dt_ids,
 454        },
 455};
 456module_platform_driver(tdm_driver);
 457
 458MODULE_AUTHOR("Baoyou Xie <baoyou.xie@linaro.org>");
 459MODULE_DESCRIPTION("ZTE TDM DAI driver");
 460MODULE_LICENSE("GPL v2");
 461