1f3bd847eSArnaud Pouliquen /* 2f3bd847eSArnaud Pouliquen * Copyright (C) STMicroelectronics SA 2015 3f3bd847eSArnaud Pouliquen * Authors: Arnaud Pouliquen <arnaud.pouliquen@st.com> 4f3bd847eSArnaud Pouliquen * for STMicroelectronics. 5f3bd847eSArnaud Pouliquen * License terms: GNU General Public License (GPL), version 2 6f3bd847eSArnaud Pouliquen */ 7f3bd847eSArnaud Pouliquen 8f3bd847eSArnaud Pouliquen #include <linux/module.h> 9*bf9185ddSMark Brown #include <linux/pinctrl/consumer.h> 10f3bd847eSArnaud Pouliquen 11f3bd847eSArnaud Pouliquen #include "uniperif.h" 12f3bd847eSArnaud Pouliquen 13f3bd847eSArnaud Pouliquen /* 14f3bd847eSArnaud Pouliquen * sti_uniperiph_dai_create_ctrl 15f3bd847eSArnaud Pouliquen * This function is used to create Ctrl associated to DAI but also pcm device. 16f3bd847eSArnaud Pouliquen * Request is done by front end to associate ctrl with pcm device id 17f3bd847eSArnaud Pouliquen */ 18f2da4542Skbuild test robot static int sti_uniperiph_dai_create_ctrl(struct snd_soc_dai *dai) 19f3bd847eSArnaud Pouliquen { 20f3bd847eSArnaud Pouliquen struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai); 21f3bd847eSArnaud Pouliquen struct uniperif *uni = priv->dai_data.uni; 22f3bd847eSArnaud Pouliquen struct snd_kcontrol_new *ctrl; 23f3bd847eSArnaud Pouliquen int i; 24f3bd847eSArnaud Pouliquen 25f3bd847eSArnaud Pouliquen if (!uni->num_ctrls) 26f3bd847eSArnaud Pouliquen return 0; 27f3bd847eSArnaud Pouliquen 28f3bd847eSArnaud Pouliquen for (i = 0; i < uni->num_ctrls; i++) { 29f3bd847eSArnaud Pouliquen /* 30f3bd847eSArnaud Pouliquen * Several Control can have same name. Controls are indexed on 31f3bd847eSArnaud Pouliquen * Uniperipheral instance ID 32f3bd847eSArnaud Pouliquen */ 33f3bd847eSArnaud Pouliquen ctrl = &uni->snd_ctrls[i]; 34f3bd847eSArnaud Pouliquen ctrl->index = uni->info->id; 35f3bd847eSArnaud Pouliquen ctrl->device = uni->info->id; 36f3bd847eSArnaud Pouliquen } 37f3bd847eSArnaud Pouliquen 38f3bd847eSArnaud Pouliquen return snd_soc_add_dai_controls(dai, uni->snd_ctrls, uni->num_ctrls); 39f3bd847eSArnaud Pouliquen } 40f3bd847eSArnaud Pouliquen 41f3bd847eSArnaud Pouliquen /* 42f3bd847eSArnaud Pouliquen * DAI 43f3bd847eSArnaud Pouliquen */ 44f3bd847eSArnaud Pouliquen int sti_uniperiph_dai_hw_params(struct snd_pcm_substream *substream, 45f3bd847eSArnaud Pouliquen struct snd_pcm_hw_params *params, 46f3bd847eSArnaud Pouliquen struct snd_soc_dai *dai) 47f3bd847eSArnaud Pouliquen { 48f3bd847eSArnaud Pouliquen struct snd_dmaengine_dai_dma_data *dma_data; 49f3bd847eSArnaud Pouliquen int transfer_size; 50f3bd847eSArnaud Pouliquen 51f3bd847eSArnaud Pouliquen transfer_size = params_channels(params) * UNIPERIF_FIFO_FRAMES; 52f3bd847eSArnaud Pouliquen 53f3bd847eSArnaud Pouliquen dma_data = snd_soc_dai_get_dma_data(dai, substream); 54f3bd847eSArnaud Pouliquen dma_data->maxburst = transfer_size; 55f3bd847eSArnaud Pouliquen 56f3bd847eSArnaud Pouliquen return 0; 57f3bd847eSArnaud Pouliquen } 58f3bd847eSArnaud Pouliquen 59f3bd847eSArnaud Pouliquen int sti_uniperiph_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) 60f3bd847eSArnaud Pouliquen { 61f3bd847eSArnaud Pouliquen struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai); 62f3bd847eSArnaud Pouliquen 63f3bd847eSArnaud Pouliquen priv->dai_data.uni->daifmt = fmt; 64f3bd847eSArnaud Pouliquen 65f3bd847eSArnaud Pouliquen return 0; 66f3bd847eSArnaud Pouliquen } 67f3bd847eSArnaud Pouliquen 68f3bd847eSArnaud Pouliquen static int sti_uniperiph_dai_suspend(struct snd_soc_dai *dai) 69f3bd847eSArnaud Pouliquen { 70f3bd847eSArnaud Pouliquen struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai); 71f3bd847eSArnaud Pouliquen struct uniperif *uni = priv->dai_data.uni; 72f3bd847eSArnaud Pouliquen int ret; 73f3bd847eSArnaud Pouliquen 74f3bd847eSArnaud Pouliquen /* The uniperipheral should be in stopped state */ 75f3bd847eSArnaud Pouliquen if (uni->state != UNIPERIF_STATE_STOPPED) { 76f3bd847eSArnaud Pouliquen dev_err(uni->dev, "%s: invalid uni state( %d)", 77f3bd847eSArnaud Pouliquen __func__, (int)uni->state); 78f3bd847eSArnaud Pouliquen return -EBUSY; 79f3bd847eSArnaud Pouliquen } 80f3bd847eSArnaud Pouliquen 81f3bd847eSArnaud Pouliquen /* Pinctrl: switch pinstate to sleep */ 82f3bd847eSArnaud Pouliquen ret = pinctrl_pm_select_sleep_state(uni->dev); 83f3bd847eSArnaud Pouliquen if (ret) 84f3bd847eSArnaud Pouliquen dev_err(uni->dev, "%s: failed to select pinctrl state", 85f3bd847eSArnaud Pouliquen __func__); 86f3bd847eSArnaud Pouliquen 87f3bd847eSArnaud Pouliquen return ret; 88f3bd847eSArnaud Pouliquen } 89f3bd847eSArnaud Pouliquen 90f3bd847eSArnaud Pouliquen static int sti_uniperiph_dai_resume(struct snd_soc_dai *dai) 91f3bd847eSArnaud Pouliquen { 92f3bd847eSArnaud Pouliquen struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai); 93f3bd847eSArnaud Pouliquen struct uniperif *uni = priv->dai_data.uni; 94f3bd847eSArnaud Pouliquen int ret; 95f3bd847eSArnaud Pouliquen 96f3bd847eSArnaud Pouliquen if (of_device_is_compatible(dai->dev->of_node, "st,sti-uni-player")) { 97f3bd847eSArnaud Pouliquen ret = uni_player_resume(uni); 98f3bd847eSArnaud Pouliquen if (ret) 99f3bd847eSArnaud Pouliquen return ret; 100f3bd847eSArnaud Pouliquen } 101f3bd847eSArnaud Pouliquen 102f3bd847eSArnaud Pouliquen /* pinctrl: switch pinstate to default */ 103f3bd847eSArnaud Pouliquen ret = pinctrl_pm_select_default_state(uni->dev); 104f3bd847eSArnaud Pouliquen if (ret) 105f3bd847eSArnaud Pouliquen dev_err(uni->dev, "%s: failed to select pinctrl state", 106f3bd847eSArnaud Pouliquen __func__); 107f3bd847eSArnaud Pouliquen 108f3bd847eSArnaud Pouliquen return ret; 109f3bd847eSArnaud Pouliquen } 110f3bd847eSArnaud Pouliquen 111f3bd847eSArnaud Pouliquen static int sti_uniperiph_dai_probe(struct snd_soc_dai *dai) 112f3bd847eSArnaud Pouliquen { 113f3bd847eSArnaud Pouliquen struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai); 114f3bd847eSArnaud Pouliquen struct sti_uniperiph_dai *dai_data = &priv->dai_data; 115f3bd847eSArnaud Pouliquen 116f3bd847eSArnaud Pouliquen /* DMA settings*/ 117f3bd847eSArnaud Pouliquen if (of_device_is_compatible(dai->dev->of_node, "st,sti-uni-player")) 118f3bd847eSArnaud Pouliquen snd_soc_dai_init_dma_data(dai, &dai_data->dma_data, NULL); 119f3bd847eSArnaud Pouliquen else 120f3bd847eSArnaud Pouliquen snd_soc_dai_init_dma_data(dai, NULL, &dai_data->dma_data); 121f3bd847eSArnaud Pouliquen 122f3bd847eSArnaud Pouliquen dai_data->dma_data.addr = dai_data->uni->fifo_phys_address; 123f3bd847eSArnaud Pouliquen dai_data->dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; 124f3bd847eSArnaud Pouliquen 125f3bd847eSArnaud Pouliquen return sti_uniperiph_dai_create_ctrl(dai); 126f3bd847eSArnaud Pouliquen } 127f3bd847eSArnaud Pouliquen 128f3bd847eSArnaud Pouliquen static const struct snd_soc_dai_driver sti_uniperiph_dai_template = { 129f3bd847eSArnaud Pouliquen .probe = sti_uniperiph_dai_probe, 130f3bd847eSArnaud Pouliquen .suspend = sti_uniperiph_dai_suspend, 131f3bd847eSArnaud Pouliquen .resume = sti_uniperiph_dai_resume 132f3bd847eSArnaud Pouliquen }; 133f3bd847eSArnaud Pouliquen 134f3bd847eSArnaud Pouliquen static const struct snd_soc_component_driver sti_uniperiph_dai_component = { 135f3bd847eSArnaud Pouliquen .name = "sti_cpu_dai", 136f3bd847eSArnaud Pouliquen }; 137f3bd847eSArnaud Pouliquen 138f3bd847eSArnaud Pouliquen static int sti_uniperiph_cpu_dai_of(struct device_node *node, 139f3bd847eSArnaud Pouliquen struct sti_uniperiph_data *priv) 140f3bd847eSArnaud Pouliquen { 141f3bd847eSArnaud Pouliquen const char *str; 142f3bd847eSArnaud Pouliquen int ret; 143f3bd847eSArnaud Pouliquen struct device *dev = &priv->pdev->dev; 144f3bd847eSArnaud Pouliquen struct sti_uniperiph_dai *dai_data = &priv->dai_data; 145f3bd847eSArnaud Pouliquen struct snd_soc_dai_driver *dai = priv->dai; 146f3bd847eSArnaud Pouliquen struct snd_soc_pcm_stream *stream; 147f3bd847eSArnaud Pouliquen struct uniperif *uni; 148f3bd847eSArnaud Pouliquen 149f3bd847eSArnaud Pouliquen uni = devm_kzalloc(dev, sizeof(*uni), GFP_KERNEL); 150f3bd847eSArnaud Pouliquen if (!uni) 151f3bd847eSArnaud Pouliquen return -ENOMEM; 152f3bd847eSArnaud Pouliquen 153f3bd847eSArnaud Pouliquen *dai = sti_uniperiph_dai_template; 154f3bd847eSArnaud Pouliquen ret = of_property_read_string(node, "dai-name", &str); 155f3bd847eSArnaud Pouliquen if (ret < 0) { 156f3bd847eSArnaud Pouliquen dev_err(dev, "%s: dai name missing.\n", __func__); 157f3bd847eSArnaud Pouliquen return -EINVAL; 158f3bd847eSArnaud Pouliquen } 159f3bd847eSArnaud Pouliquen dai->name = str; 160f3bd847eSArnaud Pouliquen 161f3bd847eSArnaud Pouliquen /* Get resources */ 162f3bd847eSArnaud Pouliquen uni->mem_region = platform_get_resource(priv->pdev, IORESOURCE_MEM, 0); 163f3bd847eSArnaud Pouliquen 164f3bd847eSArnaud Pouliquen if (!uni->mem_region) { 165f3bd847eSArnaud Pouliquen dev_err(dev, "Failed to get memory resource"); 166f3bd847eSArnaud Pouliquen return -ENODEV; 167f3bd847eSArnaud Pouliquen } 168f3bd847eSArnaud Pouliquen 169f3bd847eSArnaud Pouliquen uni->base = devm_ioremap_resource(dev, uni->mem_region); 170f3bd847eSArnaud Pouliquen 171f3bd847eSArnaud Pouliquen if (IS_ERR(uni->base)) 172f3bd847eSArnaud Pouliquen return PTR_ERR(uni->base); 173f3bd847eSArnaud Pouliquen 174f3bd847eSArnaud Pouliquen uni->fifo_phys_address = uni->mem_region->start + 175f3bd847eSArnaud Pouliquen UNIPERIF_FIFO_DATA_OFFSET(uni); 176f3bd847eSArnaud Pouliquen 177f3bd847eSArnaud Pouliquen uni->irq = platform_get_irq(priv->pdev, 0); 178f3bd847eSArnaud Pouliquen if (!uni->irq < 0) { 179f3bd847eSArnaud Pouliquen dev_err(dev, "Failed to get IRQ resource"); 180f3bd847eSArnaud Pouliquen return -ENXIO; 181f3bd847eSArnaud Pouliquen } 182f3bd847eSArnaud Pouliquen 183f3bd847eSArnaud Pouliquen dai_data->uni = uni; 184f3bd847eSArnaud Pouliquen 185f3bd847eSArnaud Pouliquen if (of_device_is_compatible(node, "st,sti-uni-player")) { 186f3bd847eSArnaud Pouliquen uni_player_init(priv->pdev, uni); 187f3bd847eSArnaud Pouliquen stream = &dai->playback; 188f3bd847eSArnaud Pouliquen } else { 189f3bd847eSArnaud Pouliquen uni_reader_init(priv->pdev, uni); 190f3bd847eSArnaud Pouliquen stream = &dai->capture; 191f3bd847eSArnaud Pouliquen } 192f3bd847eSArnaud Pouliquen dai->ops = uni->dai_ops; 193f3bd847eSArnaud Pouliquen 194f3bd847eSArnaud Pouliquen stream->stream_name = dai->name; 195f3bd847eSArnaud Pouliquen stream->channels_min = uni->hw->channels_min; 196f3bd847eSArnaud Pouliquen stream->channels_max = uni->hw->channels_max; 197f3bd847eSArnaud Pouliquen stream->rates = uni->hw->rates; 198f3bd847eSArnaud Pouliquen stream->formats = uni->hw->formats; 199f3bd847eSArnaud Pouliquen 200f3bd847eSArnaud Pouliquen return 0; 201f3bd847eSArnaud Pouliquen } 202f3bd847eSArnaud Pouliquen 203f3bd847eSArnaud Pouliquen static const struct snd_dmaengine_pcm_config dmaengine_pcm_config = { 204f3bd847eSArnaud Pouliquen .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config, 205f3bd847eSArnaud Pouliquen }; 206f3bd847eSArnaud Pouliquen 207f3bd847eSArnaud Pouliquen static int sti_uniperiph_probe(struct platform_device *pdev) 208f3bd847eSArnaud Pouliquen { 209f3bd847eSArnaud Pouliquen struct sti_uniperiph_data *priv; 210f3bd847eSArnaud Pouliquen struct device_node *node = pdev->dev.of_node; 211f3bd847eSArnaud Pouliquen int ret; 212f3bd847eSArnaud Pouliquen 213f3bd847eSArnaud Pouliquen /* Allocate the private data and the CPU_DAI array */ 214f3bd847eSArnaud Pouliquen priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 215f3bd847eSArnaud Pouliquen if (!priv) 216f3bd847eSArnaud Pouliquen return -ENOMEM; 217f3bd847eSArnaud Pouliquen priv->dai = devm_kzalloc(&pdev->dev, sizeof(*priv->dai), GFP_KERNEL); 218f3bd847eSArnaud Pouliquen if (!priv->dai) 219f3bd847eSArnaud Pouliquen return -ENOMEM; 220f3bd847eSArnaud Pouliquen 221f3bd847eSArnaud Pouliquen priv->pdev = pdev; 222f3bd847eSArnaud Pouliquen 223f3bd847eSArnaud Pouliquen ret = sti_uniperiph_cpu_dai_of(node, priv); 224f3bd847eSArnaud Pouliquen 225f3bd847eSArnaud Pouliquen dev_set_drvdata(&pdev->dev, priv); 226f3bd847eSArnaud Pouliquen 227f3bd847eSArnaud Pouliquen ret = snd_soc_register_component(&pdev->dev, 228f3bd847eSArnaud Pouliquen &sti_uniperiph_dai_component, 229f3bd847eSArnaud Pouliquen priv->dai, 1); 230f3bd847eSArnaud Pouliquen if (ret < 0) 231f3bd847eSArnaud Pouliquen return ret; 232f3bd847eSArnaud Pouliquen 233f3bd847eSArnaud Pouliquen return devm_snd_dmaengine_pcm_register(&pdev->dev, 234f3bd847eSArnaud Pouliquen &dmaengine_pcm_config, 0); 235f3bd847eSArnaud Pouliquen } 236f3bd847eSArnaud Pouliquen 237f3bd847eSArnaud Pouliquen static const struct of_device_id snd_soc_sti_match[] = { 238f3bd847eSArnaud Pouliquen { .compatible = "st,sti-uni-player", }, 239f3bd847eSArnaud Pouliquen { .compatible = "st,sti-uni-reader", }, 240f3bd847eSArnaud Pouliquen {}, 241f3bd847eSArnaud Pouliquen }; 242f3bd847eSArnaud Pouliquen 243f3bd847eSArnaud Pouliquen static struct platform_driver sti_uniperiph_driver = { 244f3bd847eSArnaud Pouliquen .driver = { 245f3bd847eSArnaud Pouliquen .name = "sti-uniperiph-dai", 246f3bd847eSArnaud Pouliquen .of_match_table = snd_soc_sti_match, 247f3bd847eSArnaud Pouliquen }, 248f3bd847eSArnaud Pouliquen .probe = sti_uniperiph_probe, 249f3bd847eSArnaud Pouliquen }; 250f3bd847eSArnaud Pouliquen module_platform_driver(sti_uniperiph_driver); 251f3bd847eSArnaud Pouliquen 252f3bd847eSArnaud Pouliquen MODULE_DESCRIPTION("uniperipheral DAI driver"); 253f3bd847eSArnaud Pouliquen MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen@st.com>"); 254f3bd847eSArnaud Pouliquen MODULE_LICENSE("GPL v2"); 255