sound: rockchip: add support for i2s

This patch add driver support for rockchip i2s bus.

Change-Id: I2c7f2b46d628706fa000055f7962ba6b4ff6d0b6
Signed-off-by: Sugar Zhang <sugar.zhang@rock-chips.com>
This commit is contained in:
Sugar Zhang 2018-09-21 16:31:49 +08:00 committed by Jianhong Chen
parent 95f2641240
commit 69ab2873d7
6 changed files with 420 additions and 0 deletions

View File

@ -31,6 +31,16 @@ config I2S_SAMSUNG
option provides an implementation for sound_init() and
sound_play().
config I2S_ROCKCHIP
bool "Enable I2S support for rockchip SoCs"
depends on SOUND
help
Rockchip SoCs support an I2S interface for sending audio
data to an audio codec. This option enables support for this,
using one of the available audio codec drivers. Enabling this
option provides an implementation for sound_init() and
sound_play().
config SOUND_MAX98095
bool "Support Maxim max98095 audio codec"
depends on I2S_SAMSUNG

View File

@ -7,6 +7,7 @@
obj-$(CONFIG_SOUND) += sound.o
obj-$(CONFIG_I2S) += sound-i2s.o
obj-$(CONFIG_I2S_ROCKCHIP) += rockchip-i2s.o
obj-$(CONFIG_I2S_SAMSUNG) += samsung-i2s.o
obj-$(CONFIG_SOUND_SANDBOX) += sandbox.o
obj-$(CONFIG_SOUND_WM8994) += wm8994.o

View File

@ -0,0 +1,189 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2018 Rockchip Electronics Co., Ltd
*/
#include <clk.h>
#include <common.h>
#include <dm.h>
#include <errno.h>
#include <malloc.h>
#include <asm/io.h>
#include <sound.h>
#include "rockchip-i2s.h"
#define I2S_FIFO_LENGTH (31)
struct rk_i2s_dev {
void *regbase;
struct clk mclk;
};
static inline u32 i2s_reg_readl(struct rk_i2s_dev *dev, u32 offset)
{
return readl(dev->regbase + offset);
}
static inline void i2s_reg_writel(struct rk_i2s_dev *dev, u32 offset, u32 val)
{
writel(val, dev->regbase + offset);
}
static inline void i2s_reg_update_bits(struct rk_i2s_dev *dev, u32 offset,
u32 mask, u32 val)
{
u32 tmp, orig;
orig = readl(dev->regbase + offset);
tmp = orig & ~mask;
tmp |= val & mask;
if (tmp != orig)
writel(tmp, dev->regbase + offset);
}
static void dump_regs(struct rk_i2s_dev *dev)
{
int i = 0;
for (i = 0; i <= I2S_RXDR; i += 4)
debug("0x%02x: 0x%08x\n", i, readl(dev->regbase + i));
}
static int rk_i2s_hw_params(struct udevice *udev, unsigned int samplerate,
unsigned int fmt, unsigned int channels)
{
struct rk_i2s_dev *dev = dev_get_priv(udev);
/* set fmt */
i2s_reg_update_bits(dev, I2S_CKR,
I2S_CKR_MSS_MASK, I2S_CKR_MSS_MASTER);
i2s_reg_update_bits(dev, I2S_TXCR,
I2S_TXCR_IBM_MASK, I2S_TXCR_IBM_NORMAL);
i2s_reg_update_bits(dev, I2S_RXCR,
I2S_RXCR_IBM_MASK, I2S_RXCR_IBM_NORMAL);
/* set div */
i2s_reg_update_bits(dev, I2S_CKR,
I2S_CKR_TSD_MASK | I2S_CKR_RSD_MASK,
I2S_CKR_TSD(64) | I2S_CKR_RSD(64));
i2s_reg_update_bits(dev, I2S_CKR,
I2S_CKR_MDIV_MASK, I2S_CKR_MDIV(4));
/* set hwparams */
i2s_reg_update_bits(dev, I2S_TXCR,
I2S_TXCR_VDW_MASK |
I2S_TXCR_CSR_MASK,
I2S_TXCR_VDW(16) |
I2S_TXCR_CHN_2);
i2s_reg_update_bits(dev, I2S_RXCR,
I2S_RXCR_CSR_MASK |
I2S_RXCR_VDW_MASK,
I2S_TXCR_VDW(16) |
I2S_TXCR_CHN_2);
i2s_reg_update_bits(dev, I2S_DMACR,
I2S_DMACR_TDL_MASK | I2S_DMACR_RDL_MASK,
I2S_DMACR_TDL(16) | I2S_DMACR_RDL(16));
return 0;
}
static void rk_i2s_txctrl(struct rk_i2s_dev *dev, int on)
{
if (on) {
i2s_reg_update_bits(dev, I2S_XFER,
I2S_XFER_TXS_MASK | I2S_XFER_RXS_MASK,
I2S_XFER_TXS_START | I2S_XFER_RXS_START);
} else {
i2s_reg_update_bits(dev, I2S_XFER,
I2S_XFER_TXS_MASK |
I2S_XFER_RXS_MASK,
I2S_XFER_TXS_STOP |
I2S_XFER_RXS_STOP);
i2s_reg_update_bits(dev, I2S_CLR,
I2S_CLR_TXC_MASK | I2S_CLR_RXC_MASK,
I2S_CLR_TXC | I2S_CLR_RXC);
}
}
static int rk_i2s_transfer_tx_data(struct udevice *udev, unsigned int *data,
unsigned long data_size)
{
struct rk_i2s_dev *dev = dev_get_priv(udev);
u32 val;
if (data_size < I2S_FIFO_LENGTH) {
debug("%s : invalid data size\n", __func__);
return -EINVAL;
}
rk_i2s_txctrl(dev, 1);
while (data_size > 0) {
val = i2s_reg_readl(dev, I2S_FIFOLR);
if (val < I2S_FIFO_LENGTH) {
i2s_reg_writel(dev, I2S_TXDR, *data++);
data_size--;
}
}
return 0;
}
static int rk_i2s_set_sysclk(struct udevice *dev, unsigned int freq)
{
struct rk_i2s_dev *i2s = dev_get_priv(dev);
clk_set_rate(&i2s->mclk, freq);
return 0;
}
static const struct snd_soc_dai_ops rockchip_i2s_ops = {
.hw_params = rk_i2s_hw_params,
.set_sysclk = rk_i2s_set_sysclk,
.transfer = rk_i2s_transfer_tx_data,
};
static int rockchip_i2s_probe(struct udevice *dev)
{
struct rk_i2s_dev *i2s = dev_get_priv(dev);
int ret;
i2s->regbase = dev_read_addr_ptr(dev);
ret = clk_get_by_name(dev, "i2s_clk", &i2s->mclk);
if (ret) {
printf("%s get i2s mclk fail!\n", __func__);
return -EINVAL;
}
dump_regs(i2s);
return 0;
}
static const struct udevice_id rockchip_i2s_ids[] = {
{ .compatible = "rockchip,px30-i2s", },
{ .compatible = "rockchip,rk3036-i2s", },
{ .compatible = "rockchip,rk3066-i2s", },
{ .compatible = "rockchip,rk3128-i2s", },
{ .compatible = "rockchip,rk3188-i2s", },
{ .compatible = "rockchip,rk3288-i2s", },
{ .compatible = "rockchip,rk3328-i2s", },
{ .compatible = "rockchip,rk3368-i2s", },
{ .compatible = "rockchip,rk3399-i2s", },
{ }
};
U_BOOT_DRIVER(rockchip_i2s) = {
.name = "rockchip_i2s",
.id = UCLASS_I2S,
.of_match = rockchip_i2s_ids,
.probe = rockchip_i2s_probe,
.priv_auto_alloc_size = sizeof(struct rk_i2s_dev),
.ops = &rockchip_i2s_ops,
};
UCLASS_DRIVER(i2s) = {
.id = UCLASS_I2S,
.name = "i2s_bus",
};

View File

@ -0,0 +1,209 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* (C) Copyright 2018 Rockchip Electronics Co., Ltd
*/
#ifndef __ROCKCHIP_I2S_H__
#define __ROCKCHIP_I2S_H__
/* I2S REGS */
#define I2S_TXCR (0x0000)
#define I2S_RXCR (0x0004)
#define I2S_CKR (0x0008)
#define I2S_FIFOLR (0x000c)
#define I2S_DMACR (0x0010)
#define I2S_INTCR (0x0014)
#define I2S_INTSR (0x0018)
#define I2S_XFER (0x001c)
#define I2S_CLR (0x0020)
#define I2S_TXDR (0x0024)
#define I2S_RXDR (0x0028)
/*
* TXCR
* transmit operation control register
*/
#define I2S_TXCR_RCNT_SHIFT 17
#define I2S_TXCR_RCNT_MASK (0x3f << I2S_TXCR_RCNT_SHIFT)
#define I2S_TXCR_CSR_SHIFT 15
#define I2S_TXCR_CSR(x) (x << I2S_TXCR_CSR_SHIFT)
#define I2S_TXCR_CHN_2 (0 << I2S_TXCR_CSR_SHIFT)
#define I2S_TXCR_CHN_4 (1 << I2S_TXCR_CSR_SHIFT)
#define I2S_TXCR_CHN_6 (2 << I2S_TXCR_CSR_SHIFT)
#define I2S_TXCR_CHN_8 (3 << I2S_TXCR_CSR_SHIFT)
#define I2S_TXCR_CSR_MASK (3 << I2S_TXCR_CSR_SHIFT)
#define I2S_TXCR_HWT BIT(14)
#define I2S_TXCR_SJM_SHIFT 12
#define I2S_TXCR_SJM_R (0 << I2S_TXCR_SJM_SHIFT)
#define I2S_TXCR_SJM_L (1 << I2S_TXCR_SJM_SHIFT)
#define I2S_TXCR_FBM_SHIFT 11
#define I2S_TXCR_FBM_MSB (0 << I2S_TXCR_FBM_SHIFT)
#define I2S_TXCR_FBM_LSB (1 << I2S_TXCR_FBM_SHIFT)
#define I2S_TXCR_IBM_SHIFT 9
#define I2S_TXCR_IBM_NORMAL (0 << I2S_TXCR_IBM_SHIFT)
#define I2S_TXCR_IBM_LSJM (1 << I2S_TXCR_IBM_SHIFT)
#define I2S_TXCR_IBM_RSJM (2 << I2S_TXCR_IBM_SHIFT)
#define I2S_TXCR_IBM_MASK (3 << I2S_TXCR_IBM_SHIFT)
#define I2S_TXCR_PBM_SHIFT 7
#define I2S_TXCR_PBM_MODE(x) (x << I2S_TXCR_PBM_SHIFT)
#define I2S_TXCR_PBM_MASK (3 << I2S_TXCR_PBM_SHIFT)
#define I2S_TXCR_TFS_SHIFT 5
#define I2S_TXCR_TFS_I2S (0 << I2S_TXCR_TFS_SHIFT)
#define I2S_TXCR_TFS_PCM (1 << I2S_TXCR_TFS_SHIFT)
#define I2S_TXCR_TFS_MASK (1 << I2S_TXCR_TFS_SHIFT)
#define I2S_TXCR_VDW_SHIFT 0
#define I2S_TXCR_VDW(x) ((x - 1) << I2S_TXCR_VDW_SHIFT)
#define I2S_TXCR_VDW_MASK (0x1f << I2S_TXCR_VDW_SHIFT)
/*
* RXCR
* receive operation control register
*/
#define I2S_RXCR_CSR_SHIFT 15
#define I2S_RXCR_CSR(x) (x << I2S_RXCR_CSR_SHIFT)
#define I2S_RXCR_CSR_MASK (3 << I2S_RXCR_CSR_SHIFT)
#define I2S_RXCR_HWT BIT(14)
#define I2S_RXCR_SJM_SHIFT 12
#define I2S_RXCR_SJM_R (0 << I2S_RXCR_SJM_SHIFT)
#define I2S_RXCR_SJM_L (1 << I2S_RXCR_SJM_SHIFT)
#define I2S_RXCR_FBM_SHIFT 11
#define I2S_RXCR_FBM_MSB (0 << I2S_RXCR_FBM_SHIFT)
#define I2S_RXCR_FBM_LSB (1 << I2S_RXCR_FBM_SHIFT)
#define I2S_RXCR_IBM_SHIFT 9
#define I2S_RXCR_IBM_NORMAL (0 << I2S_RXCR_IBM_SHIFT)
#define I2S_RXCR_IBM_LSJM (1 << I2S_RXCR_IBM_SHIFT)
#define I2S_RXCR_IBM_RSJM (2 << I2S_RXCR_IBM_SHIFT)
#define I2S_RXCR_IBM_MASK (3 << I2S_RXCR_IBM_SHIFT)
#define I2S_RXCR_PBM_SHIFT 7
#define I2S_RXCR_PBM_MODE(x) (x << I2S_RXCR_PBM_SHIFT)
#define I2S_RXCR_PBM_MASK (3 << I2S_RXCR_PBM_SHIFT)
#define I2S_RXCR_TFS_SHIFT 5
#define I2S_RXCR_TFS_I2S (0 << I2S_RXCR_TFS_SHIFT)
#define I2S_RXCR_TFS_PCM (1 << I2S_RXCR_TFS_SHIFT)
#define I2S_RXCR_TFS_MASK (1 << I2S_RXCR_TFS_SHIFT)
#define I2S_RXCR_VDW_SHIFT 0
#define I2S_RXCR_VDW(x) ((x - 1) << I2S_RXCR_VDW_SHIFT)
#define I2S_RXCR_VDW_MASK (0x1f << I2S_RXCR_VDW_SHIFT)
/*
* CKR
* clock generation register
*/
#define I2S_CKR_MSS_SHIFT 27
#define I2S_CKR_MSS_MASTER (0 << I2S_CKR_MSS_SHIFT)
#define I2S_CKR_MSS_SLAVE (1 << I2S_CKR_MSS_SHIFT)
#define I2S_CKR_MSS_MASK (1 << I2S_CKR_MSS_SHIFT)
#define I2S_CKR_CKP_SHIFT 26
#define I2S_CKR_CKP_NEG (0 << I2S_CKR_CKP_SHIFT)
#define I2S_CKR_CKP_POS (1 << I2S_CKR_CKP_SHIFT)
#define I2S_CKR_RLP_SHIFT 25
#define I2S_CKR_RLP_NORMAL (0 << I2S_CKR_RLP_SHIFT)
#define I2S_CKR_RLP_OPPSITE (1 << I2S_CKR_RLP_SHIFT)
#define I2S_CKR_TLP_SHIFT 24
#define I2S_CKR_TLP_NORMAL (0 << I2S_CKR_TLP_SHIFT)
#define I2S_CKR_TLP_OPPSITE (1 << I2S_CKR_TLP_SHIFT)
#define I2S_CKR_MDIV_SHIFT 16
#define I2S_CKR_MDIV(x) (((x) - 1) << I2S_CKR_MDIV_SHIFT)
#define I2S_CKR_MDIV_MASK (0xff << I2S_CKR_MDIV_SHIFT)
#define I2S_CKR_RSD_SHIFT 8
#define I2S_CKR_RSD(x) (((x) - 1) << I2S_CKR_RSD_SHIFT)
#define I2S_CKR_RSD_MASK (0xff << I2S_CKR_RSD_SHIFT)
#define I2S_CKR_TSD_SHIFT 0
#define I2S_CKR_TSD(x) (((x) - 1) << I2S_CKR_TSD_SHIFT)
#define I2S_CKR_TSD_MASK (0xff << I2S_CKR_TSD_SHIFT)
/*
* FIFOLR
* FIFO level register
*/
#define I2S_FIFOLR_RFL_SHIFT 24
#define I2S_FIFOLR_RFL_MASK (0x3f << I2S_FIFOLR_RFL_SHIFT)
#define I2S_FIFOLR_TFL3_SHIFT 18
#define I2S_FIFOLR_TFL3_MASK (0x3f << I2S_FIFOLR_TFL3_SHIFT)
#define I2S_FIFOLR_TFL2_SHIFT 12
#define I2S_FIFOLR_TFL2_MASK (0x3f << I2S_FIFOLR_TFL2_SHIFT)
#define I2S_FIFOLR_TFL1_SHIFT 6
#define I2S_FIFOLR_TFL1_MASK (0x3f << I2S_FIFOLR_TFL1_SHIFT)
#define I2S_FIFOLR_TFL0_SHIFT 0
#define I2S_FIFOLR_TFL0_MASK (0x3f << I2S_FIFOLR_TFL0_SHIFT)
/*
* DMACR
* DMA control register
*/
#define I2S_DMACR_RDE_SHIFT 24
#define I2S_DMACR_RDE_DISABLE (0 << I2S_DMACR_RDE_SHIFT)
#define I2S_DMACR_RDE_ENABLE (1 << I2S_DMACR_RDE_SHIFT)
#define I2S_DMACR_RDE_MASK (1 << I2S_DMACR_RDE_SHIFT)
#define I2S_DMACR_RDL_SHIFT 16
#define I2S_DMACR_RDL(x) ((x - 1) << I2S_DMACR_RDL_SHIFT)
#define I2S_DMACR_RDL_MASK (0x1f << I2S_DMACR_RDL_SHIFT)
#define I2S_DMACR_TDE_SHIFT 8
#define I2S_DMACR_TDE_DISABLE (0 << I2S_DMACR_TDE_SHIFT)
#define I2S_DMACR_TDE_ENABLE (1 << I2S_DMACR_TDE_SHIFT)
#define I2S_DMACR_TDE_MASK (1 << I2S_DMACR_TDE_SHIFT)
#define I2S_DMACR_TDL_SHIFT 0
#define I2S_DMACR_TDL(x) ((x) << I2S_DMACR_TDL_SHIFT)
#define I2S_DMACR_TDL_MASK (0x1f << I2S_DMACR_TDL_SHIFT)
/*
* INTCR
* interrupt control register
*/
#define I2S_INTCR_RFT_SHIFT 20
#define I2S_INTCR_RFT(x) ((x - 1) << I2S_INTCR_RFT_SHIFT)
#define I2S_INTCR_RXOIC BIT(18)
#define I2S_INTCR_RXOIE_SHIFT 17
#define I2S_INTCR_RXOIE_DISABLE (0 << I2S_INTCR_RXOIE_SHIFT)
#define I2S_INTCR_RXOIE_ENABLE (1 << I2S_INTCR_RXOIE_SHIFT)
#define I2S_INTCR_RXFIE_SHIFT 16
#define I2S_INTCR_RXFIE_DISABLE (0 << I2S_INTCR_RXFIE_SHIFT)
#define I2S_INTCR_RXFIE_ENABLE (1 << I2S_INTCR_RXFIE_SHIFT)
#define I2S_INTCR_TFT_SHIFT 4
#define I2S_INTCR_TFT(x) ((x - 1) << I2S_INTCR_TFT_SHIFT)
#define I2S_INTCR_TFT_MASK (0x1f << I2S_INTCR_TFT_SHIFT)
#define I2S_INTCR_TXUIC BIT(2)
#define I2S_INTCR_TXUIE_SHIFT 1
#define I2S_INTCR_TXUIE_DISABLE (0 << I2S_INTCR_TXUIE_SHIFT)
#define I2S_INTCR_TXUIE_ENABLE (1 << I2S_INTCR_TXUIE_SHIFT)
/*
* INTSR
* interrupt status register
*/
#define I2S_INTSR_RXOI_SHIFT 17
#define I2S_INTSR_RXOI_INA (0 << I2S_INTSR_RXOI_SHIFT)
#define I2S_INTSR_RXOI_ACT (1 << I2S_INTSR_RXOI_SHIFT)
#define I2S_INTSR_RXFI_SHIFT 16
#define I2S_INTSR_RXFI_INA (0 << I2S_INTSR_RXFI_SHIFT)
#define I2S_INTSR_RXFI_ACT (1 << I2S_INTSR_RXFI_SHIFT)
#define I2S_INTSR_TXUI_SHIFT 1
#define I2S_INTSR_TXUI_INA (0 << I2S_INTSR_TXUI_SHIFT)
#define I2S_INTSR_TXUI_ACT (1 << I2S_INTSR_TXUI_SHIFT)
#define I2S_INTSR_TXEI_SHIFT 0
#define I2S_INTSR_TXEI_INA (0 << I2S_INTSR_TXEI_SHIFT)
#define I2S_INTSR_TXEI_ACT (1 << I2S_INTSR_TXEI_SHIFT)
/*
* XFER
* Transfer start register
*/
#define I2S_XFER_RXS_SHIFT 1
#define I2S_XFER_RXS_STOP (0 << I2S_XFER_RXS_SHIFT)
#define I2S_XFER_RXS_START (1 << I2S_XFER_RXS_SHIFT)
#define I2S_XFER_RXS_MASK (1 << I2S_XFER_RXS_SHIFT)
#define I2S_XFER_TXS_SHIFT 0
#define I2S_XFER_TXS_STOP (0 << I2S_XFER_TXS_SHIFT)
#define I2S_XFER_TXS_START (1 << I2S_XFER_TXS_SHIFT)
#define I2S_XFER_TXS_MASK (1 << I2S_XFER_TXS_SHIFT)
/*
* CLR
* clear SCLK domain logic register
*/
#define I2S_CLR_RXC BIT(1)
#define I2S_CLR_RXC_MASK BIT(1)
#define I2S_CLR_TXC BIT(0)
#define I2S_CLR_TXC_MASK BIT(0)
#endif

View File

@ -41,6 +41,7 @@ enum uclass_id {
UCLASS_I2C_EEPROM, /* I2C EEPROM device */
UCLASS_I2C_GENERIC, /* Generic I2C device */
UCLASS_I2C_MUX, /* I2C multiplexer */
UCLASS_I2S, /* I2S bus */
UCLASS_IDE, /* IDE device */
UCLASS_IRQ, /* Interrupt controller */
UCLASS_KEYBOARD, /* Keyboard input device */

View File

@ -53,4 +53,14 @@ int sound_init(const void *blob);
*/
int sound_play(uint32_t msec, uint32_t frequency);
struct snd_soc_dai_ops {
int (*hw_params)(struct udevice *dev, unsigned int samplerate,
unsigned int fmt, unsigned int channels);
int (*startup)(struct udevice *dev);
int (*shutdown)(struct udevice *dev);
int (*transfer)(struct udevice *dev, unsigned int *data,
unsigned long data_size);
int (*set_sysclk)(struct udevice *dev, unsigned int freq);
};
#endif /* __SOUND__H__ */