@@ -621,6 +621,12 @@ config SND_SOC_RT298
config SND_SOC_RT5514
tristate
+ default y if SND_SOC_RT5514_SPI=y
+ default m if SND_SOC_RT5514_SPI=m
+
+config SND_SOC_RT5514_SPI
+ tristate
+ depends on SPI_MASTER
config SND_SOC_RT5616
tristate
@@ -96,6 +96,7 @@ snd-soc-rl6347a-objs := rl6347a.o
snd-soc-rt286-objs := rt286.o
snd-soc-rt298-objs := rt298.o
snd-soc-rt5514-objs := rt5514.o
+snd-soc-rt5514-spi-objs := rt5514-spi.o
snd-soc-rt5616-objs := rt5616.o
snd-soc-rt5631-objs := rt5631.o
snd-soc-rt5640-objs := rt5640.o
@@ -304,6 +305,7 @@ obj-$(CONFIG_SND_SOC_RL6347A) += snd-soc-rl6347a.o
obj-$(CONFIG_SND_SOC_RT286) += snd-soc-rt286.o
obj-$(CONFIG_SND_SOC_RT298) += snd-soc-rt298.o
obj-$(CONFIG_SND_SOC_RT5514) += snd-soc-rt5514.o
+obj-$(CONFIG_SND_SOC_RT5514_SPI) += snd-soc-rt5514-spi.o
obj-$(CONFIG_SND_SOC_RT5616) += snd-soc-rt5616.o
obj-$(CONFIG_SND_SOC_RT5631) += snd-soc-rt5631.o
obj-$(CONFIG_SND_SOC_RT5640) += snd-soc-rt5640.o
new file mode 100644
@@ -0,0 +1,494 @@
+/*
+ * rt5514-spi.c -- RT5514 SPI driver
+ *
+ * Copyright 2015 Realtek Semiconductor Corp.
+ * Author: Oder Chiou <oder_chiou@realtek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/spi/spi.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/sched.h>
+#include <linux/kthread.h>
+#include <linux/uaccess.h>
+#include <linux/miscdevice.h>
+#include <linux/regulator/consumer.h>
+#include <linux/pm_qos.h>
+#include <linux/sysfs.h>
+#include <linux/clk.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "rt5514-spi.h"
+
+static struct spi_device *rt5514_spi;
+
+struct rt5514_dsp {
+ struct device *dev;
+ struct delayed_work copy_work;
+ struct mutex dma_lock;
+ struct snd_pcm_substream *substream;
+ unsigned int buf_base, buf_limit, buf_rp;
+ size_t buf_size;
+ size_t dma_offset;
+ size_t dsp_offset;
+};
+
+static const struct snd_pcm_hardware rt5514_spi_pcm_hardware = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .period_bytes_min = PAGE_SIZE,
+ .period_bytes_max = 0x20000 / 8,
+ .periods_min = 8,
+ .periods_max = 8,
+ .channels_min = 1,
+ .channels_max = 1,
+ .buffer_bytes_max = 0x20000,
+};
+
+static struct snd_soc_dai_driver rt5514_spi_dai = {
+ .name = "rt5514-dsp-cpu-dai",
+ .id = 0,
+ .capture = {
+ .stream_name = "DSP Capture",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+};
+
+static void rt5514_spi_copy_work(struct work_struct *work)
+{
+ struct rt5514_dsp *rt5514_dsp =
+ container_of(work, struct rt5514_dsp, copy_work.work);
+ struct snd_pcm_runtime *runtime = rt5514_dsp->substream->runtime;
+ size_t period_bytes, truncated_bytes = 0;
+
+ mutex_lock(&rt5514_dsp->dma_lock);
+ if (!rt5514_dsp->substream) {
+ dev_err(rt5514_dsp->dev, "No pcm substream\n");
+ goto done;
+ }
+
+ period_bytes = snd_pcm_lib_period_bytes(rt5514_dsp->substream);
+
+ if (rt5514_dsp->buf_size - rt5514_dsp->dsp_offset < period_bytes)
+ period_bytes = rt5514_dsp->buf_size - rt5514_dsp->dsp_offset;
+
+ if (rt5514_dsp->buf_rp + period_bytes <= rt5514_dsp->buf_limit) {
+ rt5514_spi_burst_read(rt5514_dsp->buf_rp,
+ runtime->dma_area + rt5514_dsp->dma_offset,
+ period_bytes);
+
+ if (rt5514_dsp->buf_rp + period_bytes == rt5514_dsp->buf_limit)
+ rt5514_dsp->buf_rp = rt5514_dsp->buf_base;
+ else
+ rt5514_dsp->buf_rp += period_bytes;
+ } else {
+ truncated_bytes = rt5514_dsp->buf_limit - rt5514_dsp->buf_rp;
+ rt5514_spi_burst_read(rt5514_dsp->buf_rp,
+ runtime->dma_area + rt5514_dsp->dma_offset,
+ truncated_bytes);
+
+ rt5514_spi_burst_read(rt5514_dsp->buf_base,
+ runtime->dma_area + rt5514_dsp->dma_offset +
+ truncated_bytes, period_bytes - truncated_bytes);
+
+ rt5514_dsp->buf_rp = rt5514_dsp->buf_base +
+ period_bytes - truncated_bytes;
+ }
+
+ rt5514_dsp->dma_offset += period_bytes;
+ if (rt5514_dsp->dma_offset >= runtime->dma_bytes)
+ rt5514_dsp->dma_offset = 0;
+
+ rt5514_dsp->dsp_offset += period_bytes;
+
+ snd_pcm_period_elapsed(rt5514_dsp->substream);
+
+ if (rt5514_dsp->dsp_offset < rt5514_dsp->buf_size)
+ schedule_delayed_work(&rt5514_dsp->copy_work, 5);
+done:
+ mutex_unlock(&rt5514_dsp->dma_lock);
+}
+
+/* PCM for streaming audio from the DSP buffer */
+static int rt5514_spi_pcm_open(struct snd_pcm_substream *substream)
+{
+ snd_soc_set_runtime_hwparams(substream, &rt5514_spi_pcm_hardware);
+
+ return 0;
+}
+
+static int rt5514_spi_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct rt5514_dsp *rt5514_dsp =
+ snd_soc_platform_get_drvdata(rtd->platform);
+ int ret;
+
+ mutex_lock(&rt5514_dsp->dma_lock);
+ ret = snd_pcm_lib_alloc_vmalloc_buffer(substream,
+ params_buffer_bytes(hw_params));
+ rt5514_dsp->substream = substream;
+ mutex_unlock(&rt5514_dsp->dma_lock);
+
+ return ret;
+}
+
+static int rt5514_spi_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct rt5514_dsp *rt5514_dsp =
+ snd_soc_platform_get_drvdata(rtd->platform);
+
+ mutex_lock(&rt5514_dsp->dma_lock);
+ cancel_delayed_work_sync(&rt5514_dsp->copy_work);
+ rt5514_dsp->substream = NULL;
+ mutex_unlock(&rt5514_dsp->dma_lock);
+
+ return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+
+static int rt5514_spi_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct rt5514_dsp *rt5514_dsp =
+ snd_soc_platform_get_drvdata(rtd->platform);
+
+ rt5514_dsp->dma_offset = 0;
+ rt5514_dsp->dsp_offset = 0;
+ rt5514_spi_read_addr(RT5514_BUFFER_VOICE_BASE, &rt5514_dsp->buf_base);
+ rt5514_spi_read_addr(RT5514_BUFFER_VOICE_LIMIT, &rt5514_dsp->buf_limit);
+ rt5514_spi_read_addr(RT5514_BUFFER_VOICE_RP, &rt5514_dsp->buf_rp);
+ rt5514_spi_read_addr(RT5514_BUFFER_VOICE_SIZE, &rt5514_dsp->buf_size);
+
+ if (rt5514_dsp->buf_base && rt5514_dsp->buf_limit &&
+ rt5514_dsp->buf_rp && rt5514_dsp->buf_size)
+ schedule_delayed_work(&rt5514_dsp->copy_work, 0);
+
+ return 0;
+}
+
+static snd_pcm_uframes_t rt5514_spi_pcm_pointer(
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct rt5514_dsp *rt5514_dsp =
+ snd_soc_platform_get_drvdata(rtd->platform);
+
+ return bytes_to_frames(runtime, rt5514_dsp->dma_offset);
+}
+
+static struct snd_pcm_ops rt5514_spi_pcm_ops = {
+ .open = rt5514_spi_pcm_open,
+ .hw_params = rt5514_spi_hw_params,
+ .hw_free = rt5514_spi_hw_free,
+ .prepare = rt5514_spi_prepare,
+ .pointer = rt5514_spi_pcm_pointer,
+ .mmap = snd_pcm_lib_mmap_vmalloc,
+ .page = snd_pcm_lib_get_vmalloc_page,
+};
+
+static int rt5514_spi_pcm_probe(struct snd_soc_platform *platform)
+{
+ struct rt5514_dsp *rt5514_dsp;
+
+ rt5514_dsp = devm_kzalloc(platform->dev, sizeof(*rt5514_dsp),
+ GFP_KERNEL);
+
+ rt5514_dsp->dev = &rt5514_spi->dev;
+ mutex_init(&rt5514_dsp->dma_lock);
+ INIT_DELAYED_WORK(&rt5514_dsp->copy_work, rt5514_spi_copy_work);
+ snd_soc_platform_set_drvdata(platform, rt5514_dsp);
+
+ return 0;
+}
+
+static struct snd_soc_platform_driver rt5514_spi_platform = {
+ .probe = rt5514_spi_pcm_probe,
+ .ops = &rt5514_spi_pcm_ops,
+};
+
+static const struct snd_soc_component_driver rt5514_spi_dai_component = {
+ .name = "rt5514-spi-dai",
+};
+
+int rt5514_spi_read_addr(unsigned int addr, unsigned int *val)
+{
+ struct spi_device *spi = rt5514_spi;
+ struct spi_message message;
+ struct spi_transfer x[3];
+ u8 spi_cmd = RT5514_SPI_CMD_32_READ;
+ int status;
+ u8 write_buf[5];
+ u8 read_buf[4];
+
+ write_buf[0] = spi_cmd;
+ write_buf[1] = (addr & 0xff000000) >> 24;
+ write_buf[2] = (addr & 0x00ff0000) >> 16;
+ write_buf[3] = (addr & 0x0000ff00) >> 8;
+ write_buf[4] = (addr & 0x000000ff) >> 0;
+
+ spi_message_init(&message);
+ memset(x, 0, sizeof(x));
+
+ x[0].len = 5;
+ x[0].tx_buf = write_buf;
+ spi_message_add_tail(&x[0], &message);
+
+ x[1].len = 4;
+ x[1].tx_buf = write_buf;
+ spi_message_add_tail(&x[1], &message);
+
+ x[2].len = 4;
+ x[2].rx_buf = read_buf;
+ spi_message_add_tail(&x[2], &message);
+
+ status = spi_sync(spi, &message);
+
+ *val = read_buf[3] | read_buf[2] << 8 | read_buf[1] << 16 |
+ read_buf[0] << 24;
+
+ return status;
+}
+EXPORT_SYMBOL_GPL(rt5514_spi_read_addr);
+
+int rt5514_spi_write_addr(unsigned int addr, unsigned int val)
+{
+ struct spi_device *spi = rt5514_spi;
+ u8 spi_cmd = RT5514_SPI_CMD_32_WRITE;
+ int status;
+ u8 write_buf[10];
+
+ write_buf[0] = spi_cmd;
+ write_buf[1] = (addr & 0xff000000) >> 24;
+ write_buf[2] = (addr & 0x00ff0000) >> 16;
+ write_buf[3] = (addr & 0x0000ff00) >> 8;
+ write_buf[4] = (addr & 0x000000ff) >> 0;
+ write_buf[5] = (val & 0xff000000) >> 24;
+ write_buf[6] = (val & 0x00ff0000) >> 16;
+ write_buf[7] = (val & 0x0000ff00) >> 8;
+ write_buf[8] = (val & 0x000000ff) >> 0;
+ write_buf[9] = spi_cmd;
+
+ status = spi_write(spi, write_buf, sizeof(write_buf));
+
+ if (status)
+ dev_err(&spi->dev, "%s error %d\n", __func__, status);
+
+ return status;
+}
+EXPORT_SYMBOL_GPL(rt5514_spi_write_addr);
+
+/**
+ * rt5514_spi_burst_read - Read data from SPI by rt5514 address.
+ * @addr: Start address.
+ * @rxbuf: Data Buffer for reading.
+ * @len: Data length, it must be a multiple of 8.
+ *
+ *
+ * Returns true for success.
+ */
+int rt5514_spi_burst_read(unsigned int addr, u8 *rxbuf, size_t len)
+{
+ u8 spi_cmd = RT5514_SPI_CMD_BURST_READ;
+ int status;
+ u8 write_buf[8];
+ unsigned int i, end, offset = 0;
+
+ struct spi_message message;
+ struct spi_transfer x[3];
+
+ while (offset < len) {
+ if (offset + RT5514_SPI_BUF_LEN <= len)
+ end = RT5514_SPI_BUF_LEN;
+ else
+ end = len % RT5514_SPI_BUF_LEN;
+
+ write_buf[0] = spi_cmd;
+ write_buf[1] = ((addr + offset) & 0xff000000) >> 24;
+ write_buf[2] = ((addr + offset) & 0x00ff0000) >> 16;
+ write_buf[3] = ((addr + offset) & 0x0000ff00) >> 8;
+ write_buf[4] = ((addr + offset) & 0x000000ff) >> 0;
+
+ spi_message_init(&message);
+ memset(x, 0, sizeof(x));
+
+ x[0].len = 5;
+ x[0].tx_buf = write_buf;
+ spi_message_add_tail(&x[0], &message);
+
+ x[1].len = 4;
+ x[1].tx_buf = write_buf;
+ spi_message_add_tail(&x[1], &message);
+
+ x[2].len = end;
+ x[2].rx_buf = rxbuf + offset;
+ spi_message_add_tail(&x[2], &message);
+
+ status = spi_sync(rt5514_spi, &message);
+
+ if (status)
+ return false;
+
+ offset += RT5514_SPI_BUF_LEN;
+ }
+
+ for (i = 0; i < len; i += 8) {
+ write_buf[0] = rxbuf[i + 0];
+ write_buf[1] = rxbuf[i + 1];
+ write_buf[2] = rxbuf[i + 2];
+ write_buf[3] = rxbuf[i + 3];
+ write_buf[4] = rxbuf[i + 4];
+ write_buf[5] = rxbuf[i + 5];
+ write_buf[6] = rxbuf[i + 6];
+ write_buf[7] = rxbuf[i + 7];
+
+ rxbuf[i + 0] = write_buf[7];
+ rxbuf[i + 1] = write_buf[6];
+ rxbuf[i + 2] = write_buf[5];
+ rxbuf[i + 3] = write_buf[4];
+ rxbuf[i + 4] = write_buf[3];
+ rxbuf[i + 5] = write_buf[2];
+ rxbuf[i + 6] = write_buf[1];
+ rxbuf[i + 7] = write_buf[0];
+ }
+
+ return true;
+}
+EXPORT_SYMBOL_GPL(rt5514_spi_burst_read);
+
+/**
+ * rt5514_spi_burst_write - Write data to SPI by rt5514 address.
+ * @addr: Start address.
+ * @txbuf: Data Buffer for writng.
+ * @len: Data length, it must be a multiple of 8.
+ *
+ *
+ * Returns true for success.
+ */
+int rt5514_spi_burst_write(u32 addr, const u8 *txbuf, size_t len)
+{
+ u8 spi_cmd = RT5514_SPI_CMD_BURST_WRITE;
+ u8 *write_buf;
+ unsigned int i, end, offset = 0;
+
+ write_buf = kmalloc(RT5514_SPI_BUF_LEN + 6, GFP_KERNEL);
+
+ if (write_buf == NULL)
+ return -ENOMEM;
+
+ while (offset < len) {
+ if (offset + RT5514_SPI_BUF_LEN <= len)
+ end = RT5514_SPI_BUF_LEN;
+ else
+ end = len % RT5514_SPI_BUF_LEN;
+
+ write_buf[0] = spi_cmd;
+ write_buf[1] = ((addr + offset) & 0xff000000) >> 24;
+ write_buf[2] = ((addr + offset) & 0x00ff0000) >> 16;
+ write_buf[3] = ((addr + offset) & 0x0000ff00) >> 8;
+ write_buf[4] = ((addr + offset) & 0x000000ff) >> 0;
+
+ for (i = 0; i < end; i += 8) {
+ write_buf[i + 12] = txbuf[offset + i + 0];
+ write_buf[i + 11] = txbuf[offset + i + 1];
+ write_buf[i + 10] = txbuf[offset + i + 2];
+ write_buf[i + 9] = txbuf[offset + i + 3];
+ write_buf[i + 8] = txbuf[offset + i + 4];
+ write_buf[i + 7] = txbuf[offset + i + 5];
+ write_buf[i + 6] = txbuf[offset + i + 6];
+ write_buf[i + 5] = txbuf[offset + i + 7];
+ }
+
+ write_buf[end + 5] = spi_cmd;
+
+ spi_write(rt5514_spi, write_buf, end + 6);
+
+ offset += RT5514_SPI_BUF_LEN;
+ }
+
+ kfree(write_buf);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rt5514_spi_burst_write);
+
+static int rt5514_spi_probe(struct spi_device *spi)
+{
+ int ret;
+
+ rt5514_spi = spi;
+
+ ret = snd_soc_register_platform(&spi->dev, &rt5514_spi_platform);
+ if (ret < 0) {
+ dev_err(&spi->dev, "Failed to register platform.\n");
+ goto err_plat;
+ }
+
+ ret = snd_soc_register_component(&spi->dev, &rt5514_spi_dai_component,
+ &rt5514_spi_dai, 1);
+ if (ret < 0) {
+ dev_err(&spi->dev, "Failed to register component.\n");
+ goto err_comp;
+ }
+
+ return 0;
+err_comp:
+ snd_soc_unregister_platform(&spi->dev);
+err_plat:
+
+ return 0;
+}
+
+static int rt5514_spi_remove(struct spi_device *spi)
+{
+ snd_soc_unregister_component(&spi->dev);
+ snd_soc_unregister_platform(&spi->dev);
+
+ return 0;
+}
+
+#if defined(CONFIG_OF)
+static const struct of_device_id rt5514_of_match[] = {
+ { .compatible = "realtek,rt5514", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, rt5514_of_match);
+#endif
+
+static struct spi_driver rt5514_spi_driver = {
+ .driver = {
+ .name = "rt5514",
+ .of_match_table = of_match_ptr(rt5514_of_match),
+ },
+ .probe = rt5514_spi_probe,
+ .remove = rt5514_spi_remove,
+};
+module_spi_driver(rt5514_spi_driver);
+
+MODULE_DESCRIPTION("RT5514 SPI driver");
+MODULE_AUTHOR("Oder Chiou <oder_chiou@realtek.com>");
+MODULE_LICENSE("GPL v2");
new file mode 100644
@@ -0,0 +1,36 @@
+/*
+ * rt5514-spi.h -- RT5514 driver
+ *
+ * Copyright 2015 Realtek Semiconductor Corp.
+ * Author: Oder Chiou <oder_chiou@realtek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __RT5514_SPI_H__
+#define __RT5514_SPI_H__
+
+#define RT5514_SPI_BUF_LEN 240
+#define RT5514_BUFFER_VOICE_BASE 0x18001034
+#define RT5514_BUFFER_VOICE_LIMIT 0x18001038
+#define RT5514_BUFFER_VOICE_RP 0x1800103c
+#define RT5514_BUFFER_VOICE_SIZE 0x18001040
+
+/* SPI Command */
+enum {
+ RT5514_SPI_CMD_16_READ = 0,
+ RT5514_SPI_CMD_16_WRITE,
+ RT5514_SPI_CMD_32_READ,
+ RT5514_SPI_CMD_32_WRITE,
+ RT5514_SPI_CMD_BURST_READ,
+ RT5514_SPI_CMD_BURST_WRITE,
+};
+
+int rt5514_spi_read_addr(unsigned int addr, unsigned int *val);
+int rt5514_spi_write_addr(unsigned int addr, unsigned int val);
+int rt5514_spi_burst_read(unsigned int addr, u8 *rxbuf, size_t len);
+int rt5514_spi_burst_write(u32 addr, const u8 *txbuf, size_t len);
+
+#endif /* __RT5514_SPI_H__ */
@@ -30,6 +30,9 @@
#include "rl6231.h"
#include "rt5514.h"
+#if defined(CONFIG_SND_SOC_RT5514_SPI)
+#include "rt5514-spi.h"
+#endif
static const struct reg_sequence rt5514_i2c_patch[] = {
{0x1800101c, 0x00000000},
@@ -110,6 +113,159 @@ static const struct reg_default rt5514_reg[] = {
{RT5514_VENDOR_ID2, 0x10ec5514},
};
+static void rt5514_enable_dsp_clock(struct rt5514_priv *rt5514)
+{
+ /* Reset */
+ regmap_write(rt5514->i2c_regmap, 0x18002000, 0x000010ec);
+ /* LDO_I_limit */
+ regmap_write(rt5514->i2c_regmap, 0x18002200, 0x00028604);
+ /* (for reset DSP) mini-core reset */
+ regmap_write(rt5514->i2c_regmap, 0x18002f00, 0x0005514b);
+ /* (for reset DSP) mini-core reset */
+ regmap_write(rt5514->i2c_regmap, 0x18002f00, 0x00055149);
+ /* DMIC_OUT1/2=HiZ */
+ regmap_write(rt5514->i2c_regmap, 0x180020a0, 0x00000000);
+ /* PIN config */
+ regmap_write(rt5514->i2c_regmap, 0x18002070, 0x00000040);
+ /* pll3(QN)=RCOSC*(10+2) */
+ regmap_write(rt5514->i2c_regmap, 0x18002240, 0x0000000a);
+ /* pll3 source=RCOSC, fsi=rt_clk */
+ regmap_write(rt5514->i2c_regmap, 0x18002100, 0x0000000b);
+ /* PU RCOSC, pll3 */
+ regmap_write(rt5514->i2c_regmap, 0x18002004, 0x00808b81);
+ /* PD ADC1/2 */
+ regmap_write(rt5514->i2c_regmap, 0x18002008, 0x00220000);
+ /* DSP clk source=pll3, ENABLE DSP clk */
+ regmap_write(rt5514->i2c_regmap, 0x18002f08, 0x00000005);
+ /* 256fs=/4,DMIC_CLK_OUT=/16(disable ad2) */
+ regmap_write(rt5514->i2c_regmap, 0x18002104, 0x10023541);
+ /* clk_sys source=mux_out */
+ regmap_write(rt5514->i2c_regmap, 0x18002108, 0x00000000);
+ /* DSP clk= 15M*(20+1)/32 =10M */
+ regmap_write(rt5514->i2c_regmap, 0x18001100, 0x00000214);
+ /* DB, pointer */
+ regmap_write(rt5514->i2c_regmap, 0x18002148, 0x80000000);
+ /* DB, pop=4x */
+ regmap_write(rt5514->i2c_regmap, 0x18002140, 0x3fff00fa);
+ /* DB, pointer */
+ regmap_write(rt5514->i2c_regmap, 0x18002148, 0x00000000);
+ /* DFLL reset */
+ regmap_write(rt5514->i2c_regmap, 0x18002124, 0x00220012);
+ /* DFLL, set m/n */
+ regmap_write(rt5514->i2c_regmap, 0x18002110, 0x000101f4);
+ /* DFLL,reset DFLL */
+ regmap_write(rt5514->i2c_regmap, 0x18002124, 0x80220012);
+ /* DFLL */
+ regmap_write(rt5514->i2c_regmap, 0x18002124, 0xc0220012);
+ /* (I2S) i2s format, TDM 4ch */
+ regmap_write(rt5514->i2c_regmap, 0x18002010, 0x10000772);
+ /* (I2S) source sel; tdm_0=ad0, tdm_1=ad1 */
+ regmap_write(rt5514->i2c_regmap, 0x180020ac, 0x44000eee);
+ /* (ad0) source of DMIC */
+ regmap_write(rt5514->i2c_regmap, 0x18002190, 0x0002042f);
+ /* (ad0) source of DMIC */
+ regmap_write(rt5514->i2c_regmap, 0x18002194, 0x0002042f);
+ /* (ad0) DMIC-IN1 L/R select */
+ regmap_write(rt5514->i2c_regmap, 0x18002198, 0x10000362);
+ /* (ad1) source of DMIC */
+ regmap_write(rt5514->i2c_regmap, 0x180021a0, 0x0002042f);
+ /* (ad1) source of DMIC */
+ regmap_write(rt5514->i2c_regmap, 0x180021a4, 0x0002042f);
+ /* (ad1) DMIC-IN2 L/R select */
+ regmap_write(rt5514->i2c_regmap, 0x180021a8, 0x10000362);
+ /* (ad2) gain=24dB for WOV */
+ regmap_write(rt5514->i2c_regmap, 0x180020d0, 0x00008a2f);
+ /* dsp clk auto switch */
+ regmap_write(rt5514->i2c_regmap, 0x18001114, 0x00000001);
+ /* reduce DSP power */
+ regmap_write(rt5514->i2c_regmap, 0x18001118, 0x00000001);
+ /* UART clk=off */
+ regmap_write(rt5514->i2c_regmap, 0x18001104, 0x00000003);
+ /* (pitch VAD)fix1 */
+ regmap_write(rt5514->i2c_regmap, 0x1800201c, 0x69f32067);
+ /* (pitch VAD)fix2 */
+ regmap_write(rt5514->i2c_regmap, 0x18002020, 0x50d500a5);
+ /* (pitch VAD)fix3 */
+ regmap_write(rt5514->i2c_regmap, 0x18002024, 0x000a0206);
+ /* (pitch VAD)fix4 */
+ regmap_write(rt5514->i2c_regmap, 0x18002028, 0x01800114);
+ /* (hello VAD)fix1 */
+ regmap_write(rt5514->i2c_regmap, 0x18002038, 0x00100010);
+ /* (ok VAD)fix1 */
+ regmap_write(rt5514->i2c_regmap, 0x1800204c, 0x000503c8);
+ /* (ok VAD)fix2 */
+ regmap_write(rt5514->i2c_regmap, 0x18002050, 0x001a0308);
+ /* (ok VAD)fix3 */
+ regmap_write(rt5514->i2c_regmap, 0x18002054, 0x50020502);
+ /* (ok VAD)fix4 */
+ regmap_write(rt5514->i2c_regmap, 0x18002058, 0x50000d18);
+ /* (ok VAD)fix5 */
+ regmap_write(rt5514->i2c_regmap, 0x1800205c, 0x640c0b14);
+ /* (ok VAD)fix6 */
+ regmap_write(rt5514->i2c_regmap, 0x18002060, 0x00100001);
+ /* (FW) SENSORY_SVSCORE(for UDT+SID) */
+ regmap_write(rt5514->i2c_regmap, 0x18002fa4, 0x00000000);
+ /* (FW) SENSORY_THRS(for UDT+SID) */
+ regmap_write(rt5514->i2c_regmap, 0x18002fa8, 0x00000000);
+ /* (FW) DLY_BUF_LTC_OFFSET (for ok/hello VAD) */
+ regmap_write(rt5514->i2c_regmap, 0x18002fbc, 0x00000000);
+}
+
+static void rt5514_enable_dsp(struct rt5514_priv *rt5514)
+{
+ /* (FW) DRIVER_CTRL0: (1)VAD timeout[7:0]: unit = 0.1s
+ * (2)buffer mode[15:14]:
+ * 0 = key phrase+voice command
+ * 1 = voice command
+ * 2 = key phrase
+ */
+ regmap_write(rt5514->i2c_regmap, 0x18001028, 0x0000000a);
+ /* PU RCOSC, pll3 */
+ regmap_write(rt5514->i2c_regmap, 0x18002004, 0x00808b81);
+ /* PD ADC1/2 */
+ regmap_write(rt5514->i2c_regmap, 0x18002008, 0x00220000);
+ /* dsp stop */
+ regmap_write(rt5514->i2c_regmap, 0x18002f00, 0x00055149);
+ /* CLK, 256fs=/4,DMIC_CLK_OUT=/16(enable ad2) */
+ regmap_write(rt5514->i2c_regmap, 0x18002104, 0x14023541);
+ /* CLK, clk_sys=mux_out/1 */
+ regmap_write(rt5514->i2c_regmap, 0x18002108, 0x00000000);
+ /* (PATH) DMIC_IN1(ri)->ad2, ad2(db_PCM)->IB2 */
+ regmap_write(rt5514->i2c_regmap, 0x180020a4, 0x00808002);
+ /* DSP clk source=pll3, ENABLE DSP clk */
+ regmap_write(rt5514->i2c_regmap, 0x18002f08, 0x00000005);
+ /* (opt)VAD, clr and enable pitch/hello VAD
+ * (0x800007d2 for ok VAD, 0x800007d3 for pitch/hello VAD)
+ */
+ regmap_write(rt5514->i2c_regmap, 0x18002030, 0x800007d3);
+ /* (opt)VAD, clr and enable ok VAD
+ * (0x80000003 for ok VAD, 0x80000002 for pitch/hello VAD)
+ */
+ regmap_write(rt5514->i2c_regmap, 0x18002064, 0x80000002);
+ /* (opt)VAD type sel(..90h:hello, ..80h:pitch, ..A0h:ok) */
+ regmap_write(rt5514->i2c_regmap, 0x1800206c, 0x80000080);
+ /* dsp run */
+ regmap_write(rt5514->i2c_regmap, 0x18002f00, 0x00055148);
+ /* clear IRQ */
+ regmap_write(rt5514->i2c_regmap, 0x18002e04, 0x00000000);
+ /* (opt)VAD, release and enable pitch/hello VAD
+ * (0x7d2 for ok VAD, 0x7d3 for pitch/hello VAD)
+ */
+ regmap_write(rt5514->i2c_regmap, 0x18002030, 0x000007d3);
+ /* (opt)VAD, release and enable ok VAD
+ * (0x3 for ok VAD, 0x2 for pitch/hello VAD)
+ */
+ regmap_write(rt5514->i2c_regmap, 0x18002064, 0x00000002);
+ /* (opt)VAD, release and enable pitch/hello VAD
+ * (0x7d2 for ok VAD, 0x7d3 for pitch/hello VAD)
+ */
+ regmap_write(rt5514->i2c_regmap, 0x18002030, 0x000007d3);
+ /* (opt)VAD, release and enable ok VAD
+ * (0x3 for ok VAD, 0x2 for pitch/hello VAD)
+ */
+ regmap_write(rt5514->i2c_regmap, 0x18002064, 0x00000002);
+}
+
static bool rt5514_volatile_register(struct device *dev, unsigned int reg)
{
switch (reg) {
@@ -248,6 +404,64 @@ static const DECLARE_TLV_DB_RANGE(bst_tlv,
static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -17625, 375, 0);
+static int rt5514_dsp_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt5514_priv *rt5514 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = rt5514->dsp_mode;
+
+ return 0;
+}
+
+static int rt5514_dsp_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt5514_priv *rt5514 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_codec *codec = rt5514->codec;
+ const struct firmware *fw = NULL;
+
+ if (ucontrol->value.integer.value[0] == rt5514->dsp_mode)
+ return 0;
+
+ rt5514->dsp_mode = ucontrol->value.integer.value[0];
+
+ if (rt5514->dsp_mode) {
+ rt5514_enable_dsp_clock(rt5514);
+
+ request_firmware(&fw, "0x4ff60000.dat", codec->dev);
+ if (fw) {
+#if defined(CONFIG_SND_SOC_RT5514_SPI)
+ rt5514_spi_burst_write(0x4ff60000, fw->data,
+ ((fw->size/8)+1)*8);
+#endif
+ release_firmware(fw);
+ fw = NULL;
+ }
+
+ request_firmware(&fw, "0x4ffc0000.dat", codec->dev);
+ if (fw) {
+#if defined(CONFIG_SND_SOC_RT5514_SPI)
+ rt5514_spi_burst_write(0x4ffc0000, fw->data,
+ ((fw->size/8)+1)*8);
+#endif
+ release_firmware(fw);
+ fw = NULL;
+ }
+
+ rt5514_enable_dsp(rt5514);
+ } else {
+ regcache_mark_dirty(rt5514->i2c_regmap);
+ regcache_mark_dirty(rt5514->regmap);
+ regcache_sync(rt5514->i2c_regmap);
+ regcache_sync(rt5514->regmap);
+ }
+
+ return 0;
+}
+
static const struct snd_kcontrol_new rt5514_snd_controls[] = {
SOC_DOUBLE_TLV("MIC Boost Volume", RT5514_ANA_CTRL_MICBST,
RT5514_SEL_BSTL_SFT, RT5514_SEL_BSTR_SFT, 8, 0, bst_tlv),
@@ -257,6 +471,8 @@ static const struct snd_kcontrol_new rt5514_snd_controls[] = {
SOC_DOUBLE_R_TLV("ADC2 Capture Volume", RT5514_DOWNFILTER1_CTRL1,
RT5514_DOWNFILTER1_CTRL2, RT5514_AD_GAIN_SFT, 127, 0,
adc_vol_tlv),
+ SOC_SINGLE_EXT("DSP Control", SND_SOC_NOPM, 0, 1, 0,
+ rt5514_dsp_mode_get, rt5514_dsp_mode_put),
};
/* ADC Mixer*/
@@ -799,6 +1015,32 @@ static int rt5514_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
return 0;
}
+static int rt5514_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct rt5514_priv *rt5514 = snd_soc_codec_get_drvdata(codec);
+
+ switch (level) {
+ case SND_SOC_BIAS_PREPARE:
+ if (snd_soc_codec_get_bias_level(codec) ==
+ SND_SOC_BIAS_STANDBY) {
+ if (rt5514->dsp_mode) {
+ rt5514->dsp_mode = 0;
+ regcache_mark_dirty(rt5514->i2c_regmap);
+ regcache_mark_dirty(rt5514->regmap);
+ regcache_sync(rt5514->i2c_regmap);
+ regcache_sync(rt5514->regmap);
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
static int rt5514_probe(struct snd_soc_codec *codec)
{
struct rt5514_priv *rt5514 = snd_soc_codec_get_drvdata(codec);
@@ -857,6 +1099,7 @@ struct snd_soc_dai_driver rt5514_dai[] = {
static struct snd_soc_codec_driver soc_codec_dev_rt5514 = {
.probe = rt5514_probe,
+ .set_bias_level = rt5514_set_bias_level,
.idle_bias_off = true,
.controls = rt5514_snd_controls,
.num_controls = ARRAY_SIZE(rt5514_snd_controls),
@@ -247,6 +247,7 @@ struct rt5514_priv {
int pll_src;
int pll_in;
int pll_out;
+ int dsp_mode;
};
#endif /* __RT5514_H__ */
The patch adds the rt5514 SPI driver for loading the firmware of DSP and retrieving the voice data after the system is waked up by voice. Signed-off-by: Oder Chiou <oder_chiou@realtek.com> --- sound/soc/codecs/Kconfig | 6 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/rt5514-spi.c | 494 ++++++++++++++++++++++++++++++++++++++++++ sound/soc/codecs/rt5514-spi.h | 36 +++ sound/soc/codecs/rt5514.c | 243 +++++++++++++++++++++ sound/soc/codecs/rt5514.h | 1 + 6 files changed, 782 insertions(+) create mode 100644 sound/soc/codecs/rt5514-spi.c create mode 100644 sound/soc/codecs/rt5514-spi.h