diff -drupN a/sound/soc/sunxi/sunxi-spdif.c b/sound/soc/sunxi/sunxi-spdif.c --- a/sound/soc/sunxi/sunxi-spdif.c 1970-01-01 03:00:00.000000000 +0300 +++ b/sound/soc/sunxi/sunxi-spdif.c 2022-06-12 05:28:14.000000000 +0300 @@ -0,0 +1,1061 @@ +/* + * sound\soc\sunxi\sunxi-spdif.c + * (C) Copyright 2014-2016 + * allwinnertech Technology Co., Ltd. + * wolfgang huang + * + * some simple description for this code + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sunxi-spdif.h" +#include "sunxi-pcm.h" + +#define DRV_NAME "sunxi-spdif" + +struct sunxi_spdif_info { + struct device *dev; + void __iomem *membase; + struct regmap *regmap; + struct mutex mutex; + struct clk *pllclk; +#ifdef SPDIF_PLL_AUDIO_X4 + struct clk *pllclkx4; +#endif + struct clk *moduleclk; + struct snd_soc_dai_driver dai; + struct sunxi_dma_params playback_dma_param; + struct sunxi_dma_params capture_dma_param; + struct pinctrl *pinctrl; + struct pinctrl_state *pinstate; +#ifdef SPDIF_PINCTRL_STATE_DEFAULT_B + struct pinctrl_state *pinstate_b; +#endif + struct pinctrl_state *pinstate_sleep; + unsigned int rate; + unsigned int active; + bool configured; +}; + +struct sample_rate { + unsigned int samplerate; + unsigned int rate_bit; +}; + + +struct spdif_gpio_ { + u32 gpio; + bool cfg; +}; +struct spdif_gpio_ spdif_gpio; + +/* Origin freq convert */ +static const struct sample_rate sample_rate_orig[] = { + {44100, 0xF}, + {48000, 0xD}, + {24000, 0x9}, + {32000, 0xC}, + {96000, 0x5}, + {192000, 0x1}, + {22050, 0xB}, + {88200, 0x7}, + {176400, 0x3}, +}; + +static const struct sample_rate sample_rate_freq[] = { + {44100, 0x0}, + {48000, 0x2}, + {24000, 0x6}, + {32000, 0x3}, + {96000, 0xA}, + {192000, 0xE}, + {22050, 0x4}, + {88200, 0x8}, + {176400, 0xC}, +}; + + +static int sunxi_spdif_set_audio_mode(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_component_to_codec(component); + struct sunxi_spdif_info *sunxi_spdif = snd_soc_codec_get_drvdata(codec); + unsigned int reg_val; + + regmap_read(sunxi_spdif->regmap, SUNXI_SPDIF_TXCH_STA0, ®_val); + + switch (ucontrol->value.integer.value[0]) { + case 0: + case 1: + reg_val = 0; + break; + case 2: + reg_val = 1; + break; + default: + return -EINVAL; + } + + regmap_update_bits(sunxi_spdif->regmap, SUNXI_SPDIF_TXCFG, + (1<regmap, SUNXI_SPDIF_TXCH_STA0, + (1<regmap, SUNXI_SPDIF_RXCH_STA0, + (1<regmap, SUNXI_SPDIF_TXCFG, ®_val); + reg_val = (reg_val & (1<value.integer.value[0] = reg_val + 1; + return 0; +} + +static const char *spdif_format_function[] = {"null", "pcm", "DTS"}; +static const struct soc_enum spdif_format_enum[] = { + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(spdif_format_function), + spdif_format_function), +}; + + +static int sunxi_spdif_get_hub_mode(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_component_to_codec(component); + struct sunxi_spdif_info *sunxi_spdif = snd_soc_codec_get_drvdata(codec); + unsigned int reg_val; + + regmap_read(sunxi_spdif->regmap, SUNXI_SPDIF_FIFO_CTL, ®_val); + + ucontrol->value.integer.value[0] = + ((reg_val & (1<value.integer.value[0]) { + case 0: + case 1: + regmap_update_bits(sunxi_spdif->regmap, SUNXI_SPDIF_FIFO_CTL, + (1<regmap, SUNXI_SPDIF_TXCFG, + (1<regmap, SUNXI_SPDIF_FIFO_CTL, + (1<regmap, SUNXI_SPDIF_TXCFG, + (1<regmap, SUNXI_SPDIF_TXCFG, + (1<regmap, SUNXI_SPDIF_INT, + (1<regmap, SUNXI_SPDIF_TXCFG, + (1<regmap, SUNXI_SPDIF_INT, + (1<regmap, SUNXI_SPDIF_INT, + (1<regmap, SUNXI_SPDIF_RXCFG, + (1<regmap, SUNXI_SPDIF_RXCFG, + (1<regmap, SUNXI_SPDIF_INT, + (1<regmap, SUNXI_SPDIF_FIFO_CTL, + (CTL_TXTL_MASK << FIFO_CTL_TXTL), + (CTL_TXTL_DEFAULT << FIFO_CTL_TXTL)); + regmap_update_bits(sunxi_spdif->regmap, SUNXI_SPDIF_FIFO_CTL, + (CTL_RXTL_MASK << FIFO_CTL_RXTL), + (CTL_RXTL_DEFAULT << FIFO_CTL_RXTL)); + + regmap_write(sunxi_spdif->regmap, SUNXI_SPDIF_TXCH_STA0, + 0x2 << TXCHSTA0_CHNUM); + regmap_write(sunxi_spdif->regmap, SUNXI_SPDIF_RXCH_STA0, + 0x2 << RXCHSTA0_CHNUM); +} + +static int sunxi_spdif_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct sunxi_spdif_info *sunxi_spdif = snd_soc_dai_get_drvdata(dai); + unsigned int reg_temp; + unsigned int tx_input_mode = 0; + unsigned int rx_output_mode = 0; + unsigned int i; + unsigned int origin_freq_bit = 0, sample_freq_bit = 0; + + /* two substream should be warking on same samplerate */ + mutex_lock(&sunxi_spdif->mutex); + if (sunxi_spdif->active > 1) { + if (params_rate(params) != sunxi_spdif->rate) { + mutex_unlock(&sunxi_spdif->mutex); + return -EINVAL; + } + } + mutex_unlock(&sunxi_spdif->mutex); + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + reg_temp = 0; + tx_input_mode = 1; + rx_output_mode = 3; + break; + case SNDRV_PCM_FORMAT_S20_3LE: + reg_temp = 1; + tx_input_mode = 0; + rx_output_mode = 0; + break; + case SNDRV_PCM_FORMAT_S32_LE: + /* only for the compatible of tinyalsa */ + case SNDRV_PCM_FORMAT_S24_LE: + reg_temp = 2; + tx_input_mode = 0; + rx_output_mode = 0; + break; + default: + pr_err("[sunxi-spdif] params_format[%d] error!\n", + params_format(params)); + return -EINVAL; + } + + for (i = 0; i < ARRAY_SIZE(sample_rate_orig); i++) { + if (params_rate(params) == sample_rate_orig[i].samplerate) { + origin_freq_bit = sample_rate_orig[i].rate_bit; + break; + } + } + + for (i = 0; i < ARRAY_SIZE(sample_rate_freq); i++) { + if (params_rate(params) == sample_rate_freq[i].samplerate) { + sample_freq_bit = sample_rate_freq[i].rate_bit; + sunxi_spdif->rate = sample_rate_freq[i].samplerate; + break; + } + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + regmap_update_bits(sunxi_spdif->regmap, SUNXI_SPDIF_TXCFG, + (3<regmap, SUNXI_SPDIF_FIFO_CTL, + (1<regmap, + SUNXI_SPDIF_TXCFG, (1<regmap, + SUNXI_SPDIF_TXCFG, (1<regmap, SUNXI_SPDIF_TXCH_STA0, + (0xF<regmap, SUNXI_SPDIF_TXCH_STA1, + (0xF<regmap, + SUNXI_SPDIF_TXCH_STA1, + (0xF<regmap, + SUNXI_SPDIF_TXCH_STA1, + (0xF<regmap, + SUNXI_SPDIF_TXCH_STA1, + (0xF<regmap, SUNXI_SPDIF_FIFO_CTL, + (0x3 << FIFO_CTL_RXOM), + (rx_output_mode << FIFO_CTL_RXOM)); + regmap_update_bits(sunxi_spdif->regmap, SUNXI_SPDIF_RXCH_STA0, + (0xF<regmap, SUNXI_SPDIF_RXCH_STA1, + (0xF<regmap, + SUNXI_SPDIF_RXCH_STA1, + (0xF<regmap, + SUNXI_SPDIF_RXCH_STA1, + (0xF<regmap, + SUNXI_SPDIF_RXCH_STA1, + (0xF<mutex); + if (sunxi_spdif->active == 0) { + pr_debug("active: %u\n", sunxi_spdif->active); + if (clk_set_rate(sunxi_spdif->pllclk, freq)) { + dev_err(sunxi_spdif->dev, + "pllclk set rate to %uHz failed\n", freq); + mutex_unlock(&sunxi_spdif->mutex); + return -EBUSY; + } + } + sunxi_spdif->active++; + mutex_unlock(&sunxi_spdif->mutex); + pr_debug("End %s\n", __func__); + return 0; +} + +static int sunxi_spdif_dai_set_clkdiv(struct snd_soc_dai *dai, + int clk_id, int clk_div) +{ + struct sunxi_spdif_info *sunxi_spdif = snd_soc_dai_get_drvdata(dai); + + pr_debug("Enter %s\n", __func__); + + mutex_lock(&sunxi_spdif->mutex); + if (sunxi_spdif->configured == false) { + switch (clk_id) { + case 0: + regmap_update_bits(sunxi_spdif->regmap, + SUNXI_SPDIF_TXCFG, + (0x1F<configured = true; + mutex_unlock(&sunxi_spdif->mutex); + + pr_debug("End %s\n", __func__); + + return 0; +} + +static int sunxi_spdif_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct sunxi_spdif_info *sunxi_spdif = snd_soc_dai_get_drvdata(dai); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + snd_soc_dai_set_dma_data(dai, substream, + &sunxi_spdif->playback_dma_param); + else + snd_soc_dai_set_dma_data(dai, substream, + &sunxi_spdif->capture_dma_param); + + return 0; +} + +static void sunxi_spdif_dai_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct sunxi_spdif_info *sunxi_spdif = snd_soc_dai_get_drvdata(dai); + + mutex_lock(&sunxi_spdif->mutex); + if (sunxi_spdif->active) { + sunxi_spdif->active--; + if (sunxi_spdif->active == 0) + sunxi_spdif->configured = false; + } + mutex_unlock(&sunxi_spdif->mutex); +} + +static int sunxi_spdif_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *dai) +{ + struct sunxi_spdif_info *sunxi_spdif = 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: + if (spdif_gpio.cfg) + gpio_set_value(spdif_gpio.gpio, 1); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + sunxi_spdif_txctrl_enable(sunxi_spdif, 1); + else + sunxi_spdif_rxctrl_enable(sunxi_spdif, 1); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + sunxi_spdif_txctrl_enable(sunxi_spdif, 0); + else + sunxi_spdif_rxctrl_enable(sunxi_spdif, 0); + if (spdif_gpio.cfg) + gpio_set_value(spdif_gpio.gpio, 0); + break; + default: + ret = -EINVAL; + } + + return ret; +} + +/* Flush FIFO & Interrupt */ +static int sunxi_spdif_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct sunxi_spdif_info *sunxi_spdif = snd_soc_dai_get_drvdata(dai); + unsigned int reg_val; + + /*as you need to clean up TX or RX FIFO , need to turn off GEN bit*/ + regmap_update_bits(sunxi_spdif->regmap, SUNXI_SPDIF_CTL, + (1 << CTL_GEN_EN), (0 << CTL_GEN_EN)); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + regmap_update_bits(sunxi_spdif->regmap, + SUNXI_SPDIF_FIFO_CTL, + (1<regmap, SUNXI_SPDIF_TXCNT, 0); + } else { + regmap_update_bits(sunxi_spdif->regmap, SUNXI_SPDIF_FIFO_CTL, + (1<regmap, SUNXI_SPDIF_RXCNT, 0); + } + + /* clear all interrupt status */ + regmap_read(sunxi_spdif->regmap, SUNXI_SPDIF_INT_STA, ®_val); + regmap_write(sunxi_spdif->regmap, SUNXI_SPDIF_INT_STA, reg_val); + + /* need reset */ + regmap_update_bits(sunxi_spdif->regmap, SUNXI_SPDIF_CTL, + (1 << CTL_RESET), (1 << CTL_RESET)); + regmap_update_bits(sunxi_spdif->regmap, SUNXI_SPDIF_CTL, + (1 << CTL_GEN_EN), (1 << CTL_GEN_EN)); + + return 0; +} + +static int sunxi_spdif_probe(struct snd_soc_dai *dai) +{ + struct sunxi_spdif_info *sunxi_spdif = snd_soc_dai_get_drvdata(dai); + + mutex_init(&sunxi_spdif->mutex); + + sunxi_spdif_init(sunxi_spdif); + regmap_update_bits(sunxi_spdif->regmap, SUNXI_SPDIF_CTL, + (1<regmap, SUNXI_SPDIF_CTL, + (1<pinstate_sleep) { + ret = pinctrl_select_state(sunxi_spdif->pinctrl, + sunxi_spdif->pinstate_sleep); + if (ret) { + dev_err(sunxi_spdif->dev, + "pinstate sleep select failed\n"); + return ret; + } + } + + if (sunxi_spdif->pinctrl != NULL) + devm_pinctrl_put(sunxi_spdif->pinctrl); + + pr_debug("[sunxi-spdif]sunxi_spdif->clk_enable: %d\n", + sunxi_spdif->active); + + sunxi_spdif->pinctrl = NULL; + sunxi_spdif->pinstate = NULL; +#ifdef SPDIF_PINCTRL_STATE_DEFAULT_B + sunxi_spdif->pinstate_b = NULL; +#endif + sunxi_spdif->pinstate_sleep = NULL; + if (sunxi_spdif->moduleclk != NULL) + clk_disable_unprepare(sunxi_spdif->moduleclk); +#ifdef SPDIF_PLL_AUDIO_X4 + if (sunxi_spdif->pllclkx4 != NULL) + clk_disable_unprepare(sunxi_spdif->pllclkx4); +#endif + if (sunxi_spdif->pllclk != NULL) + clk_disable_unprepare(sunxi_spdif->pllclk); + + pr_debug("[SPDIF]End %s\n", __func__); + + return 0; +} + +static int sunxi_spdif_resume(struct snd_soc_dai *dai) +{ + struct sunxi_spdif_info *sunxi_spdif = snd_soc_dai_get_drvdata(dai); + int ret; + + pr_debug("[sunxi-spdif]Enter %s\n", __func__); + + if (sunxi_spdif->pllclk != NULL) { + ret = clk_prepare_enable(sunxi_spdif->pllclk); + if (ret) + pr_err("[%s] pllclk prepare enable error:%d\n", + __func__, ret); + } +#ifdef SPDIF_PLL_AUDIO_X4 + if (sunxi_spdif->pllclkx4 != NULL) { + ret = clk_prepare_enable(sunxi_spdif->pllclkx4); + if (ret) + pr_err("[%s] pllclkx4 prepare enable error:%d\n", + __func__, ret); + } +#endif + if (sunxi_spdif->moduleclk != NULL) { + ret = clk_prepare_enable(sunxi_spdif->moduleclk); + if (ret) + pr_err("[%s] modlule clk prepare enable error:%d\n", + __func__, ret); + } + + if (sunxi_spdif->pinctrl == NULL) { + sunxi_spdif->pinctrl = devm_pinctrl_get(sunxi_spdif->dev); + if (IS_ERR_OR_NULL(sunxi_spdif->pinctrl)) { + dev_err(sunxi_spdif->dev, + "Can't get sunxi spdif pinctrl\n"); + return -EBUSY; + } + } + + if (!sunxi_spdif->pinstate) { + sunxi_spdif->pinstate = pinctrl_lookup_state( + sunxi_spdif->pinctrl, PINCTRL_STATE_DEFAULT); + if (IS_ERR_OR_NULL(sunxi_spdif->pinstate)) { + dev_err(sunxi_spdif->dev, + "Can't get spdif pinctrl default state\n"); + return -EBUSY; + } + } + +#ifdef SPDIF_PINCTRL_STATE_DEFAULT_B + if (!sunxi_spdif->pinstate_b) { + sunxi_spdif->pinstate_b = pinctrl_lookup_state( + sunxi_spdif->pinctrl, + SPDIF_PINCTRL_STATE_DEFAULT_B); + if (IS_ERR_OR_NULL(sunxi_spdif->pinstate_b)) { + dev_err(sunxi_spdif->dev, + "Can't get spdif pinctrl pins_b state\n"); + return -EBUSY; + } + } +#endif + + if (!sunxi_spdif->pinstate_sleep) { + sunxi_spdif->pinstate_sleep = pinctrl_lookup_state( + sunxi_spdif->pinctrl, PINCTRL_STATE_SLEEP); + if (IS_ERR_OR_NULL(sunxi_spdif->pinstate_sleep)) { + dev_err(sunxi_spdif->dev, + "Can't get spdif pinctrl sleep state\n"); + return -EINVAL; + } + } + + ret = pinctrl_select_state(sunxi_spdif->pinctrl, sunxi_spdif->pinstate); + if (ret) { + dev_err(sunxi_spdif->dev, "select pin default state failed\n"); + return ret; + } + +#ifdef SPDIF_PINCTRL_STATE_DEFAULT_B + ret = pinctrl_select_state(sunxi_spdif->pinctrl, sunxi_spdif->pinstate_b); + if (ret) { + dev_err(sunxi_spdif->dev, "select pins_b state failed\n"); + return ret; + } +#endif + + sunxi_spdif_init(sunxi_spdif); + regmap_update_bits(sunxi_spdif->regmap, SUNXI_SPDIF_CTL, + (1<dev.of_node; + struct sunxi_spdif_info *sunxi_spdif; + struct gpio_config config; + int ret; + + sunxi_spdif = devm_kzalloc(&pdev->dev, + sizeof(struct sunxi_spdif_info), GFP_KERNEL); + if (!sunxi_spdif) { + ret = -ENOMEM; + goto err_node_put; + } + dev_set_drvdata(&pdev->dev, sunxi_spdif); + sunxi_spdif->dev = &pdev->dev; + sunxi_spdif->dai = sunxi_spdif_dai; + sunxi_spdif->dai.name = dev_name(&pdev->dev); + + ret = of_address_to_resource(node, 0, &res); + if (ret) { + dev_err(&pdev->dev, "Can't parse device node resource\n"); + return -ENODEV; + } + + memregion = devm_request_mem_region(&pdev->dev, res.start, + resource_size(&res), DRV_NAME); + if (memregion == NULL) { + dev_err(&pdev->dev, "Memory region already claimed\n"); + ret = -EBUSY; + goto err_devm_kfree; + } + + sunxi_spdif->membase = ioremap(res.start, resource_size(&res)); + if (sunxi_spdif->membase == NULL) { + dev_err(&pdev->dev, "Can't remap sunxi spdif registers\n"); + ret = -EINVAL; + goto err_devm_kfree; + } + + sunxi_spdif->regmap = devm_regmap_init_mmio(&pdev->dev, + sunxi_spdif->membase, &sunxi_spdif_regmap_config); + if (IS_ERR(sunxi_spdif->regmap)) { + dev_err(&pdev->dev, "regmap sunxi spdif membase failed\n"); + ret = PTR_ERR(sunxi_spdif->regmap); + goto err_iounmap; + } + + sunxi_spdif->pllclk = of_clk_get(node, 0); + if (IS_ERR(sunxi_spdif->pllclk)) { + dev_err(&pdev->dev, "Can't get pll_audo clk clocks!\n"); + ret = PTR_ERR(sunxi_spdif->pllclk); + goto err_iounmap; + } + +#ifdef SPDIF_PLL_AUDIO_X4 + sunxi_spdif->pllclkx4 = of_clk_get(node, 1); + if (IS_ERR(sunxi_spdif->pllclkx4)) { + dev_err(&pdev->dev, "Can't get pll_audiox4 clk clocks!\n"); + ret = PTR_ERR(sunxi_spdif->pllclkx4); + goto err_pllclk_put; + } + + sunxi_spdif->moduleclk = of_clk_get(node, 2); + if (IS_ERR(sunxi_spdif->moduleclk)) { + dev_err(&pdev->dev, "Can't get spdif clocks\n"); + ret = PTR_ERR(sunxi_spdif->moduleclk); + goto err_pllclkx4_put; + } else { + if (clk_set_parent(sunxi_spdif->moduleclk, + sunxi_spdif->pllclkx4)) { + dev_err(&pdev->dev, + "set parent of moduleclk to pllclk failed!\n"); + ret = -EINVAL; + goto err_moduleclk_put; + } + } + + if (clk_prepare_enable(sunxi_spdif->pllclk)) { + dev_err(&pdev->dev, "pllclk enable failed\n"); + ret = -EBUSY; + goto err_moduleclk_put; + } + + if (clk_prepare_enable(sunxi_spdif->pllclkx4)) { + dev_err(&pdev->dev, "pllclkx4 enable failed\n"); + ret = -EBUSY; + goto err_pllclk_disable; + } + + if (clk_prepare_enable(sunxi_spdif->moduleclk)) { + dev_err(&pdev->dev, "moduleclk enable failed\n"); + ret = -EBUSY; + goto err_pllclkx4_disable; + } +#else + sunxi_spdif->moduleclk = of_clk_get(node, 1); + if (IS_ERR(sunxi_spdif->moduleclk)) { + dev_err(&pdev->dev, "Can't get spdif clocks\n"); + ret = PTR_ERR(sunxi_spdif->moduleclk); + goto err_pllclk_put; + } else { + if (clk_set_parent(sunxi_spdif->moduleclk, + sunxi_spdif->pllclk)) { + dev_err(&pdev->dev, + "set parent of moduleclk to pllclk failed!\n"); + ret = -EINVAL; + goto err_moduleclk_put; + } + } + + if (clk_prepare_enable(sunxi_spdif->pllclk)) { + dev_err(&pdev->dev, "pllclk enable failed\n"); + ret = -EBUSY; + goto err_moduleclk_put; + } + + if (clk_prepare_enable(sunxi_spdif->moduleclk)) { + dev_err(&pdev->dev, "moduleclk enable failed\n"); + ret = -EBUSY; + goto err_pllclk_disable; + } +#endif + + sunxi_spdif->playback_dma_param.dma_addr = + res.start + SUNXI_SPDIF_TXFIFO; + sunxi_spdif->playback_dma_param.dma_drq_type_num = DRQDST_SPDIFTX; + sunxi_spdif->playback_dma_param.dst_maxburst = 8; + sunxi_spdif->playback_dma_param.src_maxburst = 8; + + sunxi_spdif->capture_dma_param.dma_addr = + res.start + SUNXI_SPDIF_RXFIFO; + sunxi_spdif->capture_dma_param.dma_drq_type_num = DRQSRC_SPDIFRX; + sunxi_spdif->capture_dma_param.src_maxburst = 8; + sunxi_spdif->capture_dma_param.dst_maxburst = 8; + + sunxi_spdif->pinctrl = devm_pinctrl_get(&pdev->dev); + if (IS_ERR_OR_NULL(sunxi_spdif->pinctrl)) { + dev_err(&pdev->dev, + "request pinctrl handle for audio failed\n"); + ret = -EINVAL; + goto err_moduleclk_disable; + } + + sunxi_spdif->pinstate = pinctrl_lookup_state(sunxi_spdif->pinctrl, + PINCTRL_STATE_DEFAULT); + if (IS_ERR_OR_NULL(sunxi_spdif->pinstate)) { + dev_err(&pdev->dev, "lookup pin default state failed\n"); + ret = -EINVAL; + goto err_pinctrl_put; + } + +#ifdef SPDIF_PINCTRL_STATE_DEFAULT_B + sunxi_spdif->pinstate_b = pinctrl_lookup_state(sunxi_spdif->pinctrl, + SPDIF_PINCTRL_STATE_DEFAULT_B); + if (IS_ERR_OR_NULL(sunxi_spdif->pinstate_b)) { + dev_err(&pdev->dev, "Can't get spdif pinctrl default state\n"); + ret = -EINVAL; + goto err_pinctrl_put; + } +#endif + + sunxi_spdif->pinstate_sleep = pinctrl_lookup_state( + sunxi_spdif->pinctrl, PINCTRL_STATE_SLEEP); + if (IS_ERR_OR_NULL(sunxi_spdif->pinstate_sleep)) { + dev_err(&pdev->dev, "lookup pin sleep state failed\n"); + ret = -EINVAL; + goto err_pinctrl_put; + } + + ret = pinctrl_select_state(sunxi_spdif->pinctrl, sunxi_spdif->pinstate); + if (ret) { + dev_err(sunxi_spdif->dev, "select pin default state failed\n"); + ret = -EBUSY; + goto err_pinctrl_put; + } + +#ifdef SPDIF_PINCTRL_STATE_DEFAULT_B + ret = pinctrl_select_state(sunxi_spdif->pinctrl, sunxi_spdif->pinstate_b); + if (ret) { + dev_err(sunxi_spdif->dev, "select pins_b state failed\n"); + ret = -EBUSY; + goto err_pinctrl_put; + } +#endif + + /*initial speaker gpio */ + spdif_gpio.gpio = of_get_named_gpio_flags(node, "gpio-spdif", 0, + (enum of_gpio_flags *)&config); + if (!gpio_is_valid(spdif_gpio.gpio)) { + pr_err("failed get gpio-spdif gpio from dts,spdif_gpio:%d\n", + spdif_gpio.gpio); + spdif_gpio.cfg = 0; + } else { + ret = devm_gpio_request(&pdev->dev, spdif_gpio.gpio, "SPDIF"); + if (ret) { + spdif_gpio.cfg = 0; + pr_err("failed to request gpio-spdif gpio\n"); + } else { + spdif_gpio.cfg = 1; + gpio_direction_output(spdif_gpio.gpio, 0); + } + } + ret = snd_soc_register_component(&pdev->dev, &sunxi_spdif_component, + &sunxi_spdif->dai, 1); + if (ret) { + dev_err(&pdev->dev, "Could not register DAI: %d\n", ret); + ret = -ENOMEM; + goto err_pinctrl_put; + } + + ret = asoc_dma_platform_register(&pdev->dev, 0); + if (ret) { + dev_err(&pdev->dev, "Could not register PCM: %d\n", ret); + ret = -ENOMEM; + goto err_unregister_component; + } + + return 0; + +err_unregister_component: + snd_soc_unregister_component(&pdev->dev); +err_pinctrl_put: + devm_pinctrl_put(sunxi_spdif->pinctrl); +err_moduleclk_disable: + clk_disable_unprepare(sunxi_spdif->moduleclk); +#ifdef SPDIF_PLL_AUDIO_X4 +err_pllclkx4_disable: + clk_disable_unprepare(sunxi_spdif->pllclkx4); +#endif +err_pllclk_disable: + clk_disable_unprepare(sunxi_spdif->pllclk); +err_moduleclk_put: + clk_put(sunxi_spdif->moduleclk); +#ifdef SPDIF_PLL_AUDIO_X4 +err_pllclkx4_put: + clk_put(sunxi_spdif->pllclkx4); +#endif +err_pllclk_put: + clk_put(sunxi_spdif->pllclk); +err_iounmap: + iounmap(sunxi_spdif->membase); +err_devm_kfree: + devm_kfree(&pdev->dev, sunxi_spdif); +err_node_put: + of_node_put(node); + return ret; +} + +static int __exit sunxi_spdif_dev_remove(struct platform_device *pdev) +{ + struct sunxi_spdif_info *sunxi_spdif = dev_get_drvdata(&pdev->dev); + + asoc_dma_platform_unregister(&pdev->dev); + snd_soc_unregister_component(&pdev->dev); + clk_disable_unprepare(sunxi_spdif->moduleclk); + clk_put(sunxi_spdif->moduleclk); +#ifdef SPDIF_PLL_AUDIO_X4 + clk_disable_unprepare(sunxi_spdif->pllclkx4); + clk_put(sunxi_spdif->pllclkx4); +#endif + clk_disable_unprepare(sunxi_spdif->pllclk); + clk_put(sunxi_spdif->pllclk); + + iounmap(sunxi_spdif->membase); + devm_kfree(&pdev->dev, sunxi_spdif); + + return 0; +} + +static const struct of_device_id sunxi_spdif_of_match[] = { + { .compatible = "allwinner,sunxi-spdif", }, + {}, +}; + +static struct platform_driver sunxi_spdif_driver = { + .probe = sunxi_spdif_dev_probe, + .remove = __exit_p(sunxi_spdif_dev_remove), + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .of_match_table = sunxi_spdif_of_match, + }, +}; + +module_platform_driver(sunxi_spdif_driver); + +MODULE_AUTHOR("wolfgang huang "); +MODULE_DESCRIPTION("SUNXI SPDIF ASoC Interface"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:sunxi-spdif");