linux/sound/soc/jz4740/jz4740-i2s.c
<<
>>
Prefs
   1/*
   2 *  Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de>
   3 *
   4 *  This program is free software; you can redistribute it and/or modify it
   5 *  under  the terms of the GNU General  Public License as published by the
   6 *  Free Software Foundation;  either version 2 of the License, or (at your
   7 *  option) any later version.
   8 *
   9 *  You should have received a copy of the GNU General Public License along
  10 *  with this program; if not, write to the Free Software Foundation, Inc.,
  11 *  675 Mass Ave, Cambridge, MA 02139, USA.
  12 *
  13 */
  14
  15#include <linux/init.h>
  16#include <linux/io.h>
  17#include <linux/kernel.h>
  18#include <linux/module.h>
  19#include <linux/platform_device.h>
  20#include <linux/slab.h>
  21
  22#include <linux/clk.h>
  23#include <linux/delay.h>
  24
  25#include <linux/dma-mapping.h>
  26
  27#include <sound/core.h>
  28#include <sound/pcm.h>
  29#include <sound/pcm_params.h>
  30#include <sound/soc.h>
  31#include <sound/initval.h>
  32
  33#include "jz4740-i2s.h"
  34#include "jz4740-pcm.h"
  35
  36#define JZ_REG_AIC_CONF         0x00
  37#define JZ_REG_AIC_CTRL         0x04
  38#define JZ_REG_AIC_I2S_FMT      0x10
  39#define JZ_REG_AIC_FIFO_STATUS  0x14
  40#define JZ_REG_AIC_I2S_STATUS   0x1c
  41#define JZ_REG_AIC_CLK_DIV      0x30
  42#define JZ_REG_AIC_FIFO         0x34
  43
  44#define JZ_AIC_CONF_FIFO_RX_THRESHOLD_MASK (0xf << 12)
  45#define JZ_AIC_CONF_FIFO_TX_THRESHOLD_MASK (0xf <<  8)
  46#define JZ_AIC_CONF_OVERFLOW_PLAY_LAST BIT(6)
  47#define JZ_AIC_CONF_INTERNAL_CODEC BIT(5)
  48#define JZ_AIC_CONF_I2S BIT(4)
  49#define JZ_AIC_CONF_RESET BIT(3)
  50#define JZ_AIC_CONF_BIT_CLK_MASTER BIT(2)
  51#define JZ_AIC_CONF_SYNC_CLK_MASTER BIT(1)
  52#define JZ_AIC_CONF_ENABLE BIT(0)
  53
  54#define JZ_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET 12
  55#define JZ_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET 8
  56
  57#define JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE_MASK (0x7 << 19)
  58#define JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_MASK (0x7 << 16)
  59#define JZ_AIC_CTRL_ENABLE_RX_DMA BIT(15)
  60#define JZ_AIC_CTRL_ENABLE_TX_DMA BIT(14)
  61#define JZ_AIC_CTRL_MONO_TO_STEREO BIT(11)
  62#define JZ_AIC_CTRL_SWITCH_ENDIANNESS BIT(10)
  63#define JZ_AIC_CTRL_SIGNED_TO_UNSIGNED BIT(9)
  64#define JZ_AIC_CTRL_FLUSH               BIT(8)
  65#define JZ_AIC_CTRL_ENABLE_ROR_INT BIT(6)
  66#define JZ_AIC_CTRL_ENABLE_TUR_INT BIT(5)
  67#define JZ_AIC_CTRL_ENABLE_RFS_INT BIT(4)
  68#define JZ_AIC_CTRL_ENABLE_TFS_INT BIT(3)
  69#define JZ_AIC_CTRL_ENABLE_LOOPBACK BIT(2)
  70#define JZ_AIC_CTRL_ENABLE_PLAYBACK BIT(1)
  71#define JZ_AIC_CTRL_ENABLE_CAPTURE BIT(0)
  72
  73#define JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE_OFFSET 19
  74#define JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_OFFSET  16
  75
  76#define JZ_AIC_I2S_FMT_DISABLE_BIT_CLK BIT(12)
  77#define JZ_AIC_I2S_FMT_ENABLE_SYS_CLK BIT(4)
  78#define JZ_AIC_I2S_FMT_MSB BIT(0)
  79
  80#define JZ_AIC_I2S_STATUS_BUSY BIT(2)
  81
  82#define JZ_AIC_CLK_DIV_MASK 0xf
  83
  84struct jz4740_i2s {
  85        struct resource *mem;
  86        void __iomem *base;
  87        dma_addr_t phys_base;
  88
  89        struct clk *clk_aic;
  90        struct clk *clk_i2s;
  91
  92        struct jz4740_pcm_config pcm_config_playback;
  93        struct jz4740_pcm_config pcm_config_capture;
  94};
  95
  96static inline uint32_t jz4740_i2s_read(const struct jz4740_i2s *i2s,
  97        unsigned int reg)
  98{
  99        return readl(i2s->base + reg);
 100}
 101
 102static inline void jz4740_i2s_write(const struct jz4740_i2s *i2s,
 103        unsigned int reg, uint32_t value)
 104{
 105        writel(value, i2s->base + reg);
 106}
 107
 108static int jz4740_i2s_startup(struct snd_pcm_substream *substream,
 109        struct snd_soc_dai *dai)
 110{
 111        struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai);
 112        uint32_t conf, ctrl;
 113
 114        if (dai->active)
 115                return 0;
 116
 117        ctrl = jz4740_i2s_read(i2s, JZ_REG_AIC_CTRL);
 118        ctrl |= JZ_AIC_CTRL_FLUSH;
 119        jz4740_i2s_write(i2s, JZ_REG_AIC_CTRL, ctrl);
 120
 121        clk_enable(i2s->clk_i2s);
 122
 123        conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF);
 124        conf |= JZ_AIC_CONF_ENABLE;
 125        jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
 126
 127        return 0;
 128}
 129
 130static void jz4740_i2s_shutdown(struct snd_pcm_substream *substream,
 131        struct snd_soc_dai *dai)
 132{
 133        struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai);
 134        uint32_t conf;
 135
 136        if (dai->active)
 137                return;
 138
 139        conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF);
 140        conf &= ~JZ_AIC_CONF_ENABLE;
 141        jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
 142
 143        clk_disable(i2s->clk_i2s);
 144}
 145
 146static int jz4740_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
 147        struct snd_soc_dai *dai)
 148{
 149        struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai);
 150
 151        uint32_t ctrl;
 152        uint32_t mask;
 153
 154        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 155                mask = JZ_AIC_CTRL_ENABLE_PLAYBACK | JZ_AIC_CTRL_ENABLE_TX_DMA;
 156        else
 157                mask = JZ_AIC_CTRL_ENABLE_CAPTURE | JZ_AIC_CTRL_ENABLE_RX_DMA;
 158
 159        ctrl = jz4740_i2s_read(i2s, JZ_REG_AIC_CTRL);
 160
 161        switch (cmd) {
 162        case SNDRV_PCM_TRIGGER_START:
 163        case SNDRV_PCM_TRIGGER_RESUME:
 164        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 165                ctrl |= mask;
 166                break;
 167        case SNDRV_PCM_TRIGGER_STOP:
 168        case SNDRV_PCM_TRIGGER_SUSPEND:
 169        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 170                ctrl &= ~mask;
 171                break;
 172        default:
 173                return -EINVAL;
 174        }
 175
 176        jz4740_i2s_write(i2s, JZ_REG_AIC_CTRL, ctrl);
 177
 178        return 0;
 179}
 180
 181static int jz4740_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
 182{
 183        struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai);
 184
 185        uint32_t format = 0;
 186        uint32_t conf;
 187
 188        conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF);
 189
 190        conf &= ~(JZ_AIC_CONF_BIT_CLK_MASTER | JZ_AIC_CONF_SYNC_CLK_MASTER);
 191
 192        switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
 193        case SND_SOC_DAIFMT_CBS_CFS:
 194                conf |= JZ_AIC_CONF_BIT_CLK_MASTER | JZ_AIC_CONF_SYNC_CLK_MASTER;
 195                format |= JZ_AIC_I2S_FMT_ENABLE_SYS_CLK;
 196                break;
 197        case SND_SOC_DAIFMT_CBM_CFS:
 198                conf |= JZ_AIC_CONF_SYNC_CLK_MASTER;
 199                break;
 200        case SND_SOC_DAIFMT_CBS_CFM:
 201                conf |= JZ_AIC_CONF_BIT_CLK_MASTER;
 202                break;
 203        case SND_SOC_DAIFMT_CBM_CFM:
 204                break;
 205        default:
 206                return -EINVAL;
 207        }
 208
 209        switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
 210        case SND_SOC_DAIFMT_MSB:
 211                format |= JZ_AIC_I2S_FMT_MSB;
 212                break;
 213        case SND_SOC_DAIFMT_I2S:
 214                break;
 215        default:
 216                return -EINVAL;
 217        }
 218
 219        switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
 220        case SND_SOC_DAIFMT_NB_NF:
 221                break;
 222        default:
 223                return -EINVAL;
 224        }
 225
 226        jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
 227        jz4740_i2s_write(i2s, JZ_REG_AIC_I2S_FMT, format);
 228
 229        return 0;
 230}
 231
 232static int jz4740_i2s_hw_params(struct snd_pcm_substream *substream,
 233        struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
 234{
 235        struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai);
 236        enum jz4740_dma_width dma_width;
 237        struct jz4740_pcm_config *pcm_config;
 238        unsigned int sample_size;
 239        uint32_t ctrl;
 240
 241        ctrl = jz4740_i2s_read(i2s, JZ_REG_AIC_CTRL);
 242
 243        switch (params_format(params)) {
 244        case SNDRV_PCM_FORMAT_S8:
 245                sample_size = 0;
 246                dma_width = JZ4740_DMA_WIDTH_8BIT;
 247                break;
 248        case SNDRV_PCM_FORMAT_S16:
 249                sample_size = 1;
 250                dma_width = JZ4740_DMA_WIDTH_16BIT;
 251                break;
 252        default:
 253                return -EINVAL;
 254        }
 255
 256        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 257                ctrl &= ~JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE_MASK;
 258                ctrl |= sample_size << JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE_OFFSET;
 259                if (params_channels(params) == 1)
 260                        ctrl |= JZ_AIC_CTRL_MONO_TO_STEREO;
 261                else
 262                        ctrl &= ~JZ_AIC_CTRL_MONO_TO_STEREO;
 263
 264                pcm_config = &i2s->pcm_config_playback;
 265                pcm_config->dma_config.dst_width = dma_width;
 266
 267        } else {
 268                ctrl &= ~JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_MASK;
 269                ctrl |= sample_size << JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_OFFSET;
 270
 271                pcm_config = &i2s->pcm_config_capture;
 272                pcm_config->dma_config.src_width = dma_width;
 273        }
 274
 275        jz4740_i2s_write(i2s, JZ_REG_AIC_CTRL, ctrl);
 276
 277        snd_soc_dai_set_dma_data(dai, substream, pcm_config);
 278
 279        return 0;
 280}
 281
 282static int jz4740_i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id,
 283        unsigned int freq, int dir)
 284{
 285        struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai);
 286        struct clk *parent;
 287        int ret = 0;
 288
 289        switch (clk_id) {
 290        case JZ4740_I2S_CLKSRC_EXT:
 291                parent = clk_get(NULL, "ext");
 292                clk_set_parent(i2s->clk_i2s, parent);
 293                break;
 294        case JZ4740_I2S_CLKSRC_PLL:
 295                parent = clk_get(NULL, "pll half");
 296                clk_set_parent(i2s->clk_i2s, parent);
 297                ret = clk_set_rate(i2s->clk_i2s, freq);
 298                break;
 299        default:
 300                return -EINVAL;
 301        }
 302        clk_put(parent);
 303
 304        return ret;
 305}
 306
 307static int jz4740_i2s_suspend(struct snd_soc_dai *dai)
 308{
 309        struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai);
 310        uint32_t conf;
 311
 312        if (dai->active) {
 313                conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF);
 314                conf &= ~JZ_AIC_CONF_ENABLE;
 315                jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
 316
 317                clk_disable(i2s->clk_i2s);
 318        }
 319
 320        clk_disable(i2s->clk_aic);
 321
 322        return 0;
 323}
 324
 325static int jz4740_i2s_resume(struct snd_soc_dai *dai)
 326{
 327        struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai);
 328        uint32_t conf;
 329
 330        clk_enable(i2s->clk_aic);
 331
 332        if (dai->active) {
 333                clk_enable(i2s->clk_i2s);
 334
 335                conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF);
 336                conf |= JZ_AIC_CONF_ENABLE;
 337                jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
 338        }
 339
 340        return 0;
 341}
 342
 343static void jz4740_i2c_init_pcm_config(struct jz4740_i2s *i2s)
 344{
 345        struct jz4740_dma_config *dma_config;
 346
 347        /* Playback */
 348        dma_config = &i2s->pcm_config_playback.dma_config;
 349        dma_config->src_width = JZ4740_DMA_WIDTH_32BIT,
 350        dma_config->transfer_size = JZ4740_DMA_TRANSFER_SIZE_16BYTE;
 351        dma_config->request_type = JZ4740_DMA_TYPE_AIC_TRANSMIT;
 352        dma_config->flags = JZ4740_DMA_SRC_AUTOINC;
 353        dma_config->mode = JZ4740_DMA_MODE_SINGLE;
 354        i2s->pcm_config_playback.fifo_addr = i2s->phys_base + JZ_REG_AIC_FIFO;
 355
 356        /* Capture */
 357        dma_config = &i2s->pcm_config_capture.dma_config;
 358        dma_config->dst_width = JZ4740_DMA_WIDTH_32BIT,
 359        dma_config->transfer_size = JZ4740_DMA_TRANSFER_SIZE_16BYTE;
 360        dma_config->request_type = JZ4740_DMA_TYPE_AIC_RECEIVE;
 361        dma_config->flags = JZ4740_DMA_DST_AUTOINC;
 362        dma_config->mode = JZ4740_DMA_MODE_SINGLE;
 363        i2s->pcm_config_capture.fifo_addr = i2s->phys_base + JZ_REG_AIC_FIFO;
 364}
 365
 366static int jz4740_i2s_dai_probe(struct snd_soc_dai *dai)
 367{
 368        struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai);
 369        uint32_t conf;
 370
 371        clk_enable(i2s->clk_aic);
 372
 373        jz4740_i2c_init_pcm_config(i2s);
 374
 375        conf = (7 << JZ_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET) |
 376                (8 << JZ_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET) |
 377                JZ_AIC_CONF_OVERFLOW_PLAY_LAST |
 378                JZ_AIC_CONF_I2S |
 379                JZ_AIC_CONF_INTERNAL_CODEC;
 380
 381        jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, JZ_AIC_CONF_RESET);
 382        jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
 383
 384        return 0;
 385}
 386
 387static int jz4740_i2s_dai_remove(struct snd_soc_dai *dai)
 388{
 389        struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai);
 390
 391        clk_disable(i2s->clk_aic);
 392        return 0;
 393}
 394
 395static const struct snd_soc_dai_ops jz4740_i2s_dai_ops = {
 396        .startup = jz4740_i2s_startup,
 397        .shutdown = jz4740_i2s_shutdown,
 398        .trigger = jz4740_i2s_trigger,
 399        .hw_params = jz4740_i2s_hw_params,
 400        .set_fmt = jz4740_i2s_set_fmt,
 401        .set_sysclk = jz4740_i2s_set_sysclk,
 402};
 403
 404#define JZ4740_I2S_FMTS (SNDRV_PCM_FMTBIT_S8 | \
 405                SNDRV_PCM_FMTBIT_S16_LE)
 406
 407static struct snd_soc_dai_driver jz4740_i2s_dai = {
 408        .probe = jz4740_i2s_dai_probe,
 409        .remove = jz4740_i2s_dai_remove,
 410        .playback = {
 411                .channels_min = 1,
 412                .channels_max = 2,
 413                .rates = SNDRV_PCM_RATE_8000_48000,
 414                .formats = JZ4740_I2S_FMTS,
 415        },
 416        .capture = {
 417                .channels_min = 2,
 418                .channels_max = 2,
 419                .rates = SNDRV_PCM_RATE_8000_48000,
 420                .formats = JZ4740_I2S_FMTS,
 421        },
 422        .symmetric_rates = 1,
 423        .ops = &jz4740_i2s_dai_ops,
 424        .suspend = jz4740_i2s_suspend,
 425        .resume = jz4740_i2s_resume,
 426};
 427
 428static int __devinit jz4740_i2s_dev_probe(struct platform_device *pdev)
 429{
 430        struct jz4740_i2s *i2s;
 431        int ret;
 432
 433        i2s = kzalloc(sizeof(*i2s), GFP_KERNEL);
 434
 435        if (!i2s)
 436                return -ENOMEM;
 437
 438        i2s->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 439        if (!i2s->mem) {
 440                ret = -ENOENT;
 441                goto err_free;
 442        }
 443
 444        i2s->mem = request_mem_region(i2s->mem->start, resource_size(i2s->mem),
 445                                pdev->name);
 446        if (!i2s->mem) {
 447                ret = -EBUSY;
 448                goto err_free;
 449        }
 450
 451        i2s->base = ioremap_nocache(i2s->mem->start, resource_size(i2s->mem));
 452        if (!i2s->base) {
 453                ret = -EBUSY;
 454                goto err_release_mem_region;
 455        }
 456
 457        i2s->phys_base = i2s->mem->start;
 458
 459        i2s->clk_aic = clk_get(&pdev->dev, "aic");
 460        if (IS_ERR(i2s->clk_aic)) {
 461                ret = PTR_ERR(i2s->clk_aic);
 462                goto err_iounmap;
 463        }
 464
 465        i2s->clk_i2s = clk_get(&pdev->dev, "i2s");
 466        if (IS_ERR(i2s->clk_i2s)) {
 467                ret = PTR_ERR(i2s->clk_i2s);
 468                goto err_clk_put_aic;
 469        }
 470
 471        platform_set_drvdata(pdev, i2s);
 472        ret = snd_soc_register_dai(&pdev->dev, &jz4740_i2s_dai);
 473
 474        if (ret) {
 475                dev_err(&pdev->dev, "Failed to register DAI\n");
 476                goto err_clk_put_i2s;
 477        }
 478
 479        return 0;
 480
 481err_clk_put_i2s:
 482        clk_put(i2s->clk_i2s);
 483err_clk_put_aic:
 484        clk_put(i2s->clk_aic);
 485err_iounmap:
 486        iounmap(i2s->base);
 487err_release_mem_region:
 488        release_mem_region(i2s->mem->start, resource_size(i2s->mem));
 489err_free:
 490        kfree(i2s);
 491
 492        return ret;
 493}
 494
 495static int __devexit jz4740_i2s_dev_remove(struct platform_device *pdev)
 496{
 497        struct jz4740_i2s *i2s = platform_get_drvdata(pdev);
 498
 499        snd_soc_unregister_dai(&pdev->dev);
 500
 501        clk_put(i2s->clk_i2s);
 502        clk_put(i2s->clk_aic);
 503
 504        iounmap(i2s->base);
 505        release_mem_region(i2s->mem->start, resource_size(i2s->mem));
 506
 507        platform_set_drvdata(pdev, NULL);
 508        kfree(i2s);
 509
 510        return 0;
 511}
 512
 513static struct platform_driver jz4740_i2s_driver = {
 514        .probe = jz4740_i2s_dev_probe,
 515        .remove = __devexit_p(jz4740_i2s_dev_remove),
 516        .driver = {
 517                .name = "jz4740-i2s",
 518                .owner = THIS_MODULE,
 519        },
 520};
 521
 522module_platform_driver(jz4740_i2s_driver);
 523
 524MODULE_AUTHOR("Lars-Peter Clausen, <lars@metafoo.de>");
 525MODULE_DESCRIPTION("Ingenic JZ4740 SoC I2S driver");
 526MODULE_LICENSE("GPL");
 527MODULE_ALIAS("platform:jz4740-i2s");
 528