diff options
Diffstat (limited to 'sound/soc/starfive/jh7110_pwmdac.c')
-rw-r--r-- | sound/soc/starfive/jh7110_pwmdac.c | 954 |
1 files changed, 954 insertions, 0 deletions
diff --git a/sound/soc/starfive/jh7110_pwmdac.c b/sound/soc/starfive/jh7110_pwmdac.c new file mode 100644 index 000000000000..0d4ad0602663 --- /dev/null +++ b/sound/soc/starfive/jh7110_pwmdac.c @@ -0,0 +1,954 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * PWMDAC driver for the StarFive JH7110 SoC + * + * Copyright (C) 2022 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 <linux/kthread.h> +#include <linux/reset.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/dmaengine_pcm.h> +#include "jh7110_pwmdac.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_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 == PWMDAC_SHIFT_8) { + value = (~((~value) | SFC_PWMDAC_SHIFT)); + pwmdc_write_reg(dev->pwmdac_base, PWMDAC_CTRL, value); + } else if (data == PWMDAC_SHIFT_10) { + value |= SFC_PWMDAC_SHIFT; + 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 = pwmdc_read_reg(dev->pwmdac_base, PWMDAC_CTRL); + + if (data == PWMDAC_CYCLE_LEFT) { + value = (~((~value) | (0x03<<PWMDAC_DUTY_CYCLE_LOW))); + pwmdc_write_reg(dev->pwmdac_base, PWMDAC_CTRL, value); + } else if (data == PWMDAC_CYCLE_RIGHT) { + value = (~((~value) | (0x01<<PWMDAC_DUTY_CYCLE_HIGH))) | + (0x01<<PWMDAC_DUTY_CYCLE_LOW); + pwmdc_write_reg(dev->pwmdac_base, PWMDAC_CTRL, value); + } else if (data == PWMDAC_CYCLE_CENTER) { + value = (~((~value) | (0x01<<PWMDAC_DUTY_CYCLE_LOW))) | + (0x01<<PWMDAC_DUTY_CYCLE_HIGH); + pwmdc_write_reg(dev->pwmdac_base, PWMDAC_CTRL, value); + } +} + +static void pwmdac_set_ctrl_N(struct sf_pwmdac_dev *dev, u16 data) +{ + u32 value = pwmdc_read_reg(dev->pwmdac_base, PWMDAC_CTRL); + + pwmdc_write_reg(dev->pwmdac_base, PWMDAC_CTRL, + (value & PWMDAC_CTRL_DATA_MASK) | ((data - 1) << PWMDAC_CTRL_DATA_SHIFT)); +} + +static void pwmdac_LR_data_change(struct sf_pwmdac_dev *dev, u8 data) +{ + u32 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 = 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; + + 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 << PWMDAC_DATA_LEFT_SHIFT)); + value |= (data<<PWMDAC_DATA_LEFT_SHIFT); + pwmdc_write_reg(dev->pwmdac_base, PWMDAC_CTRL, value); + return 0; +} + +static int get_pwmdac_fifo_state(struct sf_pwmdac_dev *dev) +{ + u32 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) +{ + return 0; +} + +int pwmdac_tx_thread(void *dev) +{ + struct sf_pwmdac_dev *pwmdac_dev = (struct sf_pwmdac_dev *)dev; + + if (!pwmdac_dev) { + dev_err(pwmdac_dev->dev, "%s L.%d dev is null.\n", __FILE__, __LINE__); + return -1; + } + + while (!pwmdac_dev->tx_thread_exit) { + if (get_pwmdac_fifo_state(pwmdac_dev) == 0) + sf_pwmdac_pcm_push_tx(pwmdac_dev); + else + udelay(100); + } + + 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; +} + +static int sf_pwmdac_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_dai_link *dai_link = rtd->dai_link; + + dai_link->stop_dma_first = 1; + + 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); + int ret; + unsigned long mclk_dac_value; + + dev->play_dma_data.addr = dev->mapbase + PWMDAC_WDATA; + + switch (params_rate(params)) { + case 8000: + dev->datan = PWMDAC_SAMPLE_CNT_3; + mclk_dac_value = 6144000; + break; + case 11025: + dev->datan = PWMDAC_SAMPLE_CNT_2; + mclk_dac_value = 5644800; + break; + case 16000: + dev->datan = PWMDAC_SAMPLE_CNT_3; + mclk_dac_value = 12288000; + break; + case 22050: + dev->datan = PWMDAC_SAMPLE_CNT_1; + mclk_dac_value = 5644800; + break; + case 32000: + dev->datan = PWMDAC_SAMPLE_CNT_1; + mclk_dac_value = 8192000; + break; + case 44100: + dev->datan = PWMDAC_SAMPLE_CNT_1; + mclk_dac_value = 11289600; + break; + case 48000: + dev->datan = PWMDAC_SAMPLE_CNT_1; + mclk_dac_value = 12288000; + break; + default: + dev_err(dai->dev, "%d rate not supported\n", + params_rate(params)); + return -EINVAL; + } + + 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; + } + + /* The mclock for the clock driver always rounds down so add a little slack */ + mclk_dac_value = mclk_dac_value + 64; + pwmdac_set(dev); + + ret = clk_set_rate(dev->clk_pwmdac_core, mclk_dac_value); + if (ret) { + dev_err(dai->dev, "failed to set rate for clk_pwmdac_core %lu\n", mclk_dac_value); + goto err_clk_pwmdac; + } + + 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; + +err_clk_pwmdac: + return ret; +} + +static int sf_pwmdac_clks_get(struct platform_device *pdev, + struct sf_pwmdac_dev *dev) +{ + dev->clk_apb0 = devm_clk_get(&pdev->dev, "apb0"); + if (IS_ERR(dev->clk_apb0)) + return PTR_ERR(dev->clk_apb0); + + dev->clk_pwmdac_apb = devm_clk_get(&pdev->dev, "apb"); + if (IS_ERR(dev->clk_pwmdac_apb)) + return PTR_ERR(dev->clk_pwmdac_apb); + + dev->clk_pwmdac_core = devm_clk_get(&pdev->dev, "core"); + if (IS_ERR(dev->clk_pwmdac_core)) + return PTR_ERR(dev->clk_pwmdac_core); + + return 0; +} + +static int sf_pwmdac_resets_get(struct platform_device *pdev, + struct sf_pwmdac_dev *dev) +{ + dev->rst_apb = devm_reset_control_get_exclusive(&pdev->dev, "apb"); + if (IS_ERR(dev->rst_apb)) { + dev_err(&pdev->dev, "%s: failed to get pwmdac apb reset control\n", __func__); + return PTR_ERR(dev->rst_apb); + } + + return 0; +} + +static int starfive_pwmdac_crg_enable(struct sf_pwmdac_dev *dev, bool enable) +{ + int ret = 0; + + dev_dbg(dev->dev, "starfive_pwmdac clk&rst %sable.\n", enable ? "en":"dis"); + if (enable) { + ret = clk_prepare_enable(dev->clk_apb0); + if (ret) { + dev_err(dev->dev, "failed to prepare enable clk_apb0\n"); + goto err_clk_apb0; + } + + ret = clk_prepare_enable(dev->clk_pwmdac_apb); + if (ret) { + dev_err(dev->dev, "failed to prepare enable clk_pwmdac_apb\n"); + goto err_clk_apb; + } + + ret = clk_prepare_enable(dev->clk_pwmdac_core); + if (ret) { + dev_err(dev->dev, "failed to prepare enable clk_pwmdac_core\n"); + goto err_clk_core; + } + + ret = reset_control_deassert(dev->rst_apb); + if (ret) { + dev_err(dev->dev, "failed to deassert apb\n"); + goto err_rst_apb; + } + } else { + clk_disable_unprepare(dev->clk_pwmdac_core); + clk_disable_unprepare(dev->clk_pwmdac_apb); + clk_disable_unprepare(dev->clk_apb0); + } + + return 0; + +err_rst_apb: + clk_disable_unprepare(dev->clk_pwmdac_core); +err_clk_core: + clk_disable_unprepare(dev->clk_pwmdac_apb); +err_clk_apb: + clk_disable_unprepare(dev->clk_apb0); +err_clk_apb0: + return ret; +} + +static int sf_pwmdac_clk_init(struct platform_device *pdev, + struct sf_pwmdac_dev *dev) +{ + int ret = 0; + + ret = starfive_pwmdac_crg_enable(dev, true); + if (ret) + goto err_clk_pwmdac; + + ret = clk_set_rate(dev->clk_pwmdac_core, 4096000); + if (ret) { + dev_err(&pdev->dev, "failed to set rate for clk_pwmdac_core\n"); + goto err_clk_pwmdac; + } + + dev_info(&pdev->dev, "clk_apb0 = %lu, clk_pwmdac_apb = %lu, clk_pwmdac_core = %lu\n", + clk_get_rate(dev->clk_apb0), clk_get_rate(dev->clk_pwmdac_apb), + clk_get_rate(dev->clk_pwmdac_core)); + +err_clk_pwmdac: + return ret; +} + +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; +} + +#ifdef CONFIG_PM_SLEEP +static int starfive_pwmdac_system_suspend(struct device *dev) +{ + struct sf_pwmdac_dev *pwmdac = dev_get_drvdata(dev); + + /* save the register value */ + pwmdac->pwmdac_ctrl_data = pwmdc_read_reg(pwmdac->pwmdac_base, PWMDAC_CTRL); + return pm_runtime_force_suspend(dev); +} + +static int starfive_pwmdac_system_resume(struct device *dev) +{ + struct sf_pwmdac_dev *pwmdac = dev_get_drvdata(dev); + int ret; + + ret = pm_runtime_force_resume(dev); + if (ret) + return ret; + + /* restore the register value */ + pwmdc_write_reg(pwmdac->pwmdac_base, PWMDAC_CTRL, pwmdac->pwmdac_ctrl_data); + return 0; +} +#endif + +#ifdef CONFIG_PM +static int starfive_pwmdac_runtime_suspend(struct device *dev) +{ + struct sf_pwmdac_dev *pwmdac = dev_get_drvdata(dev); + + return starfive_pwmdac_crg_enable(pwmdac, false); +} + +static int starfive_pwmdac_runtime_resume(struct device *dev) +{ + struct sf_pwmdac_dev *pwmdac = dev_get_drvdata(dev); + + return starfive_pwmdac_crg_enable(pwmdac, true); +} +#endif + +static const struct dev_pm_ops starfive_pwmdac_pm_ops = { + SET_RUNTIME_PM_OPS(starfive_pwmdac_runtime_suspend, starfive_pwmdac_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(starfive_pwmdac_system_suspend, starfive_pwmdac_system_resume) +}; + +#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) +{ + snd_soc_add_component_controls(component, pwmdac_snd_controls, + ARRAY_SIZE(pwmdac_snd_controls)); + + return 0; +} + +static const struct snd_soc_dai_ops sf_pwmdac_dai_ops = { + .startup = sf_pwmdac_startup, + .hw_params = sf_pwmdac_hw_params, + .prepare = sf_pwmdac_prepare, + .trigger = sf_pwmdac_trigger, +}; + +static const struct snd_soc_component_driver sf_pwmdac_component = { + .name = "starfive-pwmdac", + .probe = pwmdac_probe, +}; + +static struct snd_soc_dai_driver pwmdac_dai = { + .name = "pwmdac", + .id = 0, + .probe = sf_pwmdac_dai_probe, + .playback = { + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .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; + + dev->pwmdac_base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(dev->pwmdac_base)) + return PTR_ERR(dev->pwmdac_base); + + dev->mapbase = res->start; + dev->dev = &pdev->dev; + + ret = sf_pwmdac_clks_get(pdev, dev); + if (ret) { + dev_err(&pdev->dev, "failed to get pwmdac clock\n"); + return ret; + } + + ret = sf_pwmdac_resets_get(pdev, dev); + if (ret) { + dev_err(&pdev->dev, "failed to get pwmdac reset controls\n"); + return ret; + } + + ret = sf_pwmdac_clk_init(pdev, dev); + if (ret) { + dev_err(&pdev->dev, "failed to enable pwmdac clock\n"); + return ret; + } + + 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); + +#ifdef CONFIG_PM + starfive_pwmdac_crg_enable(dev, false); +#endif + + pm_runtime_enable(dev->dev); + + 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,jh7110-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 = "starfive-pwmdac", + .of_match_table = of_match_ptr(sf_pwmdac_of_match), + .pm = &starfive_pwmdac_pm_ops, + }, +}; + +static int __init pwmdac_driver_init(void) +{ + return platform_driver_register(&sf_pwmdac_driver); +} + +static void pwmdac_driver_exit(void) +{ + platform_driver_unregister(&sf_pwmdac_driver); +} + +late_initcall(pwmdac_driver_init); +module_exit(pwmdac_driver_exit); + +MODULE_AUTHOR("curry.zhang <curry.zhang@starfivetech.com>"); +MODULE_AUTHOR("Xingyu Wu <xingyu.wu@starfivetech.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("starfive pwmdac SoC Interface"); +MODULE_ALIAS("platform:starfive-pwmdac"); |