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