1 /* 2 * Intel Smart Sound Technology (SST) DSP Core Driver 3 * 4 * Copyright (C) 2013, Intel Corporation. All rights reserved. 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License version 8 * 2 as published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 */ 16 17 #include <linux/slab.h> 18 #include <linux/export.h> 19 #include <linux/interrupt.h> 20 #include <linux/module.h> 21 #include <linux/platform_device.h> 22 #include <linux/io.h> 23 24 #include "sst-dsp.h" 25 #include "sst-dsp-priv.h" 26 27 #define CREATE_TRACE_POINTS 28 #include <trace/events/intel-sst.h> 29 30 /* Internal generic low-level SST IO functions - can be overidden */ 31 void sst_shim32_write(void __iomem *addr, u32 offset, u32 value) 32 { 33 writel(value, addr + offset); 34 } 35 EXPORT_SYMBOL_GPL(sst_shim32_write); 36 37 u32 sst_shim32_read(void __iomem *addr, u32 offset) 38 { 39 return readl(addr + offset); 40 } 41 EXPORT_SYMBOL_GPL(sst_shim32_read); 42 43 void sst_shim32_write64(void __iomem *addr, u32 offset, u64 value) 44 { 45 memcpy_toio(addr + offset, &value, sizeof(value)); 46 } 47 EXPORT_SYMBOL_GPL(sst_shim32_write64); 48 49 u64 sst_shim32_read64(void __iomem *addr, u32 offset) 50 { 51 u64 val; 52 53 memcpy_fromio(&val, addr + offset, sizeof(val)); 54 return val; 55 } 56 EXPORT_SYMBOL_GPL(sst_shim32_read64); 57 58 static inline void _sst_memcpy_toio_32(volatile u32 __iomem *dest, 59 u32 *src, size_t bytes) 60 { 61 int i, words = bytes >> 2; 62 63 for (i = 0; i < words; i++) 64 writel(src[i], dest + i); 65 } 66 67 static inline void _sst_memcpy_fromio_32(u32 *dest, 68 const volatile __iomem u32 *src, size_t bytes) 69 { 70 int i, words = bytes >> 2; 71 72 for (i = 0; i < words; i++) 73 dest[i] = readl(src + i); 74 } 75 76 void sst_memcpy_toio_32(struct sst_dsp *sst, 77 void __iomem *dest, void *src, size_t bytes) 78 { 79 _sst_memcpy_toio_32(dest, src, bytes); 80 } 81 EXPORT_SYMBOL_GPL(sst_memcpy_toio_32); 82 83 void sst_memcpy_fromio_32(struct sst_dsp *sst, void *dest, 84 void __iomem *src, size_t bytes) 85 { 86 _sst_memcpy_fromio_32(dest, src, bytes); 87 } 88 EXPORT_SYMBOL_GPL(sst_memcpy_fromio_32); 89 90 /* Public API */ 91 void sst_dsp_shim_write(struct sst_dsp *sst, u32 offset, u32 value) 92 { 93 unsigned long flags; 94 95 spin_lock_irqsave(&sst->spinlock, flags); 96 sst->ops->write(sst->addr.shim, offset, value); 97 spin_unlock_irqrestore(&sst->spinlock, flags); 98 } 99 EXPORT_SYMBOL_GPL(sst_dsp_shim_write); 100 101 u32 sst_dsp_shim_read(struct sst_dsp *sst, u32 offset) 102 { 103 unsigned long flags; 104 u32 val; 105 106 spin_lock_irqsave(&sst->spinlock, flags); 107 val = sst->ops->read(sst->addr.shim, offset); 108 spin_unlock_irqrestore(&sst->spinlock, flags); 109 110 return val; 111 } 112 EXPORT_SYMBOL_GPL(sst_dsp_shim_read); 113 114 void sst_dsp_shim_write64(struct sst_dsp *sst, u32 offset, u64 value) 115 { 116 unsigned long flags; 117 118 spin_lock_irqsave(&sst->spinlock, flags); 119 sst->ops->write64(sst->addr.shim, offset, value); 120 spin_unlock_irqrestore(&sst->spinlock, flags); 121 } 122 EXPORT_SYMBOL_GPL(sst_dsp_shim_write64); 123 124 u64 sst_dsp_shim_read64(struct sst_dsp *sst, u32 offset) 125 { 126 unsigned long flags; 127 u64 val; 128 129 spin_lock_irqsave(&sst->spinlock, flags); 130 val = sst->ops->read64(sst->addr.shim, offset); 131 spin_unlock_irqrestore(&sst->spinlock, flags); 132 133 return val; 134 } 135 EXPORT_SYMBOL_GPL(sst_dsp_shim_read64); 136 137 void sst_dsp_shim_write_unlocked(struct sst_dsp *sst, u32 offset, u32 value) 138 { 139 sst->ops->write(sst->addr.shim, offset, value); 140 } 141 EXPORT_SYMBOL_GPL(sst_dsp_shim_write_unlocked); 142 143 u32 sst_dsp_shim_read_unlocked(struct sst_dsp *sst, u32 offset) 144 { 145 return sst->ops->read(sst->addr.shim, offset); 146 } 147 EXPORT_SYMBOL_GPL(sst_dsp_shim_read_unlocked); 148 149 void sst_dsp_shim_write64_unlocked(struct sst_dsp *sst, u32 offset, u64 value) 150 { 151 sst->ops->write64(sst->addr.shim, offset, value); 152 } 153 EXPORT_SYMBOL_GPL(sst_dsp_shim_write64_unlocked); 154 155 u64 sst_dsp_shim_read64_unlocked(struct sst_dsp *sst, u32 offset) 156 { 157 return sst->ops->read64(sst->addr.shim, offset); 158 } 159 EXPORT_SYMBOL_GPL(sst_dsp_shim_read64_unlocked); 160 161 int sst_dsp_shim_update_bits_unlocked(struct sst_dsp *sst, u32 offset, 162 u32 mask, u32 value) 163 { 164 bool change; 165 unsigned int old, new; 166 u32 ret; 167 168 ret = sst_dsp_shim_read_unlocked(sst, offset); 169 170 old = ret; 171 new = (old & (~mask)) | (value & mask); 172 173 change = (old != new); 174 if (change) 175 sst_dsp_shim_write_unlocked(sst, offset, new); 176 177 return change; 178 } 179 EXPORT_SYMBOL_GPL(sst_dsp_shim_update_bits_unlocked); 180 181 int sst_dsp_shim_update_bits64_unlocked(struct sst_dsp *sst, u32 offset, 182 u64 mask, u64 value) 183 { 184 bool change; 185 u64 old, new; 186 187 old = sst_dsp_shim_read64_unlocked(sst, offset); 188 189 new = (old & (~mask)) | (value & mask); 190 191 change = (old != new); 192 if (change) 193 sst_dsp_shim_write64_unlocked(sst, offset, new); 194 195 return change; 196 } 197 EXPORT_SYMBOL_GPL(sst_dsp_shim_update_bits64_unlocked); 198 199 int sst_dsp_shim_update_bits(struct sst_dsp *sst, u32 offset, 200 u32 mask, u32 value) 201 { 202 unsigned long flags; 203 bool change; 204 205 spin_lock_irqsave(&sst->spinlock, flags); 206 change = sst_dsp_shim_update_bits_unlocked(sst, offset, mask, value); 207 spin_unlock_irqrestore(&sst->spinlock, flags); 208 return change; 209 } 210 EXPORT_SYMBOL_GPL(sst_dsp_shim_update_bits); 211 212 int sst_dsp_shim_update_bits64(struct sst_dsp *sst, u32 offset, 213 u64 mask, u64 value) 214 { 215 unsigned long flags; 216 bool change; 217 218 spin_lock_irqsave(&sst->spinlock, flags); 219 change = sst_dsp_shim_update_bits64_unlocked(sst, offset, mask, value); 220 spin_unlock_irqrestore(&sst->spinlock, flags); 221 return change; 222 } 223 EXPORT_SYMBOL_GPL(sst_dsp_shim_update_bits64); 224 225 void sst_dsp_dump(struct sst_dsp *sst) 226 { 227 if (sst->ops->dump) 228 sst->ops->dump(sst); 229 } 230 EXPORT_SYMBOL_GPL(sst_dsp_dump); 231 232 void sst_dsp_reset(struct sst_dsp *sst) 233 { 234 if (sst->ops->reset) 235 sst->ops->reset(sst); 236 } 237 EXPORT_SYMBOL_GPL(sst_dsp_reset); 238 239 int sst_dsp_boot(struct sst_dsp *sst) 240 { 241 if (sst->ops->boot) 242 sst->ops->boot(sst); 243 244 return 0; 245 } 246 EXPORT_SYMBOL_GPL(sst_dsp_boot); 247 248 int sst_dsp_wake(struct sst_dsp *sst) 249 { 250 if (sst->ops->wake) 251 return sst->ops->wake(sst); 252 253 return 0; 254 } 255 EXPORT_SYMBOL_GPL(sst_dsp_wake); 256 257 void sst_dsp_sleep(struct sst_dsp *sst) 258 { 259 if (sst->ops->sleep) 260 sst->ops->sleep(sst); 261 } 262 EXPORT_SYMBOL_GPL(sst_dsp_sleep); 263 264 void sst_dsp_stall(struct sst_dsp *sst) 265 { 266 if (sst->ops->stall) 267 sst->ops->stall(sst); 268 } 269 EXPORT_SYMBOL_GPL(sst_dsp_stall); 270 271 void sst_dsp_ipc_msg_tx(struct sst_dsp *dsp, u32 msg) 272 { 273 sst_dsp_shim_write_unlocked(dsp, SST_IPCX, msg | SST_IPCX_BUSY); 274 trace_sst_ipc_msg_tx(msg); 275 } 276 EXPORT_SYMBOL_GPL(sst_dsp_ipc_msg_tx); 277 278 u32 sst_dsp_ipc_msg_rx(struct sst_dsp *dsp) 279 { 280 u32 msg; 281 282 msg = sst_dsp_shim_read_unlocked(dsp, SST_IPCX); 283 trace_sst_ipc_msg_rx(msg); 284 285 return msg; 286 } 287 EXPORT_SYMBOL_GPL(sst_dsp_ipc_msg_rx); 288 289 int sst_dsp_mailbox_init(struct sst_dsp *sst, u32 inbox_offset, size_t inbox_size, 290 u32 outbox_offset, size_t outbox_size) 291 { 292 sst->mailbox.in_base = sst->addr.lpe + inbox_offset; 293 sst->mailbox.out_base = sst->addr.lpe + outbox_offset; 294 sst->mailbox.in_size = inbox_size; 295 sst->mailbox.out_size = outbox_size; 296 return 0; 297 } 298 EXPORT_SYMBOL_GPL(sst_dsp_mailbox_init); 299 300 void sst_dsp_outbox_write(struct sst_dsp *sst, void *message, size_t bytes) 301 { 302 u32 i; 303 304 trace_sst_ipc_outbox_write(bytes); 305 306 memcpy_toio(sst->mailbox.out_base, message, bytes); 307 308 for (i = 0; i < bytes; i += 4) 309 trace_sst_ipc_outbox_wdata(i, *(u32 *)(message + i)); 310 } 311 EXPORT_SYMBOL_GPL(sst_dsp_outbox_write); 312 313 void sst_dsp_outbox_read(struct sst_dsp *sst, void *message, size_t bytes) 314 { 315 u32 i; 316 317 trace_sst_ipc_outbox_read(bytes); 318 319 memcpy_fromio(message, sst->mailbox.out_base, bytes); 320 321 for (i = 0; i < bytes; i += 4) 322 trace_sst_ipc_outbox_rdata(i, *(u32 *)(message + i)); 323 } 324 EXPORT_SYMBOL_GPL(sst_dsp_outbox_read); 325 326 void sst_dsp_inbox_write(struct sst_dsp *sst, void *message, size_t bytes) 327 { 328 u32 i; 329 330 trace_sst_ipc_inbox_write(bytes); 331 332 memcpy_toio(sst->mailbox.in_base, message, bytes); 333 334 for (i = 0; i < bytes; i += 4) 335 trace_sst_ipc_inbox_wdata(i, *(u32 *)(message + i)); 336 } 337 EXPORT_SYMBOL_GPL(sst_dsp_inbox_write); 338 339 void sst_dsp_inbox_read(struct sst_dsp *sst, void *message, size_t bytes) 340 { 341 u32 i; 342 343 trace_sst_ipc_inbox_read(bytes); 344 345 memcpy_fromio(message, sst->mailbox.in_base, bytes); 346 347 for (i = 0; i < bytes; i += 4) 348 trace_sst_ipc_inbox_rdata(i, *(u32 *)(message + i)); 349 } 350 EXPORT_SYMBOL_GPL(sst_dsp_inbox_read); 351 352 struct sst_dsp *sst_dsp_new(struct device *dev, 353 struct sst_dsp_device *sst_dev, struct sst_pdata *pdata) 354 { 355 struct sst_dsp *sst; 356 int err; 357 358 dev_dbg(dev, "initialising audio DSP id 0x%x\n", pdata->id); 359 360 sst = devm_kzalloc(dev, sizeof(*sst), GFP_KERNEL); 361 if (sst == NULL) 362 return NULL; 363 364 spin_lock_init(&sst->spinlock); 365 mutex_init(&sst->mutex); 366 sst->dev = dev; 367 sst->dma_dev = pdata->dma_dev; 368 sst->thread_context = sst_dev->thread_context; 369 sst->sst_dev = sst_dev; 370 sst->id = pdata->id; 371 sst->irq = pdata->irq; 372 sst->ops = sst_dev->ops; 373 sst->pdata = pdata; 374 INIT_LIST_HEAD(&sst->used_block_list); 375 INIT_LIST_HEAD(&sst->free_block_list); 376 INIT_LIST_HEAD(&sst->module_list); 377 INIT_LIST_HEAD(&sst->fw_list); 378 INIT_LIST_HEAD(&sst->scratch_block_list); 379 380 /* Initialise SST Audio DSP */ 381 if (sst->ops->init) { 382 err = sst->ops->init(sst, pdata); 383 if (err < 0) 384 return NULL; 385 } 386 387 /* Register the ISR */ 388 err = request_threaded_irq(sst->irq, sst->ops->irq_handler, 389 sst_dev->thread, IRQF_SHARED, "AudioDSP", sst); 390 if (err) 391 goto irq_err; 392 393 err = sst_dma_new(sst); 394 if (err) 395 dev_warn(dev, "sst_dma_new failed %d\n", err); 396 397 return sst; 398 399 irq_err: 400 if (sst->ops->free) 401 sst->ops->free(sst); 402 403 return NULL; 404 } 405 EXPORT_SYMBOL_GPL(sst_dsp_new); 406 407 void sst_dsp_free(struct sst_dsp *sst) 408 { 409 free_irq(sst->irq, sst); 410 if (sst->ops->free) 411 sst->ops->free(sst); 412 413 sst_dma_free(sst->dma); 414 } 415 EXPORT_SYMBOL_GPL(sst_dsp_free); 416 417 /* Module information */ 418 MODULE_AUTHOR("Liam Girdwood"); 419 MODULE_DESCRIPTION("Intel SST Core"); 420 MODULE_LICENSE("GPL v2"); 421