diff options
author | Jayachandran B <jayachandran.b@intel.com> | 2016-11-03 07:37:17 -0400 |
---|---|---|
committer | Mark Brown <broonie@kernel.org> | 2016-11-03 13:14:22 -0400 |
commit | 5bb4cd46ace5f220fbc34370e7fe9ba515ead9f8 (patch) | |
tree | 4a604053780d18f5450c7d022559cafe9ff62c6a | |
parent | a26a3f53e3d2bfeb666ca31b5f86c65a1816eb89 (diff) |
ASoC: Intel: Skylake: Add D0iX callbacks
The driver needs two DSP callback, one to set D0i0 (active) and D0i3
(low-power) states.
Add these callbacks in dsp ops and implement them for broxton platforms.
Signed-off-by: Jayachandran B <jayachandran.b@intel.com>
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
-rw-r--r-- | sound/soc/intel/skylake/bxt-sst.c | 140 | ||||
-rw-r--r-- | sound/soc/intel/skylake/skl-sst-dsp.h | 12 | ||||
-rw-r--r-- | sound/soc/intel/skylake/skl-sst-ipc.h | 19 |
3 files changed, 171 insertions, 0 deletions
diff --git a/sound/soc/intel/skylake/bxt-sst.c b/sound/soc/intel/skylake/bxt-sst.c index 1d251d59bcb9..fed818fe2e69 100644 --- a/sound/soc/intel/skylake/bxt-sst.c +++ b/sound/soc/intel/skylake/bxt-sst.c | |||
@@ -43,6 +43,9 @@ | |||
43 | 43 | ||
44 | #define BXT_ADSP_FW_BIN_HDR_OFFSET 0x2000 | 44 | #define BXT_ADSP_FW_BIN_HDR_OFFSET 0x2000 |
45 | 45 | ||
46 | /* Delay before scheduling D0i3 entry */ | ||
47 | #define BXT_D0I3_DELAY 5000 | ||
48 | |||
46 | static unsigned int bxt_get_errorcode(struct sst_dsp *ctx) | 49 | static unsigned int bxt_get_errorcode(struct sst_dsp *ctx) |
47 | { | 50 | { |
48 | return sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE); | 51 | return sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE); |
@@ -288,6 +291,141 @@ sst_load_base_firmware_failed: | |||
288 | return ret; | 291 | return ret; |
289 | } | 292 | } |
290 | 293 | ||
294 | /* | ||
295 | * Decide the D0i3 state that can be targeted based on the usecase | ||
296 | * ref counts and DSP state | ||
297 | * | ||
298 | * Decision Matrix: (X= dont care; state = target state) | ||
299 | * | ||
300 | * DSP state != SKL_DSP_RUNNING ; state = no d0i3 | ||
301 | * | ||
302 | * DSP state == SKL_DSP_RUNNING , the following matrix applies | ||
303 | * non_d0i3 >0; streaming =X; non_streaming =X; state = no d0i3 | ||
304 | * non_d0i3 =X; streaming =0; non_streaming =0; state = no d0i3 | ||
305 | * non_d0i3 =0; streaming >0; non_streaming =X; state = streaming d0i3 | ||
306 | * non_d0i3 =0; streaming =0; non_streaming =X; state = non-streaming d0i3 | ||
307 | */ | ||
308 | static int bxt_d0i3_target_state(struct sst_dsp *ctx) | ||
309 | { | ||
310 | struct skl_sst *skl = ctx->thread_context; | ||
311 | struct skl_d0i3_data *d0i3 = &skl->d0i3; | ||
312 | |||
313 | if (skl->cores.state[SKL_DSP_CORE0_ID] != SKL_DSP_RUNNING) | ||
314 | return SKL_DSP_D0I3_NONE; | ||
315 | |||
316 | if (d0i3->non_d0i3) | ||
317 | return SKL_DSP_D0I3_NONE; | ||
318 | else if (d0i3->streaming) | ||
319 | return SKL_DSP_D0I3_STREAMING; | ||
320 | else if (d0i3->non_streaming) | ||
321 | return SKL_DSP_D0I3_NON_STREAMING; | ||
322 | else | ||
323 | return SKL_DSP_D0I3_NONE; | ||
324 | } | ||
325 | |||
326 | static void bxt_set_dsp_D0i3(struct work_struct *work) | ||
327 | { | ||
328 | int ret; | ||
329 | struct skl_ipc_d0ix_msg msg; | ||
330 | struct skl_sst *skl = container_of(work, | ||
331 | struct skl_sst, d0i3.work.work); | ||
332 | struct sst_dsp *ctx = skl->dsp; | ||
333 | struct skl_d0i3_data *d0i3 = &skl->d0i3; | ||
334 | int target_state; | ||
335 | |||
336 | dev_dbg(ctx->dev, "In %s:\n", __func__); | ||
337 | |||
338 | /* D0i3 entry allowed only if core 0 alone is running */ | ||
339 | if (skl_dsp_get_enabled_cores(ctx) != SKL_DSP_CORE0_MASK) { | ||
340 | dev_warn(ctx->dev, | ||
341 | "D0i3 allowed when only core0 running:Exit\n"); | ||
342 | return; | ||
343 | } | ||
344 | |||
345 | target_state = bxt_d0i3_target_state(ctx); | ||
346 | if (target_state == SKL_DSP_D0I3_NONE) | ||
347 | return; | ||
348 | |||
349 | msg.instance_id = 0; | ||
350 | msg.module_id = 0; | ||
351 | msg.wake = 1; | ||
352 | msg.streaming = 0; | ||
353 | if (target_state == SKL_DSP_D0I3_STREAMING) | ||
354 | msg.streaming = 1; | ||
355 | |||
356 | ret = skl_ipc_set_d0ix(&skl->ipc, &msg); | ||
357 | |||
358 | if (ret < 0) { | ||
359 | dev_err(ctx->dev, "Failed to set DSP to D0i3 state\n"); | ||
360 | return; | ||
361 | } | ||
362 | |||
363 | /* Set Vendor specific register D0I3C.I3 to enable D0i3*/ | ||
364 | if (skl->update_d0i3c) | ||
365 | skl->update_d0i3c(skl->dev, true); | ||
366 | |||
367 | d0i3->state = target_state; | ||
368 | skl->cores.state[SKL_DSP_CORE0_ID] = SKL_DSP_RUNNING_D0I3; | ||
369 | } | ||
370 | |||
371 | static int bxt_schedule_dsp_D0i3(struct sst_dsp *ctx) | ||
372 | { | ||
373 | struct skl_sst *skl = ctx->thread_context; | ||
374 | struct skl_d0i3_data *d0i3 = &skl->d0i3; | ||
375 | |||
376 | /* Schedule D0i3 only if the usecase ref counts are appropriate */ | ||
377 | if (bxt_d0i3_target_state(ctx) != SKL_DSP_D0I3_NONE) { | ||
378 | |||
379 | dev_dbg(ctx->dev, "%s: Schedule D0i3\n", __func__); | ||
380 | |||
381 | schedule_delayed_work(&d0i3->work, | ||
382 | msecs_to_jiffies(BXT_D0I3_DELAY)); | ||
383 | } | ||
384 | |||
385 | return 0; | ||
386 | } | ||
387 | |||
388 | static int bxt_set_dsp_D0i0(struct sst_dsp *ctx) | ||
389 | { | ||
390 | int ret; | ||
391 | struct skl_ipc_d0ix_msg msg; | ||
392 | struct skl_sst *skl = ctx->thread_context; | ||
393 | |||
394 | dev_dbg(ctx->dev, "In %s:\n", __func__); | ||
395 | |||
396 | /* First Cancel any pending attempt to put DSP to D0i3 */ | ||
397 | cancel_delayed_work_sync(&skl->d0i3.work); | ||
398 | |||
399 | /* If DSP is currently in D0i3, bring it to D0i0 */ | ||
400 | if (skl->cores.state[SKL_DSP_CORE0_ID] != SKL_DSP_RUNNING_D0I3) | ||
401 | return 0; | ||
402 | |||
403 | dev_dbg(ctx->dev, "Set DSP to D0i0\n"); | ||
404 | |||
405 | msg.instance_id = 0; | ||
406 | msg.module_id = 0; | ||
407 | msg.streaming = 0; | ||
408 | msg.wake = 0; | ||
409 | |||
410 | if (skl->d0i3.state == SKL_DSP_D0I3_STREAMING) | ||
411 | msg.streaming = 1; | ||
412 | |||
413 | /* Clear Vendor specific register D0I3C.I3 to disable D0i3*/ | ||
414 | if (skl->update_d0i3c) | ||
415 | skl->update_d0i3c(skl->dev, false); | ||
416 | |||
417 | ret = skl_ipc_set_d0ix(&skl->ipc, &msg); | ||
418 | if (ret < 0) { | ||
419 | dev_err(ctx->dev, "Failed to set DSP to D0i0\n"); | ||
420 | return ret; | ||
421 | } | ||
422 | |||
423 | skl->cores.state[SKL_DSP_CORE0_ID] = SKL_DSP_RUNNING; | ||
424 | skl->d0i3.state = SKL_DSP_D0I3_NONE; | ||
425 | |||
426 | return 0; | ||
427 | } | ||
428 | |||
291 | static int bxt_set_dsp_D0(struct sst_dsp *ctx, unsigned int core_id) | 429 | static int bxt_set_dsp_D0(struct sst_dsp *ctx, unsigned int core_id) |
292 | { | 430 | { |
293 | struct skl_sst *skl = ctx->thread_context; | 431 | struct skl_sst *skl = ctx->thread_context; |
@@ -414,6 +552,8 @@ static int bxt_set_dsp_D3(struct sst_dsp *ctx, unsigned int core_id) | |||
414 | static struct skl_dsp_fw_ops bxt_fw_ops = { | 552 | static struct skl_dsp_fw_ops bxt_fw_ops = { |
415 | .set_state_D0 = bxt_set_dsp_D0, | 553 | .set_state_D0 = bxt_set_dsp_D0, |
416 | .set_state_D3 = bxt_set_dsp_D3, | 554 | .set_state_D3 = bxt_set_dsp_D3, |
555 | .set_state_D0i3 = bxt_schedule_dsp_D0i3, | ||
556 | .set_state_D0i0 = bxt_set_dsp_D0i0, | ||
417 | .load_fw = bxt_load_base_firmware, | 557 | .load_fw = bxt_load_base_firmware, |
418 | .get_fw_errcode = bxt_get_errorcode, | 558 | .get_fw_errcode = bxt_get_errorcode, |
419 | .load_library = bxt_load_library, | 559 | .load_library = bxt_load_library, |
diff --git a/sound/soc/intel/skylake/skl-sst-dsp.h b/sound/soc/intel/skylake/skl-sst-dsp.h index b9e71d051fb1..7c272ba0f4b5 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.h +++ b/sound/soc/intel/skylake/skl-sst-dsp.h | |||
@@ -126,11 +126,21 @@ struct sst_dsp_device; | |||
126 | #define SKL_ADSPCS_CPA_SHIFT 24 | 126 | #define SKL_ADSPCS_CPA_SHIFT 24 |
127 | #define SKL_ADSPCS_CPA_MASK(cm) ((cm) << SKL_ADSPCS_CPA_SHIFT) | 127 | #define SKL_ADSPCS_CPA_MASK(cm) ((cm) << SKL_ADSPCS_CPA_SHIFT) |
128 | 128 | ||
129 | /* DSP Core state */ | ||
129 | enum skl_dsp_states { | 130 | enum skl_dsp_states { |
130 | SKL_DSP_RUNNING = 1, | 131 | SKL_DSP_RUNNING = 1, |
132 | /* Running in D0i3 state; can be in streaming or non-streaming D0i3 */ | ||
133 | SKL_DSP_RUNNING_D0I3, /* Running in D0i3 state*/ | ||
131 | SKL_DSP_RESET, | 134 | SKL_DSP_RESET, |
132 | }; | 135 | }; |
133 | 136 | ||
137 | /* D0i3 substates */ | ||
138 | enum skl_dsp_d0i3_states { | ||
139 | SKL_DSP_D0I3_NONE = -1, /* No D0i3 */ | ||
140 | SKL_DSP_D0I3_NON_STREAMING = 0, | ||
141 | SKL_DSP_D0I3_STREAMING = 1, | ||
142 | }; | ||
143 | |||
134 | struct skl_dsp_fw_ops { | 144 | struct skl_dsp_fw_ops { |
135 | int (*load_fw)(struct sst_dsp *ctx); | 145 | int (*load_fw)(struct sst_dsp *ctx); |
136 | /* FW module parser/loader */ | 146 | /* FW module parser/loader */ |
@@ -139,6 +149,8 @@ struct skl_dsp_fw_ops { | |||
139 | int (*parse_fw)(struct sst_dsp *ctx); | 149 | int (*parse_fw)(struct sst_dsp *ctx); |
140 | int (*set_state_D0)(struct sst_dsp *ctx, unsigned int core_id); | 150 | int (*set_state_D0)(struct sst_dsp *ctx, unsigned int core_id); |
141 | int (*set_state_D3)(struct sst_dsp *ctx, unsigned int core_id); | 151 | int (*set_state_D3)(struct sst_dsp *ctx, unsigned int core_id); |
152 | int (*set_state_D0i3)(struct sst_dsp *ctx); | ||
153 | int (*set_state_D0i0)(struct sst_dsp *ctx); | ||
142 | unsigned int (*get_fw_errcode)(struct sst_dsp *ctx); | 154 | unsigned int (*get_fw_errcode)(struct sst_dsp *ctx); |
143 | int (*load_mod)(struct sst_dsp *ctx, u16 mod_id, u8 *mod_name); | 155 | int (*load_mod)(struct sst_dsp *ctx, u16 mod_id, u8 *mod_name); |
144 | int (*unload_mod)(struct sst_dsp *ctx, u16 mod_id); | 156 | int (*unload_mod)(struct sst_dsp *ctx, u16 mod_id); |
diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h index ef2182d21934..a45c42046b64 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.h +++ b/sound/soc/intel/skylake/skl-sst-ipc.h | |||
@@ -53,6 +53,23 @@ struct skl_dsp_cores { | |||
53 | int usage_count[SKL_DSP_CORES_MAX]; | 53 | int usage_count[SKL_DSP_CORES_MAX]; |
54 | }; | 54 | }; |
55 | 55 | ||
56 | /** | ||
57 | * skl_d0i3_data: skl D0i3 counters data struct | ||
58 | * | ||
59 | * @streaming: Count of usecases that can attempt streaming D0i3 | ||
60 | * @non_streaming: Count of usecases that can attempt non-streaming D0i3 | ||
61 | * @non_d0i3: Count of usecases that cannot attempt D0i3 | ||
62 | * @state: current state | ||
63 | * @work: D0i3 worker thread | ||
64 | */ | ||
65 | struct skl_d0i3_data { | ||
66 | int streaming; | ||
67 | int non_streaming; | ||
68 | int non_d0i3; | ||
69 | enum skl_dsp_d0i3_states state; | ||
70 | struct delayed_work work; | ||
71 | }; | ||
72 | |||
56 | struct skl_sst { | 73 | struct skl_sst { |
57 | struct device *dev; | 74 | struct device *dev; |
58 | struct sst_dsp *dsp; | 75 | struct sst_dsp *dsp; |
@@ -86,6 +103,8 @@ struct skl_sst { | |||
86 | 103 | ||
87 | /* Callback to update D0i3C register */ | 104 | /* Callback to update D0i3C register */ |
88 | void (*update_d0i3c)(struct device *dev, bool enable); | 105 | void (*update_d0i3c)(struct device *dev, bool enable); |
106 | |||
107 | struct skl_d0i3_data d0i3; | ||
89 | }; | 108 | }; |
90 | 109 | ||
91 | struct skl_ipc_init_instance_msg { | 110 | struct skl_ipc_init_instance_msg { |