forked from Minki/linux
ASoC: wm_adsp: Signal firmware shutdown through event control
If the firmware has any system event signalling controls, signal them during DSP PRE_PMD to tell the firmware it is about to be stopped. Signed-off-by: Richard Fitzgerald <rf@opensource.wolfsonmicro.com> Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
parent
88c1886075
commit
f4f0c4c60c
@ -162,6 +162,14 @@
|
||||
|
||||
#define ADSP_MAX_STD_CTRL_SIZE 512
|
||||
|
||||
#define WM_ADSP_ACKED_CTL_TIMEOUT_MS 100
|
||||
#define WM_ADSP_ACKED_CTL_N_QUICKPOLLS 10
|
||||
|
||||
/*
|
||||
* Event control messages
|
||||
*/
|
||||
#define WM_ADSP_FW_EVENT_SHUTDOWN 0x000001
|
||||
|
||||
struct wm_adsp_buf {
|
||||
struct list_head list;
|
||||
void *buf;
|
||||
@ -739,6 +747,66 @@ static int wm_coeff_info(struct snd_kcontrol *kctl,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wm_coeff_write_acked_control(struct wm_coeff_ctl *ctl,
|
||||
unsigned int event_id)
|
||||
{
|
||||
struct wm_adsp *dsp = ctl->dsp;
|
||||
u32 val = cpu_to_be32(event_id);
|
||||
unsigned int reg;
|
||||
int i, ret;
|
||||
|
||||
ret = wm_coeff_base_reg(ctl, ®);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
adsp_dbg(dsp, "Sending 0x%x to acked control alg 0x%x %s:0x%x\n",
|
||||
event_id, ctl->alg_region.alg,
|
||||
wm_adsp_mem_region_name(ctl->alg_region.type), ctl->offset);
|
||||
|
||||
ret = regmap_raw_write(dsp->regmap, reg, &val, sizeof(val));
|
||||
if (ret) {
|
||||
adsp_err(dsp, "Failed to write %x: %d\n", reg, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Poll for ack, we initially poll at ~1ms intervals for firmwares
|
||||
* that respond quickly, then go to ~10ms polls. A firmware is unlikely
|
||||
* to ack instantly so we do the first 1ms delay before reading the
|
||||
* control to avoid a pointless bus transaction
|
||||
*/
|
||||
for (i = 0; i < WM_ADSP_ACKED_CTL_TIMEOUT_MS;) {
|
||||
switch (i) {
|
||||
case 0 ... WM_ADSP_ACKED_CTL_N_QUICKPOLLS - 1:
|
||||
usleep_range(1000, 2000);
|
||||
i++;
|
||||
break;
|
||||
default:
|
||||
usleep_range(10000, 20000);
|
||||
i += 10;
|
||||
break;
|
||||
}
|
||||
|
||||
ret = regmap_raw_read(dsp->regmap, reg, &val, sizeof(val));
|
||||
if (ret) {
|
||||
adsp_err(dsp, "Failed to read %x: %d\n", reg, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (val == 0) {
|
||||
adsp_dbg(dsp, "Acked control ACKED at poll %u\n", i);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
adsp_warn(dsp, "Acked control @0x%x alg:0x%x %s:0x%x timed out\n",
|
||||
reg, ctl->alg_region.alg,
|
||||
wm_adsp_mem_region_name(ctl->alg_region.type),
|
||||
ctl->offset);
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static int wm_coeff_write_control(struct wm_coeff_ctl *ctl,
|
||||
const void *buf, size_t len)
|
||||
{
|
||||
@ -1034,6 +1102,24 @@ static int wm_coeff_sync_controls(struct wm_adsp *dsp)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void wm_adsp_signal_event_controls(struct wm_adsp *dsp,
|
||||
unsigned int event)
|
||||
{
|
||||
struct wm_coeff_ctl *ctl;
|
||||
int ret;
|
||||
|
||||
list_for_each_entry(ctl, &dsp->ctl_list, list) {
|
||||
if (ctl->type != WMFW_CTL_TYPE_HOSTEVENT)
|
||||
continue;
|
||||
|
||||
ret = wm_coeff_write_acked_control(ctl, event);
|
||||
if (ret)
|
||||
adsp_warn(dsp,
|
||||
"Failed to send 0x%x event to alg 0x%x (%d)\n",
|
||||
event, ctl->alg_region.alg, ret);
|
||||
}
|
||||
}
|
||||
|
||||
static void wm_adsp_ctl_work(struct work_struct *work)
|
||||
{
|
||||
struct wmfw_ctl_work *ctl_work = container_of(work,
|
||||
@ -1307,6 +1393,21 @@ static inline void wm_coeff_parse_coeff(struct wm_adsp *dsp, const u8 **data,
|
||||
adsp_dbg(dsp, "\tALSA control len: %#x\n", blk->len);
|
||||
}
|
||||
|
||||
static int wm_adsp_check_coeff_flags(struct wm_adsp *dsp,
|
||||
const struct wm_coeff_parsed_coeff *coeff_blk,
|
||||
unsigned int f_required,
|
||||
unsigned int f_illegal)
|
||||
{
|
||||
if ((coeff_blk->flags & f_illegal) ||
|
||||
((coeff_blk->flags & f_required) != f_required)) {
|
||||
adsp_err(dsp, "Illegal flags 0x%x for control type 0x%x\n",
|
||||
coeff_blk->flags, coeff_blk->ctl_type);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wm_adsp_parse_coeff(struct wm_adsp *dsp,
|
||||
const struct wmfw_region *region)
|
||||
{
|
||||
@ -1323,6 +1424,16 @@ static int wm_adsp_parse_coeff(struct wm_adsp *dsp,
|
||||
switch (coeff_blk.ctl_type) {
|
||||
case SNDRV_CTL_ELEM_TYPE_BYTES:
|
||||
break;
|
||||
case WMFW_CTL_TYPE_HOSTEVENT:
|
||||
ret = wm_adsp_check_coeff_flags(dsp, &coeff_blk,
|
||||
WMFW_CTL_FLAG_SYS |
|
||||
WMFW_CTL_FLAG_VOLATILE |
|
||||
WMFW_CTL_FLAG_WRITEABLE |
|
||||
WMFW_CTL_FLAG_READABLE,
|
||||
0);
|
||||
if (ret)
|
||||
return -EINVAL;
|
||||
break;
|
||||
default:
|
||||
adsp_err(dsp, "Unknown control type: %d\n",
|
||||
coeff_blk.ctl_type);
|
||||
@ -2400,6 +2511,9 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
|
||||
break;
|
||||
|
||||
case SND_SOC_DAPM_PRE_PMD:
|
||||
/* Tell the firmware to cleanup */
|
||||
wm_adsp_signal_event_controls(dsp, WM_ADSP_FW_EVENT_SHUTDOWN);
|
||||
|
||||
/* Log firmware state, it can be useful for analysis */
|
||||
wm_adsp2_show_fw_status(dsp);
|
||||
|
||||
|
@ -26,6 +26,9 @@
|
||||
#define WMFW_CTL_FLAG_WRITEABLE 0x0002
|
||||
#define WMFW_CTL_FLAG_READABLE 0x0001
|
||||
|
||||
/* Non-ALSA coefficient types start at 0x1000 */
|
||||
#define WMFW_CTL_TYPE_HOSTEVENT 0x1001 /* event control */
|
||||
|
||||
struct wmfw_header {
|
||||
char magic[4];
|
||||
__le32 len;
|
||||
|
Loading…
Reference in New Issue
Block a user