1 /* 2 * STM32 ALSA SoC Digital Audio Interface (SAI) driver. 3 * 4 * Copyright (C) 2016, STMicroelectronics - All Rights Reserved 5 * Author(s): Olivier Moysan <olivier.moysan@st.com> for STMicroelectronics. 6 * 7 * License terms: GPL V2.0. 8 * 9 * This program is free software; you can redistribute it and/or modify it 10 * under the terms of the GNU General Public License version 2 as published by 11 * the Free Software Foundation. 12 * 13 * This program is distributed in the hope that it will be useful, but 14 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 16 * details. 17 */ 18 19 #include <linux/bitfield.h> 20 #include <linux/clk.h> 21 #include <linux/delay.h> 22 #include <linux/module.h> 23 #include <linux/of_platform.h> 24 #include <linux/reset.h> 25 26 #include <sound/dmaengine_pcm.h> 27 #include <sound/core.h> 28 29 #include "stm32_sai.h" 30 31 static LIST_HEAD(sync_providers); 32 static DEFINE_MUTEX(sync_mutex); 33 34 struct sync_provider { 35 struct list_head link; 36 struct device_node *node; 37 int (*sync_conf)(void *data, int synco); 38 void *data; 39 }; 40 41 static const struct stm32_sai_conf stm32_sai_conf_f4 = { 42 .version = SAI_STM32F4, 43 }; 44 45 static const struct stm32_sai_conf stm32_sai_conf_h7 = { 46 .version = SAI_STM32H7, 47 }; 48 49 static const struct of_device_id stm32_sai_ids[] = { 50 { .compatible = "st,stm32f4-sai", .data = (void *)&stm32_sai_conf_f4 }, 51 { .compatible = "st,stm32h7-sai", .data = (void *)&stm32_sai_conf_h7 }, 52 {} 53 }; 54 55 static int stm32_sai_sync_conf_client(struct stm32_sai_data *sai, int synci) 56 { 57 int ret; 58 59 /* Enable peripheral clock to allow GCR register access */ 60 ret = clk_prepare_enable(sai->pclk); 61 if (ret) { 62 dev_err(&sai->pdev->dev, "failed to enable clock: %d\n", ret); 63 return ret; 64 } 65 66 writel_relaxed(FIELD_PREP(SAI_GCR_SYNCIN_MASK, (synci - 1)), sai->base); 67 68 clk_disable_unprepare(sai->pclk); 69 70 return 0; 71 } 72 73 static int stm32_sai_sync_conf_provider(void *data, int synco) 74 { 75 struct stm32_sai_data *sai = (struct stm32_sai_data *)data; 76 u32 prev_synco; 77 int ret; 78 79 /* Enable peripheral clock to allow GCR register access */ 80 ret = clk_prepare_enable(sai->pclk); 81 if (ret) { 82 dev_err(&sai->pdev->dev, "failed to enable clock: %d\n", ret); 83 return ret; 84 } 85 86 dev_dbg(&sai->pdev->dev, "Set %s%s as synchro provider\n", 87 sai->pdev->dev.of_node->name, 88 synco == STM_SAI_SYNC_OUT_A ? "A" : "B"); 89 90 prev_synco = FIELD_GET(SAI_GCR_SYNCOUT_MASK, readl_relaxed(sai->base)); 91 if (prev_synco != STM_SAI_SYNC_OUT_NONE && synco != prev_synco) { 92 dev_err(&sai->pdev->dev, "%s%s already set as sync provider\n", 93 sai->pdev->dev.of_node->name, 94 prev_synco == STM_SAI_SYNC_OUT_A ? "A" : "B"); 95 clk_disable_unprepare(sai->pclk); 96 return -EINVAL; 97 } 98 99 writel_relaxed(FIELD_PREP(SAI_GCR_SYNCOUT_MASK, synco), sai->base); 100 101 clk_disable_unprepare(sai->pclk); 102 103 return 0; 104 } 105 106 static int stm32_sai_set_sync_provider(struct device_node *np, int synco) 107 { 108 struct sync_provider *provider; 109 int ret; 110 111 mutex_lock(&sync_mutex); 112 list_for_each_entry(provider, &sync_providers, link) { 113 if (provider->node == np) { 114 ret = provider->sync_conf(provider->data, synco); 115 mutex_unlock(&sync_mutex); 116 return ret; 117 } 118 } 119 mutex_unlock(&sync_mutex); 120 121 /* SAI sync provider not found */ 122 return -ENODEV; 123 } 124 125 static int stm32_sai_set_sync(struct stm32_sai_data *sai, 126 struct device_node *np_provider, 127 int synco, int synci) 128 { 129 int ret; 130 131 /* Configure sync client */ 132 stm32_sai_sync_conf_client(sai, synci); 133 134 /* Configure sync provider */ 135 ret = stm32_sai_set_sync_provider(np_provider, synco); 136 137 return ret; 138 } 139 140 static int stm32_sai_sync_add_provider(struct platform_device *pdev, 141 void *data) 142 { 143 struct sync_provider *sp; 144 145 sp = devm_kzalloc(&pdev->dev, sizeof(*sp), GFP_KERNEL); 146 if (!sp) 147 return -ENOMEM; 148 149 sp->node = of_node_get(pdev->dev.of_node); 150 sp->data = data; 151 sp->sync_conf = &stm32_sai_sync_conf_provider; 152 153 mutex_lock(&sync_mutex); 154 list_add(&sp->link, &sync_providers); 155 mutex_unlock(&sync_mutex); 156 157 return 0; 158 } 159 160 static void stm32_sai_sync_del_provider(struct device_node *np) 161 { 162 struct sync_provider *sp; 163 164 mutex_lock(&sync_mutex); 165 list_for_each_entry(sp, &sync_providers, link) { 166 if (sp->node == np) { 167 list_del(&sp->link); 168 of_node_put(sp->node); 169 break; 170 } 171 } 172 mutex_unlock(&sync_mutex); 173 } 174 175 static int stm32_sai_probe(struct platform_device *pdev) 176 { 177 struct device_node *np = pdev->dev.of_node; 178 struct stm32_sai_data *sai; 179 struct reset_control *rst; 180 struct resource *res; 181 const struct of_device_id *of_id; 182 int ret; 183 184 sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL); 185 if (!sai) 186 return -ENOMEM; 187 188 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 189 sai->base = devm_ioremap_resource(&pdev->dev, res); 190 if (IS_ERR(sai->base)) 191 return PTR_ERR(sai->base); 192 193 of_id = of_match_device(stm32_sai_ids, &pdev->dev); 194 if (of_id) 195 sai->conf = (struct stm32_sai_conf *)of_id->data; 196 else 197 return -EINVAL; 198 199 if (!STM_SAI_IS_F4(sai)) { 200 sai->pclk = devm_clk_get(&pdev->dev, "pclk"); 201 if (IS_ERR(sai->pclk)) { 202 dev_err(&pdev->dev, "missing bus clock pclk\n"); 203 return PTR_ERR(sai->pclk); 204 } 205 } 206 207 sai->clk_x8k = devm_clk_get(&pdev->dev, "x8k"); 208 if (IS_ERR(sai->clk_x8k)) { 209 dev_err(&pdev->dev, "missing x8k parent clock\n"); 210 return PTR_ERR(sai->clk_x8k); 211 } 212 213 sai->clk_x11k = devm_clk_get(&pdev->dev, "x11k"); 214 if (IS_ERR(sai->clk_x11k)) { 215 dev_err(&pdev->dev, "missing x11k parent clock\n"); 216 return PTR_ERR(sai->clk_x11k); 217 } 218 219 /* init irqs */ 220 sai->irq = platform_get_irq(pdev, 0); 221 if (sai->irq < 0) { 222 dev_err(&pdev->dev, "no irq for node %s\n", pdev->name); 223 return sai->irq; 224 } 225 226 /* reset */ 227 rst = devm_reset_control_get_exclusive(&pdev->dev, NULL); 228 if (!IS_ERR(rst)) { 229 reset_control_assert(rst); 230 udelay(2); 231 reset_control_deassert(rst); 232 } 233 234 ret = stm32_sai_sync_add_provider(pdev, sai); 235 if (ret < 0) 236 return ret; 237 sai->set_sync = &stm32_sai_set_sync; 238 239 sai->pdev = pdev; 240 platform_set_drvdata(pdev, sai); 241 242 ret = of_platform_populate(np, NULL, NULL, &pdev->dev); 243 if (ret < 0) 244 stm32_sai_sync_del_provider(np); 245 246 return ret; 247 } 248 249 static int stm32_sai_remove(struct platform_device *pdev) 250 { 251 of_platform_depopulate(&pdev->dev); 252 253 stm32_sai_sync_del_provider(pdev->dev.of_node); 254 255 return 0; 256 } 257 258 MODULE_DEVICE_TABLE(of, stm32_sai_ids); 259 260 static struct platform_driver stm32_sai_driver = { 261 .driver = { 262 .name = "st,stm32-sai", 263 .of_match_table = stm32_sai_ids, 264 }, 265 .probe = stm32_sai_probe, 266 .remove = stm32_sai_remove, 267 }; 268 269 module_platform_driver(stm32_sai_driver); 270 271 MODULE_DESCRIPTION("STM32 Soc SAI Interface"); 272 MODULE_AUTHOR("Olivier Moysan <olivier.moysan@st.com>"); 273 MODULE_ALIAS("platform:st,stm32-sai"); 274 MODULE_LICENSE("GPL v2"); 275