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 */ 31void sst_shim32_write(void __iomem *addr, u32 offset, u32 value) 32{ 33 writel(value, addr + offset); 34} 35EXPORT_SYMBOL_GPL(sst_shim32_write); 36 37u32 sst_shim32_read(void __iomem *addr, u32 offset) 38{ 39 return readl(addr + offset); 40} 41EXPORT_SYMBOL_GPL(sst_shim32_read); 42 43void sst_shim32_write64(void __iomem *addr, u32 offset, u64 value) 44{ 45 memcpy_toio(addr + offset, &value, sizeof(value)); 46} 47EXPORT_SYMBOL_GPL(sst_shim32_write64); 48 49u64 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} 56EXPORT_SYMBOL_GPL(sst_shim32_read64); 57 58static 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 67static 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 76void 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} 81EXPORT_SYMBOL_GPL(sst_memcpy_toio_32); 82 83void 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} 88EXPORT_SYMBOL_GPL(sst_memcpy_fromio_32); 89 90/* Public API */ 91void 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} 99EXPORT_SYMBOL_GPL(sst_dsp_shim_write); 100 101u32 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} 112EXPORT_SYMBOL_GPL(sst_dsp_shim_read); 113 114void 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} 122EXPORT_SYMBOL_GPL(sst_dsp_shim_write64); 123 124u64 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} 135EXPORT_SYMBOL_GPL(sst_dsp_shim_read64); 136 137void sst_dsp_shim_write_unlocked(struct sst_dsp *sst, u32 offset, u32 value) 138{ 139 sst->ops->write(sst->addr.shim, offset, value); 140} 141EXPORT_SYMBOL_GPL(sst_dsp_shim_write_unlocked); 142 143u32 sst_dsp_shim_read_unlocked(struct sst_dsp *sst, u32 offset) 144{ 145 return sst->ops->read(sst->addr.shim, offset); 146} 147EXPORT_SYMBOL_GPL(sst_dsp_shim_read_unlocked); 148 149void sst_dsp_shim_write64_unlocked(struct sst_dsp *sst, u32 offset, u64 value) 150{ 151 sst->ops->write64(sst->addr.shim, offset, value); 152} 153EXPORT_SYMBOL_GPL(sst_dsp_shim_write64_unlocked); 154 155u64 sst_dsp_shim_read64_unlocked(struct sst_dsp *sst, u32 offset) 156{ 157 return sst->ops->read64(sst->addr.shim, offset); 158} 159EXPORT_SYMBOL_GPL(sst_dsp_shim_read64_unlocked); 160 161int 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} 179EXPORT_SYMBOL_GPL(sst_dsp_shim_update_bits_unlocked); 180 181int 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} 197EXPORT_SYMBOL_GPL(sst_dsp_shim_update_bits64_unlocked); 198 199int 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} 210EXPORT_SYMBOL_GPL(sst_dsp_shim_update_bits); 211 212int 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} 223EXPORT_SYMBOL_GPL(sst_dsp_shim_update_bits64); 224 225void sst_dsp_dump(struct sst_dsp *sst) 226{ 227 if (sst->ops->dump) 228 sst->ops->dump(sst); 229} 230EXPORT_SYMBOL_GPL(sst_dsp_dump); 231 232void sst_dsp_reset(struct sst_dsp *sst) 233{ 234 if (sst->ops->reset) 235 sst->ops->reset(sst); 236} 237EXPORT_SYMBOL_GPL(sst_dsp_reset); 238 239int sst_dsp_boot(struct sst_dsp *sst) 240{ 241 if (sst->ops->boot) 242 sst->ops->boot(sst); 243 244 return 0; 245} 246EXPORT_SYMBOL_GPL(sst_dsp_boot); 247 248int sst_dsp_wake(struct sst_dsp *sst) 249{ 250 if (sst->ops->wake) 251 return sst->ops->wake(sst); 252 253 return 0; 254} 255EXPORT_SYMBOL_GPL(sst_dsp_wake); 256 257void sst_dsp_sleep(struct sst_dsp *sst) 258{ 259 if (sst->ops->sleep) 260 sst->ops->sleep(sst); 261} 262EXPORT_SYMBOL_GPL(sst_dsp_sleep); 263 264void sst_dsp_stall(struct sst_dsp *sst) 265{ 266 if (sst->ops->stall) 267 sst->ops->stall(sst); 268} 269EXPORT_SYMBOL_GPL(sst_dsp_stall); 270 271void 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} 276EXPORT_SYMBOL_GPL(sst_dsp_ipc_msg_tx); 277 278u32 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} 287EXPORT_SYMBOL_GPL(sst_dsp_ipc_msg_rx); 288 289int 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} 298EXPORT_SYMBOL_GPL(sst_dsp_mailbox_init); 299 300void 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} 311EXPORT_SYMBOL_GPL(sst_dsp_outbox_write); 312 313void 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} 324EXPORT_SYMBOL_GPL(sst_dsp_outbox_read); 325 326void 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} 337EXPORT_SYMBOL_GPL(sst_dsp_inbox_write); 338 339void 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} 350EXPORT_SYMBOL_GPL(sst_dsp_inbox_read); 351 352struct 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 399irq_err: 400 if (sst->ops->free) 401 sst->ops->free(sst); 402 403 return NULL; 404} 405EXPORT_SYMBOL_GPL(sst_dsp_new); 406 407void 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} 415EXPORT_SYMBOL_GPL(sst_dsp_free); 416 417/* Module information */ 418MODULE_AUTHOR("Liam Girdwood"); 419MODULE_DESCRIPTION("Intel SST Core"); 420MODULE_LICENSE("GPL v2"); 421