Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ASoC: SOF: Add DSP HW abstraction operations

Add operation pointers that can be called by core to control a wide
variety of DSP targets. The DSP HW drivers will fill in these
operations.

Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Reviewed-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Mark Brown <broonie@kernel.org>

authored by

Liam Girdwood and committed by
Mark Brown
d1d95fcb fd51c47b

+562
+163
sound/soc/sof/ops.c
··· 1 + // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) 2 + // 3 + // This file is provided under a dual BSD/GPLv2 license. When using or 4 + // redistributing this file, you may do so under either license. 5 + // 6 + // Copyright(c) 2018 Intel Corporation. All rights reserved. 7 + // 8 + // Author: Liam Girdwood <liam.r.girdwood@linux.intel.com> 9 + // 10 + 11 + #include <linux/pci.h> 12 + #include "ops.h" 13 + 14 + static 15 + bool snd_sof_pci_update_bits_unlocked(struct snd_sof_dev *sdev, u32 offset, 16 + u32 mask, u32 value) 17 + { 18 + struct pci_dev *pci = to_pci_dev(sdev->dev); 19 + unsigned int old, new; 20 + u32 ret; 21 + 22 + pci_read_config_dword(pci, offset, &ret); 23 + old = ret; 24 + dev_dbg(sdev->dev, "Debug PCIR: %8.8x at %8.8x\n", old & mask, offset); 25 + 26 + new = (old & ~mask) | (value & mask); 27 + 28 + if (old == new) 29 + return false; 30 + 31 + pci_write_config_dword(pci, offset, new); 32 + dev_dbg(sdev->dev, "Debug PCIW: %8.8x at %8.8x\n", value, 33 + offset); 34 + 35 + return true; 36 + } 37 + 38 + bool snd_sof_pci_update_bits(struct snd_sof_dev *sdev, u32 offset, 39 + u32 mask, u32 value) 40 + { 41 + unsigned long flags; 42 + bool change; 43 + 44 + spin_lock_irqsave(&sdev->hw_lock, flags); 45 + change = snd_sof_pci_update_bits_unlocked(sdev, offset, mask, value); 46 + spin_unlock_irqrestore(&sdev->hw_lock, flags); 47 + return change; 48 + } 49 + EXPORT_SYMBOL(snd_sof_pci_update_bits); 50 + 51 + bool snd_sof_dsp_update_bits_unlocked(struct snd_sof_dev *sdev, u32 bar, 52 + u32 offset, u32 mask, u32 value) 53 + { 54 + unsigned int old, new; 55 + u32 ret; 56 + 57 + ret = snd_sof_dsp_read(sdev, bar, offset); 58 + 59 + old = ret; 60 + new = (old & ~mask) | (value & mask); 61 + 62 + if (old == new) 63 + return false; 64 + 65 + snd_sof_dsp_write(sdev, bar, offset, new); 66 + 67 + return true; 68 + } 69 + EXPORT_SYMBOL(snd_sof_dsp_update_bits_unlocked); 70 + 71 + bool snd_sof_dsp_update_bits64_unlocked(struct snd_sof_dev *sdev, u32 bar, 72 + u32 offset, u64 mask, u64 value) 73 + { 74 + u64 old, new; 75 + 76 + old = snd_sof_dsp_read64(sdev, bar, offset); 77 + 78 + new = (old & ~mask) | (value & mask); 79 + 80 + if (old == new) 81 + return false; 82 + 83 + snd_sof_dsp_write64(sdev, bar, offset, new); 84 + 85 + return true; 86 + } 87 + EXPORT_SYMBOL(snd_sof_dsp_update_bits64_unlocked); 88 + 89 + /* This is for registers bits with attribute RWC */ 90 + bool snd_sof_dsp_update_bits(struct snd_sof_dev *sdev, u32 bar, u32 offset, 91 + u32 mask, u32 value) 92 + { 93 + unsigned long flags; 94 + bool change; 95 + 96 + spin_lock_irqsave(&sdev->hw_lock, flags); 97 + change = snd_sof_dsp_update_bits_unlocked(sdev, bar, offset, mask, 98 + value); 99 + spin_unlock_irqrestore(&sdev->hw_lock, flags); 100 + return change; 101 + } 102 + EXPORT_SYMBOL(snd_sof_dsp_update_bits); 103 + 104 + bool snd_sof_dsp_update_bits64(struct snd_sof_dev *sdev, u32 bar, u32 offset, 105 + u64 mask, u64 value) 106 + { 107 + unsigned long flags; 108 + bool change; 109 + 110 + spin_lock_irqsave(&sdev->hw_lock, flags); 111 + change = snd_sof_dsp_update_bits64_unlocked(sdev, bar, offset, mask, 112 + value); 113 + spin_unlock_irqrestore(&sdev->hw_lock, flags); 114 + return change; 115 + } 116 + EXPORT_SYMBOL(snd_sof_dsp_update_bits64); 117 + 118 + static 119 + void snd_sof_dsp_update_bits_forced_unlocked(struct snd_sof_dev *sdev, u32 bar, 120 + u32 offset, u32 mask, u32 value) 121 + { 122 + unsigned int old, new; 123 + u32 ret; 124 + 125 + ret = snd_sof_dsp_read(sdev, bar, offset); 126 + 127 + old = ret; 128 + new = (old & ~mask) | (value & mask); 129 + 130 + snd_sof_dsp_write(sdev, bar, offset, new); 131 + } 132 + 133 + /* This is for registers bits with attribute RWC */ 134 + void snd_sof_dsp_update_bits_forced(struct snd_sof_dev *sdev, u32 bar, 135 + u32 offset, u32 mask, u32 value) 136 + { 137 + unsigned long flags; 138 + 139 + spin_lock_irqsave(&sdev->hw_lock, flags); 140 + snd_sof_dsp_update_bits_forced_unlocked(sdev, bar, offset, mask, value); 141 + spin_unlock_irqrestore(&sdev->hw_lock, flags); 142 + } 143 + EXPORT_SYMBOL(snd_sof_dsp_update_bits_forced); 144 + 145 + void snd_sof_dsp_panic(struct snd_sof_dev *sdev, u32 offset) 146 + { 147 + dev_err(sdev->dev, "error : DSP panic!\n"); 148 + 149 + /* 150 + * check if DSP is not ready and did not set the dsp_oops_offset. 151 + * if the dsp_oops_offset is not set, set it from the panic message. 152 + * Also add a check to memory window setting with panic message. 153 + */ 154 + if (!sdev->dsp_oops_offset) 155 + sdev->dsp_oops_offset = offset; 156 + else 157 + dev_dbg(sdev->dev, "panic: dsp_oops_offset %zu offset %d\n", 158 + sdev->dsp_oops_offset, offset); 159 + 160 + snd_sof_dsp_dbg_dump(sdev, SOF_DBG_REGS | SOF_DBG_MBOX); 161 + snd_sof_trace_notify_for_error(sdev); 162 + } 163 + EXPORT_SYMBOL(snd_sof_dsp_panic);
+399
sound/soc/sof/ops.h
··· 1 + /* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */ 2 + /* 3 + * This file is provided under a dual BSD/GPLv2 license. When using or 4 + * redistributing this file, you may do so under either license. 5 + * 6 + * Copyright(c) 2018 Intel Corporation. All rights reserved. 7 + * 8 + * Author: Liam Girdwood <liam.r.girdwood@linux.intel.com> 9 + */ 10 + 11 + #ifndef __SOUND_SOC_SOF_IO_H 12 + #define __SOUND_SOC_SOF_IO_H 13 + 14 + #include <linux/device.h> 15 + #include <linux/interrupt.h> 16 + #include <linux/kernel.h> 17 + #include <linux/types.h> 18 + #include <sound/pcm.h> 19 + #include "sof-priv.h" 20 + 21 + #define sof_ops(sdev) \ 22 + ((sdev)->pdata->desc->ops) 23 + 24 + /* Mandatory operations are verified during probing */ 25 + 26 + /* init */ 27 + static inline int snd_sof_probe(struct snd_sof_dev *sdev) 28 + { 29 + return sof_ops(sdev)->probe(sdev); 30 + } 31 + 32 + static inline int snd_sof_remove(struct snd_sof_dev *sdev) 33 + { 34 + if (sof_ops(sdev)->remove) 35 + return sof_ops(sdev)->remove(sdev); 36 + 37 + return 0; 38 + } 39 + 40 + /* control */ 41 + 42 + /* 43 + * snd_sof_dsp_run returns the core mask of the cores that are available 44 + * after successful fw boot 45 + */ 46 + static inline int snd_sof_dsp_run(struct snd_sof_dev *sdev) 47 + { 48 + return sof_ops(sdev)->run(sdev); 49 + } 50 + 51 + static inline int snd_sof_dsp_stall(struct snd_sof_dev *sdev) 52 + { 53 + if (sof_ops(sdev)->stall) 54 + return sof_ops(sdev)->stall(sdev); 55 + 56 + return 0; 57 + } 58 + 59 + static inline int snd_sof_dsp_reset(struct snd_sof_dev *sdev) 60 + { 61 + if (sof_ops(sdev)->reset) 62 + return sof_ops(sdev)->reset(sdev); 63 + 64 + return 0; 65 + } 66 + 67 + /* dsp core power up/power down */ 68 + static inline int snd_sof_dsp_core_power_up(struct snd_sof_dev *sdev, 69 + unsigned int core_mask) 70 + { 71 + if (sof_ops(sdev)->core_power_up) 72 + return sof_ops(sdev)->core_power_up(sdev, core_mask); 73 + 74 + return 0; 75 + } 76 + 77 + static inline int snd_sof_dsp_core_power_down(struct snd_sof_dev *sdev, 78 + unsigned int core_mask) 79 + { 80 + if (sof_ops(sdev)->core_power_down) 81 + return sof_ops(sdev)->core_power_down(sdev, core_mask); 82 + 83 + return 0; 84 + } 85 + 86 + /* pre/post fw load */ 87 + static inline int snd_sof_dsp_pre_fw_run(struct snd_sof_dev *sdev) 88 + { 89 + if (sof_ops(sdev)->pre_fw_run) 90 + return sof_ops(sdev)->pre_fw_run(sdev); 91 + 92 + return 0; 93 + } 94 + 95 + static inline int snd_sof_dsp_post_fw_run(struct snd_sof_dev *sdev) 96 + { 97 + if (sof_ops(sdev)->post_fw_run) 98 + return sof_ops(sdev)->post_fw_run(sdev); 99 + 100 + return 0; 101 + } 102 + 103 + /* power management */ 104 + static inline int snd_sof_dsp_resume(struct snd_sof_dev *sdev) 105 + { 106 + if (sof_ops(sdev)->resume) 107 + return sof_ops(sdev)->resume(sdev); 108 + 109 + return 0; 110 + } 111 + 112 + static inline int snd_sof_dsp_suspend(struct snd_sof_dev *sdev, int state) 113 + { 114 + if (sof_ops(sdev)->suspend) 115 + return sof_ops(sdev)->suspend(sdev, state); 116 + 117 + return 0; 118 + } 119 + 120 + static inline int snd_sof_dsp_runtime_resume(struct snd_sof_dev *sdev) 121 + { 122 + if (sof_ops(sdev)->runtime_resume) 123 + return sof_ops(sdev)->runtime_resume(sdev); 124 + 125 + return 0; 126 + } 127 + 128 + static inline int snd_sof_dsp_runtime_suspend(struct snd_sof_dev *sdev, 129 + int state) 130 + { 131 + if (sof_ops(sdev)->runtime_suspend) 132 + return sof_ops(sdev)->runtime_suspend(sdev, state); 133 + 134 + return 0; 135 + } 136 + 137 + static inline int snd_sof_dsp_set_clk(struct snd_sof_dev *sdev, u32 freq) 138 + { 139 + if (sof_ops(sdev)->set_clk) 140 + return sof_ops(sdev)->set_clk(sdev, freq); 141 + 142 + return 0; 143 + } 144 + 145 + /* debug */ 146 + static inline void snd_sof_dsp_dbg_dump(struct snd_sof_dev *sdev, u32 flags) 147 + { 148 + if (sof_ops(sdev)->dbg_dump) 149 + return sof_ops(sdev)->dbg_dump(sdev, flags); 150 + } 151 + 152 + /* register IO */ 153 + static inline void snd_sof_dsp_write(struct snd_sof_dev *sdev, u32 bar, 154 + u32 offset, u32 value) 155 + { 156 + if (sof_ops(sdev)->write) { 157 + sof_ops(sdev)->write(sdev, sdev->bar[bar] + offset, value); 158 + return; 159 + } 160 + 161 + dev_err_ratelimited(sdev->dev, "error: %s not defined\n", __func__); 162 + } 163 + 164 + static inline void snd_sof_dsp_write64(struct snd_sof_dev *sdev, u32 bar, 165 + u32 offset, u64 value) 166 + { 167 + if (sof_ops(sdev)->write64) { 168 + sof_ops(sdev)->write64(sdev, sdev->bar[bar] + offset, value); 169 + return; 170 + } 171 + 172 + dev_err_ratelimited(sdev->dev, "error: %s not defined\n", __func__); 173 + } 174 + 175 + static inline u32 snd_sof_dsp_read(struct snd_sof_dev *sdev, u32 bar, 176 + u32 offset) 177 + { 178 + if (sof_ops(sdev)->read) 179 + return sof_ops(sdev)->read(sdev, sdev->bar[bar] + offset); 180 + 181 + dev_err(sdev->dev, "error: %s not defined\n", __func__); 182 + return -ENOTSUPP; 183 + } 184 + 185 + static inline u64 snd_sof_dsp_read64(struct snd_sof_dev *sdev, u32 bar, 186 + u32 offset) 187 + { 188 + if (sof_ops(sdev)->read64) 189 + return sof_ops(sdev)->read64(sdev, sdev->bar[bar] + offset); 190 + 191 + dev_err(sdev->dev, "error: %s not defined\n", __func__); 192 + return -ENOTSUPP; 193 + } 194 + 195 + /* block IO */ 196 + static inline void snd_sof_dsp_block_read(struct snd_sof_dev *sdev, u32 bar, 197 + u32 offset, void *dest, size_t bytes) 198 + { 199 + sof_ops(sdev)->block_read(sdev, bar, offset, dest, bytes); 200 + } 201 + 202 + static inline void snd_sof_dsp_block_write(struct snd_sof_dev *sdev, u32 bar, 203 + u32 offset, void *src, size_t bytes) 204 + { 205 + sof_ops(sdev)->block_write(sdev, bar, offset, src, bytes); 206 + } 207 + 208 + /* ipc */ 209 + static inline int snd_sof_dsp_send_msg(struct snd_sof_dev *sdev, 210 + struct snd_sof_ipc_msg *msg) 211 + { 212 + return sof_ops(sdev)->send_msg(sdev, msg); 213 + } 214 + 215 + /* host DMA trace */ 216 + static inline int snd_sof_dma_trace_init(struct snd_sof_dev *sdev, 217 + u32 *stream_tag) 218 + { 219 + if (sof_ops(sdev)->trace_init) 220 + return sof_ops(sdev)->trace_init(sdev, stream_tag); 221 + 222 + return 0; 223 + } 224 + 225 + static inline int snd_sof_dma_trace_release(struct snd_sof_dev *sdev) 226 + { 227 + if (sof_ops(sdev)->trace_release) 228 + return sof_ops(sdev)->trace_release(sdev); 229 + 230 + return 0; 231 + } 232 + 233 + static inline int snd_sof_dma_trace_trigger(struct snd_sof_dev *sdev, int cmd) 234 + { 235 + if (sof_ops(sdev)->trace_trigger) 236 + return sof_ops(sdev)->trace_trigger(sdev, cmd); 237 + 238 + return 0; 239 + } 240 + 241 + /* host PCM ops */ 242 + static inline int 243 + snd_sof_pcm_platform_open(struct snd_sof_dev *sdev, 244 + struct snd_pcm_substream *substream) 245 + { 246 + if (sof_ops(sdev) && sof_ops(sdev)->pcm_open) 247 + return sof_ops(sdev)->pcm_open(sdev, substream); 248 + 249 + return 0; 250 + } 251 + 252 + /* disconnect pcm substream to a host stream */ 253 + static inline int 254 + snd_sof_pcm_platform_close(struct snd_sof_dev *sdev, 255 + struct snd_pcm_substream *substream) 256 + { 257 + if (sof_ops(sdev) && sof_ops(sdev)->pcm_close) 258 + return sof_ops(sdev)->pcm_close(sdev, substream); 259 + 260 + return 0; 261 + } 262 + 263 + /* host stream hw params */ 264 + static inline int 265 + snd_sof_pcm_platform_hw_params(struct snd_sof_dev *sdev, 266 + struct snd_pcm_substream *substream, 267 + struct snd_pcm_hw_params *params, 268 + struct sof_ipc_stream_params *ipc_params) 269 + { 270 + if (sof_ops(sdev) && sof_ops(sdev)->pcm_hw_params) 271 + return sof_ops(sdev)->pcm_hw_params(sdev, substream, 272 + params, ipc_params); 273 + 274 + return 0; 275 + } 276 + 277 + /* host stream trigger */ 278 + static inline int 279 + snd_sof_pcm_platform_trigger(struct snd_sof_dev *sdev, 280 + struct snd_pcm_substream *substream, int cmd) 281 + { 282 + if (sof_ops(sdev) && sof_ops(sdev)->pcm_trigger) 283 + return sof_ops(sdev)->pcm_trigger(sdev, substream, cmd); 284 + 285 + return 0; 286 + } 287 + 288 + /* host DSP message data */ 289 + static inline void snd_sof_ipc_msg_data(struct snd_sof_dev *sdev, 290 + struct snd_pcm_substream *substream, 291 + void *p, size_t sz) 292 + { 293 + sof_ops(sdev)->ipc_msg_data(sdev, substream, p, sz); 294 + } 295 + 296 + /* host configure DSP HW parameters */ 297 + static inline int 298 + snd_sof_ipc_pcm_params(struct snd_sof_dev *sdev, 299 + struct snd_pcm_substream *substream, 300 + const struct sof_ipc_pcm_params_reply *reply) 301 + { 302 + return sof_ops(sdev)->ipc_pcm_params(sdev, substream, reply); 303 + } 304 + 305 + /* host stream pointer */ 306 + static inline snd_pcm_uframes_t 307 + snd_sof_pcm_platform_pointer(struct snd_sof_dev *sdev, 308 + struct snd_pcm_substream *substream) 309 + { 310 + if (sof_ops(sdev) && sof_ops(sdev)->pcm_pointer) 311 + return sof_ops(sdev)->pcm_pointer(sdev, substream); 312 + 313 + return 0; 314 + } 315 + 316 + static inline const struct snd_sof_dsp_ops 317 + *sof_get_ops(const struct sof_dev_desc *d, 318 + const struct sof_ops_table mach_ops[], int asize) 319 + { 320 + int i; 321 + 322 + for (i = 0; i < asize; i++) { 323 + if (d == mach_ops[i].desc) 324 + return mach_ops[i].ops; 325 + } 326 + 327 + /* not found */ 328 + return NULL; 329 + } 330 + 331 + /** 332 + * snd_sof_dsp_register_poll_timeout - Periodically poll an address 333 + * until a condition is met or a timeout occurs 334 + * @op: accessor function (takes @addr as its only argument) 335 + * @addr: Address to poll 336 + * @val: Variable to read the value into 337 + * @cond: Break condition (usually involving @val) 338 + * @sleep_us: Maximum time to sleep between reads in us (0 339 + * tight-loops). Should be less than ~20ms since usleep_range 340 + * is used (see Documentation/timers/timers-howto.txt). 341 + * @timeout_us: Timeout in us, 0 means never timeout 342 + * 343 + * Returns 0 on success and -ETIMEDOUT upon a timeout. In either 344 + * case, the last read value at @addr is stored in @val. Must not 345 + * be called from atomic context if sleep_us or timeout_us are used. 346 + * 347 + * This is modelled after the readx_poll_timeout macros in linux/iopoll.h. 348 + */ 349 + #define snd_sof_dsp_read_poll_timeout(sdev, bar, offset, val, cond, sleep_us, timeout_us) \ 350 + ({ \ 351 + u64 __timeout_us = (timeout_us); \ 352 + unsigned long __sleep_us = (sleep_us); \ 353 + ktime_t __timeout = ktime_add_us(ktime_get(), __timeout_us); \ 354 + might_sleep_if((__sleep_us) != 0); \ 355 + for (;;) { \ 356 + (val) = snd_sof_dsp_read(sdev, bar, offset); \ 357 + if (cond) { \ 358 + dev_dbg(sdev->dev, \ 359 + "FW Poll Status: reg=%#x successful\n", (val)); \ 360 + break; \ 361 + } \ 362 + if (__timeout_us && \ 363 + ktime_compare(ktime_get(), __timeout) > 0) { \ 364 + (val) = snd_sof_dsp_read(sdev, bar, offset); \ 365 + dev_dbg(sdev->dev, \ 366 + "FW Poll Status: reg=%#x timedout\n", (val)); \ 367 + break; \ 368 + } \ 369 + if (__sleep_us) \ 370 + usleep_range((__sleep_us >> 2) + 1, __sleep_us); \ 371 + } \ 372 + (cond) ? 0 : -ETIMEDOUT; \ 373 + }) 374 + 375 + /* This is for registers bits with attribute RWC */ 376 + bool snd_sof_pci_update_bits(struct snd_sof_dev *sdev, u32 offset, 377 + u32 mask, u32 value); 378 + 379 + bool snd_sof_dsp_update_bits_unlocked(struct snd_sof_dev *sdev, u32 bar, 380 + u32 offset, u32 mask, u32 value); 381 + 382 + bool snd_sof_dsp_update_bits64_unlocked(struct snd_sof_dev *sdev, u32 bar, 383 + u32 offset, u64 mask, u64 value); 384 + 385 + bool snd_sof_dsp_update_bits(struct snd_sof_dev *sdev, u32 bar, u32 offset, 386 + u32 mask, u32 value); 387 + 388 + bool snd_sof_dsp_update_bits64(struct snd_sof_dev *sdev, u32 bar, 389 + u32 offset, u64 mask, u64 value); 390 + 391 + void snd_sof_dsp_update_bits_forced(struct snd_sof_dev *sdev, u32 bar, 392 + u32 offset, u32 mask, u32 value); 393 + 394 + int snd_sof_dsp_register_poll(struct snd_sof_dev *sdev, u32 bar, u32 offset, 395 + u32 mask, u32 target, u32 timeout_ms, 396 + u32 interval_us); 397 + 398 + void snd_sof_dsp_panic(struct snd_sof_dev *sdev, u32 offset); 399 + #endif