summaryrefslogtreecommitdiff
path: root/sound/soc/starfive/pwmdac.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/starfive/pwmdac.c')
-rw-r--r--sound/soc/starfive/pwmdac.c863
1 files changed, 863 insertions, 0 deletions
diff --git a/sound/soc/starfive/pwmdac.c b/sound/soc/starfive/pwmdac.c
new file mode 100644
index 000000000000..853909cb0010
--- /dev/null
+++ b/sound/soc/starfive/pwmdac.c
@@ -0,0 +1,863 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PWMDAC driver for the StarFive JH7100 SoC
+ *
+ * Copyright (C) 2021 StarFive Technology Co., Ltd.
+ */
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/pm_runtime.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/dmaengine_pcm.h>
+#include "pwmdac.h"
+#include <linux/kthread.h>
+
+struct ct_pwmdac {
+ char *name;
+ unsigned int vals;
+};
+
+static const struct ct_pwmdac pwmdac_ct_shift_bit[] = {
+ { .name = "8bit", .vals = PWMDAC_SHIFT_8 },
+ { .name = "10bit", .vals = PWMDAC_SHIFT_10 }
+};
+
+static const struct ct_pwmdac pwmdac_ct_duty_cycle[] = {
+ { .name = "left", .vals = PWMDAC_CYCLE_LEFT },
+ { .name = "right", .vals = PWMDAC_CYCLE_RIGHT },
+ { .name = "center", .vals = PWMDAC_CYCLE_CENTER }
+};
+
+static const struct ct_pwmdac pwmdac_ct_data_mode[] = {
+ { .name = "unsinged", .vals = UNSINGED_DATA },
+ { .name = "inverter", .vals = INVERTER_DATA_MSB }
+};
+
+static const struct ct_pwmdac pwmdac_ct_lr_change[] = {
+ { .name = "no_change", .vals = NO_CHANGE },
+ { .name = "change", .vals = CHANGE }
+};
+
+static const struct ct_pwmdac pwmdac_ct_shift[] = {
+ { .name = "left 0 bit", .vals = PWMDAC_DATA_LEFT_SHIFT_BIT_0 },
+ { .name = "left 1 bit", .vals = PWMDAC_DATA_LEFT_SHIFT_BIT_1 },
+ { .name = "left 2 bit", .vals = PWMDAC_DATA_LEFT_SHIFT_BIT_2 },
+ { .name = "left 3 bit", .vals = PWMDAC_DATA_LEFT_SHIFT_BIT_3 },
+ { .name = "left 4 bit", .vals = PWMDAC_DATA_LEFT_SHIFT_BIT_4 },
+ { .name = "left 5 bit", .vals = PWMDAC_DATA_LEFT_SHIFT_BIT_5 },
+ { .name = "left 6 bit", .vals = PWMDAC_DATA_LEFT_SHIFT_BIT_6 }
+};
+
+static int pwmdac_shift_bit_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ unsigned int items = ARRAY_SIZE(pwmdac_ct_shift_bit);
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = items;
+ if (uinfo->value.enumerated.item >= items) {
+ uinfo->value.enumerated.item = items - 1;
+ }
+ strcpy(uinfo->value.enumerated.name,
+ pwmdac_ct_shift_bit[uinfo->value.enumerated.item].name);
+
+ return 0;
+}
+static int pwmdac_shift_bit_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct sf_pwmdac_dev *dev = snd_soc_component_get_drvdata(component);
+ unsigned int item;
+
+ if (dev->shift_bit == pwmdac_ct_shift_bit[0].vals)
+ item = 0;
+ else
+ item = 1;
+
+ ucontrol->value.enumerated.item[0] = item;
+
+ return 0;
+}
+
+static int pwmdac_shift_bit_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct sf_pwmdac_dev *dev = snd_soc_component_get_drvdata(component);
+ int sel = ucontrol->value.enumerated.item[0];
+ unsigned int items = ARRAY_SIZE(pwmdac_ct_shift_bit);
+
+ if (sel > items)
+ return 0;
+
+ switch (sel) {
+ case 1:
+ dev->shift_bit = pwmdac_ct_shift_bit[1].vals;
+ break;
+ default:
+ dev->shift_bit = pwmdac_ct_shift_bit[0].vals;
+ break;
+ }
+
+ return 0;
+}
+
+static int pwmdac_duty_cycle_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ unsigned int items = ARRAY_SIZE(pwmdac_ct_duty_cycle);
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = items;
+ if (uinfo->value.enumerated.item >= items)
+ uinfo->value.enumerated.item = items - 1;
+ strcpy(uinfo->value.enumerated.name,
+ pwmdac_ct_duty_cycle[uinfo->value.enumerated.item].name);
+
+ return 0;
+}
+
+static int pwmdac_duty_cycle_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct sf_pwmdac_dev *dev = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.enumerated.item[0] = dev->duty_cycle;
+ return 0;
+}
+
+static int pwmdac_duty_cycle_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct sf_pwmdac_dev *dev = snd_soc_component_get_drvdata(component);
+ int sel = ucontrol->value.enumerated.item[0];
+ unsigned int items = ARRAY_SIZE(pwmdac_ct_duty_cycle);
+
+ if (sel > items)
+ return 0;
+
+ dev->duty_cycle = pwmdac_ct_duty_cycle[sel].vals;
+ return 0;
+}
+
+/*
+static int pwmdac_datan_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 1;
+ uinfo->value.integer.max = PWMDAC_SAMPLE_CNT_511;
+ uinfo->value.integer.step = 1;
+ return 0;
+}
+
+static int pwmdac_datan_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct sf_pwmdac_dev *dev = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = dev->datan;
+
+ return 0;
+}
+
+static int pwmdac_datan_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct sf_pwmdac_dev *dev = snd_soc_component_get_drvdata(component);
+ int sel = ucontrol->value.integer.value[0];
+
+ if (sel > PWMDAC_SAMPLE_CNT_511)
+ return 0;
+
+ dev->datan = sel;
+
+ return 0;
+}
+*/
+
+static int pwmdac_data_mode_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ unsigned int items = ARRAY_SIZE(pwmdac_ct_data_mode);
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = items;
+ if (uinfo->value.enumerated.item >= items)
+ uinfo->value.enumerated.item = items - 1;
+ strcpy(uinfo->value.enumerated.name,
+ pwmdac_ct_data_mode[uinfo->value.enumerated.item].name);
+
+ return 0;
+}
+
+static int pwmdac_data_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct sf_pwmdac_dev *dev = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.enumerated.item[0] = dev->data_mode;
+ return 0;
+}
+
+static int pwmdac_data_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct sf_pwmdac_dev *dev = snd_soc_component_get_drvdata(component);
+ int sel = ucontrol->value.enumerated.item[0];
+ unsigned int items = ARRAY_SIZE(pwmdac_ct_data_mode);
+
+ if (sel > items)
+ return 0;
+
+ dev->data_mode = pwmdac_ct_data_mode[sel].vals;
+ return 0;
+}
+
+static int pwmdac_shift_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ unsigned int items = ARRAY_SIZE(pwmdac_ct_shift);
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = items;
+ if (uinfo->value.enumerated.item >= items)
+ uinfo->value.enumerated.item = items - 1;
+ strcpy(uinfo->value.enumerated.name,
+ pwmdac_ct_shift[uinfo->value.enumerated.item].name);
+
+ return 0;
+}
+
+static int pwmdac_shift_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct sf_pwmdac_dev *dev = snd_soc_component_get_drvdata(component);
+ unsigned int item = dev->shift;
+
+ ucontrol->value.enumerated.item[0] = pwmdac_ct_shift[item].vals;
+ return 0;
+}
+
+static int pwmdac_shift_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct sf_pwmdac_dev *dev = snd_soc_component_get_drvdata(component);
+ int sel = ucontrol->value.enumerated.item[0];
+ unsigned int items = ARRAY_SIZE(pwmdac_ct_shift);
+
+ if (sel > items)
+ return 0;
+
+ dev->shift = pwmdac_ct_shift[sel].vals;
+ return 0;
+}
+
+static int pwmdac_lr_change_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ unsigned int items = ARRAY_SIZE(pwmdac_ct_lr_change);
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = items;
+ if (uinfo->value.enumerated.item >= items)
+ uinfo->value.enumerated.item = items - 1;
+ strcpy(uinfo->value.enumerated.name,
+ pwmdac_ct_lr_change[uinfo->value.enumerated.item].name);
+
+ return 0;
+}
+
+static int pwmdac_lr_change_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct sf_pwmdac_dev *dev = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.enumerated.item[0] = dev->lr_change;
+ return 0;
+}
+
+static int pwmdac_lr_change_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct sf_pwmdac_dev *dev = snd_soc_component_get_drvdata(component);
+ int sel = ucontrol->value.enumerated.item[0];
+ unsigned int items = ARRAY_SIZE(pwmdac_ct_lr_change);
+
+ if (sel > items)
+ return 0;
+
+ dev->lr_change = pwmdac_ct_lr_change[sel].vals;
+ return 0;
+}
+
+static inline void pwmdc_write_reg(void __iomem *io_base, int reg, u32 val)
+{
+ writel(val, io_base + reg);
+}
+
+static inline u32 pwmdc_read_reg(void __iomem *io_base, int reg)
+{
+ return readl(io_base + reg);
+}
+
+/*
+ * 32bit-4byte
+*/
+static void pwmdac_set_ctrl_enable(struct sf_pwmdac_dev *dev)
+{
+ u32 date;
+ date = pwmdc_read_reg(dev->pwmdac_base, PWMDAC_CTRL);
+ pwmdc_write_reg(dev->pwmdac_base, PWMDAC_CTRL, date | BIT(0) );
+}
+
+/*
+ * 32bit-4byte
+*/
+static void pwmdac_set_ctrl_disable(struct sf_pwmdac_dev *dev)
+{
+ u32 date;
+ date = pwmdc_read_reg(dev->pwmdac_base, PWMDAC_CTRL);
+ pwmdc_write_reg(dev->pwmdac_base, PWMDAC_CTRL, date & ~ BIT(0));
+}
+
+/*
+ * 8:8-bit
+ * 10:10-bit
+*/
+static void pwmdac_set_ctrl_shift(struct sf_pwmdac_dev *dev, u8 data)
+{
+ u32 value = 0;
+
+ if (data == 8) {
+ value = (~((~value) | 0x02));
+ pwmdc_write_reg(dev->pwmdac_base , PWMDAC_CTRL, value);
+ }
+ else if(data == 10){
+ value |= 0x02;
+ pwmdc_write_reg(dev->pwmdac_base , PWMDAC_CTRL, value);
+ }
+}
+
+/*
+ * 00:left
+ * 01:right
+ * 10:center
+*/
+static void pwmdac_set_ctrl_dutyCycle(struct sf_pwmdac_dev *dev, u8 data)
+{
+ u32 value = 0;
+
+ value = pwmdc_read_reg(dev->pwmdac_base , PWMDAC_CTRL);
+ if (data == 0) { //left
+ value = (~((~value) | (0x03<<2)));
+ pwmdc_write_reg(dev->pwmdac_base , PWMDAC_CTRL, value);
+ }
+ else if (data == 1) { //right
+ value = (~((~value) | (0x01<<3))) | (0x01<<2);
+ pwmdc_write_reg(dev->pwmdac_base , PWMDAC_CTRL, value);
+ }
+ else if (data == 2) { //center
+ value = (~((~value) | (0x01<<2))) | (0x01<<3);
+ pwmdc_write_reg(dev->pwmdac_base , PWMDAC_CTRL, value);
+ }
+}
+
+
+static void pwmdac_set_ctrl_N(struct sf_pwmdac_dev *dev, u16 data)
+{
+ u32 value = 0;
+
+ value = pwmdc_read_reg(dev->pwmdac_base , PWMDAC_CTRL);
+ pwmdc_write_reg(dev->pwmdac_base , PWMDAC_CTRL, (value & 0xF) | ((data - 1) << 4));
+}
+
+
+static void pwmdac_LR_data_change(struct sf_pwmdac_dev *dev, u8 data)
+{
+ u32 value = 0;
+
+ value = pwmdc_read_reg(dev->pwmdac_base , PWMDAC_CTRL);
+ switch (data) {
+ case NO_CHANGE:
+ value &= (~SFC_PWMDAC_LEFT_RIGHT_DATA_CHANGE);
+ break;
+ case CHANGE:
+ value |= SFC_PWMDAC_LEFT_RIGHT_DATA_CHANGE;
+ break;
+ }
+ pwmdc_write_reg(dev->pwmdac_base, PWMDAC_CTRL, value);
+}
+
+static void pwmdac_data_mode(struct sf_pwmdac_dev *dev, u8 data)
+{
+ u32 value = 0;
+
+ value = pwmdc_read_reg(dev->pwmdac_base , PWMDAC_CTRL);
+ if (data == UNSINGED_DATA) {
+ value &= (~SFC_PWMDAC_DATA_MODE);
+ }
+ else if (data == INVERTER_DATA_MSB) {
+ value |= SFC_PWMDAC_DATA_MODE;
+ }
+ pwmdc_write_reg(dev->pwmdac_base,PWMDAC_CTRL, value);
+}
+
+static int pwmdac_data_shift(struct sf_pwmdac_dev *dev,u8 data)
+{
+ u32 value = 0;
+
+ if ((data < PWMDAC_DATA_LEFT_SHIFT_BIT_0) || (data > PWMDAC_DATA_LEFT_SHIFT_BIT_7)) {
+ return -1;
+ }
+
+ value = pwmdc_read_reg(dev->pwmdac_base , PWMDAC_CTRL);
+ value &= ( ~ ( PWMDAC_DATA_LEFT_SHIFT_BIT_ALL << 15 ) );
+ value |= (data<<15);
+ pwmdc_write_reg(dev->pwmdac_base , PWMDAC_CTRL, value);
+ return 0;
+}
+
+static int get_pwmdac_fifo_state(struct sf_pwmdac_dev *dev)
+{
+ u32 value;
+
+ value = pwmdc_read_reg(dev->pwmdac_base , PWMDAC_SATAE);
+ if ((value & 0x02) == 0)
+ return FIFO_UN_FULL;
+
+ return FIFO_FULL;
+}
+
+
+static void pwmdac_set(struct sf_pwmdac_dev *dev)
+{
+ ///8-bit + left + N=16
+ pwmdac_set_ctrl_shift(dev, dev->shift_bit);
+ pwmdac_set_ctrl_dutyCycle(dev, dev->duty_cycle);
+ pwmdac_set_ctrl_N(dev, dev->datan);
+ pwmdac_set_ctrl_enable(dev);
+
+ pwmdac_LR_data_change(dev, dev->lr_change);
+ pwmdac_data_mode(dev, dev->data_mode);
+ if (dev->shift) {
+ pwmdac_data_shift(dev, dev->shift);
+ }
+}
+
+static void pwmdac_stop(struct sf_pwmdac_dev *dev)
+{
+ pwmdac_set_ctrl_disable(dev);
+}
+
+static int pwmdac_config(struct sf_pwmdac_dev *dev)
+{
+ switch (dev->mode) {
+ case shift_8Bit_unsigned:
+ case shift_8Bit_unsigned_dataShift:
+ /* 8 bit, unsigned */
+ dev->shift_bit = PWMDAC_SHIFT_8;
+ dev->duty_cycle = PWMDAC_CYCLE_CENTER;
+ dev->datan = PWMDAC_SAMPLE_CNT_8;
+ dev->data_mode = UNSINGED_DATA;
+ break;
+
+ case shift_8Bit_inverter:
+ case shift_8Bit_inverter_dataShift:
+ /* 8 bit, invert */
+ dev->shift_bit = PWMDAC_SHIFT_8;
+ dev->duty_cycle = PWMDAC_CYCLE_CENTER;
+ dev->datan = PWMDAC_SAMPLE_CNT_8;
+ dev->data_mode = INVERTER_DATA_MSB;
+ break;
+
+ case shift_10Bit_unsigned:
+ case shift_10Bit_unsigned_dataShift:
+ /* 10 bit, unsigend */
+ dev->shift_bit = PWMDAC_SHIFT_10;
+ dev->duty_cycle = PWMDAC_CYCLE_CENTER;
+ dev->datan = PWMDAC_SAMPLE_CNT_8;
+ dev->data_mode = UNSINGED_DATA;
+ break;
+
+ case shift_10Bit_inverter:
+ case shift_10Bit_inverter_dataShift:
+ /* 10 bit, invert */
+ dev->shift_bit = PWMDAC_SHIFT_10;
+ dev->duty_cycle = PWMDAC_CYCLE_CENTER;
+ dev->datan = PWMDAC_SAMPLE_CNT_8;
+ dev->data_mode = INVERTER_DATA_MSB;
+ break;
+
+ default:
+ return -1;
+ }
+
+ if ((dev->mode == shift_8Bit_unsigned_dataShift) || (dev->mode == shift_8Bit_inverter_dataShift)
+ || (dev->mode == shift_10Bit_unsigned_dataShift) || (dev->mode == shift_10Bit_inverter_dataShift)) {
+ dev->shift = 4; /*0~7*/
+ } else {
+ dev->shift = 0;
+ }
+ dev->lr_change = NO_CHANGE;
+ return 0;
+}
+
+static int sf_pwmdac_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ //struct sf_pwmdac_dev *dev = snd_soc_dai_get_drvdata(dai);
+ //pwmdac_set(dev);
+ return 0;
+}
+
+static int pwmdac_tx_thread(void *dev)
+{
+ struct sf_pwmdac_dev *pwmdac_dev = (struct sf_pwmdac_dev *)dev;
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ while (!schedule_timeout(usecs_to_jiffies(50))) {
+ if (pwmdac_dev->tx_thread_exit)
+ break;
+ if (get_pwmdac_fifo_state(pwmdac_dev)==0) {
+ sf_pwmdac_pcm_push_tx(pwmdac_dev);
+ }
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ }
+
+ pwmdac_dev->tx_thread = NULL;
+ return 0;
+}
+
+static int sf_pwmdac_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ struct sf_pwmdac_dev *dev = snd_soc_dai_get_drvdata(dai);
+ int ret = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ dev->active++;
+ pwmdac_set(dev);
+ if (dev->use_pio) {
+ dev->tx_thread = kthread_create(pwmdac_tx_thread, (void *)dev, "pwmdac");
+ if (IS_ERR(dev->tx_thread)) {
+ return PTR_ERR(dev->tx_thread);
+ }
+ wake_up_process(dev->tx_thread);
+ dev->tx_thread_exit = 0;
+ }
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ dev->active--;
+ pwmdac_stop(dev);
+ if (dev->use_pio) {
+ if(dev->tx_thread) {
+ dev->tx_thread_exit = 1;
+ }
+ }
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+
+ return 0;
+}
+
+static int sf_pwmdac_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct sf_pwmdac_dev *dev = dev_get_drvdata(dai->dev);
+
+ dev->play_dma_data.addr = dev->mapbase + PWMDAC_WDATA;
+
+ switch (params_channels(params)) {
+ case 2:
+ dev->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ break;
+ case 1:
+ dev->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+ break;
+ default:
+ dev_err(dai->dev, "%d channels not supported\n",
+ params_channels(params));
+ return -EINVAL;
+ }
+
+ dev->play_dma_data.fifo_size = 1;
+ dev->play_dma_data.maxburst = 16;
+
+ snd_soc_dai_init_dma_data(dai, &dev->play_dma_data, NULL);
+ snd_soc_dai_set_drvdata(dai, dev);
+
+ return 0;
+}
+
+static int sf_pwmdac_clks_get(struct platform_device *pdev,
+ struct sf_pwmdac_dev *dev)
+{
+ static const char *const clock_names[PWMDAC_CLK_NUM] = {
+ [PWMDAC_CLK_AUDIO_ROOT] = "audio_root",
+ [PWMDAC_CLK_AUDIO_SRC] = "audio_src",
+ [PWMDAC_CLK_AUDIO_12288] = "audio_12288",
+ [PWMDAC_CLK_DMA1P_AHB] = "dma1p_ahb",
+ [PWMDAC_CLK_PWMDAC_APB] = "pwmdac_apb",
+ [PWMDAC_CLK_DAC_MCLK] = "dac_mclk",
+ };
+ int i;
+
+ for (i = 0; i < PWMDAC_CLK_NUM; i++)
+ dev->clk[i].id = clock_names[i];
+
+ return devm_clk_bulk_get(&pdev->dev, ARRAY_SIZE(dev->clk), dev->clk);
+}
+
+static int sf_pwmdac_resets_get(struct platform_device *pdev,
+ struct sf_pwmdac_dev *dev)
+{
+ static const char *const reset_names[PWMDAC_RST_NUM] = {
+ [PWMDAC_RST_APB_BUS] = "apb_bus",
+ [PWMDAC_RST_DMA1P_AHB] = "dma1p_ahb",
+ [PWMDAC_RST_APB_PWMDAC] = "apb_pwmdac",
+ };
+ int i;
+
+ for (i = 0; i < PWMDAC_RST_NUM; i++)
+ dev->rst[i].id = reset_names[i];
+
+ return devm_reset_control_bulk_get_exclusive(&pdev->dev, ARRAY_SIZE(dev->rst), dev->rst);
+}
+
+static int sf_pwmdac_clk_init(struct platform_device *pdev,
+ struct sf_pwmdac_dev *dev)
+{
+ int ret;
+ int i;
+
+ for (i = 0; i <= PWMDAC_CLK_DMA1P_AHB; i++) {
+ ret = clk_prepare_enable(dev->clk[i].clk);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret,
+ "failed to enable %s\n", dev->clk[i].id);
+ }
+
+ for (i = 0; i <= PWMDAC_RST_DMA1P_AHB; i++) {
+ ret = reset_control_deassert(dev->rst[i].rstc);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret,
+ "failed to deassert %s\n", dev->rst[i].id);
+ }
+
+ ret = clk_set_rate(dev->clk[PWMDAC_CLK_AUDIO_SRC].clk, 12288000);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret,
+ "failed to set 12.288 MHz rate for clk_audio_src\n");
+
+ ret = reset_control_assert(dev->rst[PWMDAC_RST_APB_PWMDAC].rstc);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "failed to assert apb_pwmdac\n");
+
+ ret = clk_prepare_enable(dev->clk[PWMDAC_CLK_DAC_MCLK].clk);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "failed to prepare enable clk_dac_mclk\n");
+
+ /* we want 4096kHz but the clock driver always rounds down so add a little slack */
+ ret = clk_set_rate(dev->clk[PWMDAC_CLK_DAC_MCLK].clk, 4096000 + 64);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "failed to set 4096kHz rate for clk_dac_mclk\n");
+
+ ret = clk_prepare_enable(dev->clk[PWMDAC_CLK_PWMDAC_APB].clk);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "failed to prepare enable clk_pwmdac_apb\n");
+
+ ret = reset_control_deassert(dev->rst[PWMDAC_RST_APB_PWMDAC].rstc);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "failed to deassert apb_pwmdac\n");
+
+ return 0;
+}
+
+#define SOC_PWMDAC_ENUM_DECL(xname, xinfo, xget, xput) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = xinfo, .get = xget, \
+ .put = xput,}
+static const struct snd_kcontrol_new pwmdac_snd_controls[] = {
+ SOC_PWMDAC_ENUM_DECL("shift_bit", pwmdac_shift_bit_info,
+ pwmdac_shift_bit_get, pwmdac_shift_bit_put),
+ SOC_PWMDAC_ENUM_DECL("duty_cycle", pwmdac_duty_cycle_info,
+ pwmdac_duty_cycle_get, pwmdac_duty_cycle_put),
+ SOC_PWMDAC_ENUM_DECL("data_mode", pwmdac_data_mode_info,
+ pwmdac_data_mode_get, pwmdac_data_mode_put),
+ SOC_PWMDAC_ENUM_DECL("shift", pwmdac_shift_info,
+ pwmdac_shift_get, pwmdac_shift_put),
+ SOC_PWMDAC_ENUM_DECL("lr_change", pwmdac_lr_change_info,
+ pwmdac_lr_change_get, pwmdac_lr_change_put),
+};
+
+static int pwmdac_probe(struct snd_soc_component *component)
+{
+// struct sf_pwmdac_dev *priv = snd_soc_component_get_drvdata(component);
+ snd_soc_add_component_controls(component, pwmdac_snd_controls,
+ ARRAY_SIZE(pwmdac_snd_controls));
+ return 0;
+}
+
+static const struct snd_soc_component_driver sf_pwmdac_component = {
+ .name = "sf-pwmdac",
+ .probe = pwmdac_probe,
+};
+
+static int sf_pwmdac_dai_probe(struct snd_soc_dai *dai)
+{
+ struct sf_pwmdac_dev *dev = dev_get_drvdata(dai->dev);
+
+ dev->play_dma_data.addr = dev->mapbase + PWMDAC_WDATA;
+ dev->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ dev->play_dma_data.fifo_size = 1;
+ dev->play_dma_data.maxburst = 16;
+
+ snd_soc_dai_init_dma_data(dai, &dev->play_dma_data, NULL);
+ snd_soc_dai_set_drvdata(dai, dev);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops sf_pwmdac_dai_ops = {
+ .probe = sf_pwmdac_dai_probe,
+ .hw_params = sf_pwmdac_hw_params,
+ .prepare = sf_pwmdac_prepare,
+ .trigger = sf_pwmdac_trigger,
+};
+
+static struct snd_soc_dai_driver pwmdac_dai = {
+ .name = "pwmdac",
+ .id = 0,
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &sf_pwmdac_dai_ops,
+};
+
+static int sf_pwmdac_probe(struct platform_device *pdev)
+{
+ struct sf_pwmdac_dev *dev;
+ struct resource *res;
+ int ret;
+
+ dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ dev->mapbase = res->start;
+ dev->pwmdac_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(dev->pwmdac_base))
+ return PTR_ERR(dev->pwmdac_base);
+
+ ret = sf_pwmdac_clks_get(pdev, dev);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to get audio clock\n");
+ return ret;
+ }
+
+ ret = sf_pwmdac_resets_get(pdev, dev);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to get audio reset controls\n");
+ return ret;
+ }
+
+ ret = sf_pwmdac_clk_init(pdev, dev);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to enable audio clock\n");
+ return ret;
+ }
+
+ dev->dev = &pdev->dev;
+ dev->mode = shift_8Bit_inverter;
+ dev->fifo_th = 1;//8byte
+ pwmdac_config(dev);
+
+ dev->use_pio = false;
+ dev_set_drvdata(&pdev->dev, dev);
+ ret = devm_snd_soc_register_component(&pdev->dev, &sf_pwmdac_component,
+ &pwmdac_dai, 1);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "not able to register dai\n");
+ return ret;
+ }
+
+ if (dev->use_pio) {
+ ret = sf_pwmdac_pcm_register(pdev);
+ } else {
+ ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL,
+ 0);
+ }
+ return 0;
+}
+
+
+static int sf_pwmdac_remove(struct platform_device *pdev)
+{
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id sf_pwmdac_of_match[] = {
+ { .compatible = "starfive,pwmdac", },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, sf_pwmdac_of_match);
+#endif
+
+
+static struct platform_driver sf_pwmdac_driver = {
+ .probe = sf_pwmdac_probe,
+ .remove = sf_pwmdac_remove,
+ .driver = {
+ .name = "sf-pwmdac",
+ .of_match_table = of_match_ptr(sf_pwmdac_of_match),
+ },
+};
+
+module_platform_driver(sf_pwmdac_driver);
+
+MODULE_AUTHOR("jenny.zhang <jenny.zhang@starfivetech.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("starfive pwmdac SoC Interface");
+MODULE_ALIAS("platform:starfive-pwmdac");