diff --git a/src/drivers/intel/baytrail/ipc.c b/src/drivers/intel/baytrail/ipc.c index 69f1567e8327..a834209357d0 100644 --- a/src/drivers/intel/baytrail/ipc.c +++ b/src/drivers/intel/baytrail/ipc.c @@ -162,7 +162,7 @@ void ipc_platform_do_cmd(struct ipc *ipc) // TODO: signal audio work to enter D3 in normal context /* are we about to enter D3 ? */ - if (iipc->pm_prepare_D3) { + if (iipc->pm_target_state == SOF_PM_STATE_D3) { while (1) { wait_for_interrupt(0); } @@ -235,7 +235,7 @@ int platform_ipc_init(struct ipc *ipc) iipc->dmac = dma_get(dir, caps, dev, DMA_ACCESS_SHARED); /* PM */ - iipc->pm_prepare_D3 = 0; + iipc->pm_target_state = SOF_PM_STATE_D0; /* initial value */ /* configure interrupt */ interrupt_register(PLATFORM_IPC_INTERRUPT, IRQ_AUTO_UNMASK, diff --git a/src/drivers/intel/cavs/ipc.c b/src/drivers/intel/cavs/ipc.c index 5ca723f54a98..e43ba1c315d3 100644 --- a/src/drivers/intel/cavs/ipc.c +++ b/src/drivers/intel/cavs/ipc.c @@ -60,27 +60,33 @@ extern struct ipc *_ipc; static void ipc_irq_handler(void *arg) { uint32_t dipcctl; + struct ipc_data *iipc = ipc_get_drvdata(_ipc); #if CAVS_VERSION == CAVS_VERSION_1_5 uint32_t dipct; uint32_t dipcie; + uint32_t dipcte; dipct = ipc_read(IPC_DIPCT); dipcie = ipc_read(IPC_DIPCIE); dipcctl = ipc_read(IPC_DIPCCTL); + dipcte = ipc_read(IPC_DIPCCTE); - tracev_ipc("ipc: irq dipct 0x%x dipcie 0x%x dipcctl 0x%x", dipct, - dipcie, dipcctl); + tracev_ipc("ipc: irq dipct 0x%x dipcie 0x%x dipcctl 0x%x dipcte 0x%x", + dipct, dipcie, dipcctl, dipcte); #else uint32_t dipctdr; uint32_t dipcida; + uint32_t dipctdd; dipctdr = ipc_read(IPC_DIPCTDR); dipcida = ipc_read(IPC_DIPCIDA); dipcctl = ipc_read(IPC_DIPCCTL); + dipctdd = ipc_read(IPC_DIPCTDD); - tracev_ipc("ipc: irq dipctdr 0x%x dipcida 0x%x dipcctl 0x%x", dipctdr, - dipcida, dipcctl); + tracev_ipc("ipc: irq dipctdr 0x%x dipcida 0x%x dipcctl 0x%x", + dipctdr, dipcida, dipcctl); + tracev_ipc("ipc: irq dipctdd 0x%x", dipctdd); #endif /* new message from host */ @@ -106,6 +112,22 @@ static void ipc_irq_handler(void *arg) ipc_read(IPC_DIPCCTL)); #endif } else { +#if CAVS_VERSION == CAVS_VERSION_1_5 + if (dipct & IPC_DIPCT_MSG_MASK) { +#else + if (dipctdr & IPC_DIPCTDR_MSG_MASK) { +#endif + iipc->ipc_payload = SOF_IPC_PAYLOAD_REGISTER; +#if CAVS_VERSION == CAVS_VERSION_1_5 + iipc->reg1 = dipct; + iipc->reg2 = dipcte; +#else + iipc->reg1 = dipctdr; + iipc->reg2 = dipctdd; +#endif + } else { + iipc->ipc_payload = SOF_IPC_PAYLOAD_MEMORY; + } _ipc->host_pending = 1; ipc_schedule_process(_ipc); } @@ -145,7 +167,6 @@ void ipc_platform_do_cmd(struct ipc *ipc) /* perform command and return any error */ err = ipc_cmd(); - /* if err > 0, reply created and copied by cmd() */ if (err <= 0) { /* send std error/ok reply */ @@ -160,7 +181,7 @@ void ipc_platform_do_cmd(struct ipc *ipc) /* are we about to enter D3 ? */ #if CAVS_VERSION < CAVS_VERSION_2_0 - if (iipc->pm_prepare_D3) { + if (iipc->pm_target_state == SOF_PM_STATE_D3) { /* no return - memory will be powered off and IPC sent */ platform_pm_runtime_power_off(); } @@ -178,7 +199,7 @@ void ipc_platform_do_cmd(struct ipc *ipc) ipc_write(IPC_DIPCCTL, ipc_read(IPC_DIPCCTL) | IPC_DIPCCTL_IPCTBIE); #if CAVS_VERSION == CAVS_VERSION_2_0 - if (iipc->pm_prepare_D3) { + if (iipc->pm_target_state == SOF_PM_STATE_D3) { //TODO: add support for Icelake while (1) wait_for_interrupt(5); @@ -246,7 +267,7 @@ int platform_ipc_init(struct ipc *ipc) ipc_process_task, _ipc, 0, 0); /* PM */ - iipc->pm_prepare_D3 = 0; + iipc->pm_target_state = SOF_PM_STATE_D0; /* initial value */ /* configure interrupt */ interrupt_register(PLATFORM_IPC_INTERRUPT, IRQ_AUTO_UNMASK, diff --git a/src/drivers/intel/cavs/sue-ipc.c b/src/drivers/intel/cavs/sue-ipc.c index 68d2a649656e..b9163e3bf0c9 100644 --- a/src/drivers/intel/cavs/sue-ipc.c +++ b/src/drivers/intel/cavs/sue-ipc.c @@ -86,7 +86,7 @@ void ipc_platform_do_cmd(struct ipc *ipc) // TODO: signal audio work to enter D3 in normal context /* are we about to enter D3 ? */ - if (iipc->pm_prepare_D3) { + if (iipc->pm_target_state == SOF_PM_STATE_D3) { while (1) wait_for_interrupt(0); } @@ -137,7 +137,7 @@ int platform_ipc_init(struct ipc *ipc) ipc_process_task, _ipc, 0, 0); /* PM */ - iipc->pm_prepare_D3 = 0; + iipc->pm_target_state = SOF_PM_STATE_D0; /* initial value */ return 0; } diff --git a/src/drivers/intel/haswell/ipc.c b/src/drivers/intel/haswell/ipc.c index 6b63c4182a25..7a16ca98ddda 100644 --- a/src/drivers/intel/haswell/ipc.c +++ b/src/drivers/intel/haswell/ipc.c @@ -153,7 +153,7 @@ void ipc_platform_do_cmd(struct ipc *ipc) // TODO: signal audio work to enter D3 in normal context /* are we about to enter D3 ? */ - if (iipc->pm_prepare_D3) { + if (iipc->pm_target_state == SOF_PM_STATE_D3) { while (1) { wait_for_interrupt(0); } @@ -225,7 +225,7 @@ int platform_ipc_init(struct ipc *ipc) iipc->dmac = dma_get(dir, caps, dev, DMA_ACCESS_SHARED); /* PM */ - iipc->pm_prepare_D3 = 0; + iipc->pm_target_state = SOF_PM_STATE_D0; /* initial value */ /* configure interrupt */ interrupt_register(PLATFORM_IPC_INTERRUPT, IRQ_AUTO_UNMASK, diff --git a/src/host/ipc.c b/src/host/ipc.c index 8453598bb2fd..0845c2bf1a32 100644 --- a/src/host/ipc.c +++ b/src/host/ipc.c @@ -52,7 +52,7 @@ int platform_ipc_init(struct ipc *ipc) bzero(iipc->page_table, HOST_PAGE_SIZE); /* PM */ - iipc->pm_prepare_D3 = 0; + iipc->pm_target_state = SOF_PM_STATE_D0; /* initial value */ return 0; } diff --git a/src/include/sof/ipc.h b/src/include/sof/ipc.h index a24282b47ed1..e50c54eac038 100644 --- a/src/include/sof/ipc.h +++ b/src/include/sof/ipc.h @@ -42,6 +42,7 @@ #include #include #include +#include struct sof; struct dai_config; @@ -204,6 +205,11 @@ int ipc_dma_trace_send_position(void); /* get posn offset by pipeline. */ int ipc_get_posn_offset(struct ipc *ipc, struct pipeline *pipe); +enum ipc_payload_location { + SOF_IPC_PAYLOAD_MEMORY = 0, + SOF_IPC_PAYLOAD_REGISTER +}; + /* private data for IPC */ struct ipc_data { /* DMA */ @@ -211,7 +217,12 @@ struct ipc_data { uint8_t *page_table; /* PM */ - int pm_prepare_D3; /* do we need to prepare for D3 */ + uint32_t pm_target_state; /* SOF_PM_STATE_D.. */ + + /* register based communication */ + uint32_t ipc_payload; /* SOF_IPC_PAYLOAD_... */ + uint32_t reg1; + uint32_t reg2; }; int ipc_cmd(void); diff --git a/src/include/uapi/abi.h b/src/include/uapi/abi.h index 2bf11c03a6a4..5a08f4039497 100644 --- a/src/include/uapi/abi.h +++ b/src/include/uapi/abi.h @@ -52,7 +52,7 @@ /** \brief SOF ABI version major, minor and patch numbers */ #define SOF_ABI_MAJOR 3 -#define SOF_ABI_MINOR 6 +#define SOF_ABI_MINOR 7 #define SOF_ABI_PATCH 0 /** \brief SOF ABI version number. Format within 32bit word is MMmmmppp */ diff --git a/src/include/uapi/ipc/header.h b/src/include/uapi/ipc/header.h index 915448c601d4..0b1a8a7f5588 100644 --- a/src/include/uapi/ipc/header.h +++ b/src/include/uapi/ipc/header.h @@ -124,6 +124,8 @@ #define SOF_IPC_PM_CLK_GET SOF_CMD_TYPE(0x005) #define SOF_IPC_PM_CLK_REQ SOF_CMD_TYPE(0x006) #define SOF_IPC_PM_CORE_ENABLE SOF_CMD_TYPE(0x007) +#define SOF_IPC_PM_STATE_SET SOF_CMD_TYPE(0x008) +#define SOF_IPC_PM_STATE_GET SOF_CMD_TYPE(0x009) /** \name DSP Command: Component runtime config - multiple different types * @{ diff --git a/src/include/uapi/ipc/pm.h b/src/include/uapi/ipc/pm.h index 5bb7fb4144f0..fb0576811277 100644 --- a/src/include/uapi/ipc/pm.h +++ b/src/include/uapi/ipc/pm.h @@ -75,4 +75,49 @@ struct sof_ipc_pm_core_config { uint32_t enable_mask; } __attribute__((packed)); +/* DSP subsystem power states */ +enum sof_pm_state { + SOF_PM_STATE_D0 = 0, /* D0 only, also initial state */ + SOF_PM_STATE_D3, /* D3 DSP subsystem is power gated*/ + /* internal FW transitions across D0ix substates incl. D0 are allowed */ + SOF_PM_STATE_D0ix, +}; + +/* Flags to allow FW for applying internally clock gating policies */ +enum sof_pm_cg { + SOF_PM_CG_ON = 0, /* Clock gating enabled, */ + SOF_PM_CG_OFF, /* Clock gating disabled */ +}; + +/* Flags to allow FW for applying power gating policies */ +enum sof_pm_pg { + SOF_PM_PG_ON = 0, /* Power gating enabled, */ + SOF_PM_PG_OFF, /* Power gating disabled */ +}; + +/* PM state - SOF_IPC_PM_STATE_SET */ +struct sof_ipc_pm_state { + struct sof_ipc_cmd_hdr hdr; + + uint32_t pm_state; /**< power state SOF_PM_STATE_D.. */ + /**< flag to disable FW logic for autonomous D0ix<->D0 transitions */ + uint32_t prevent_power_gating; + /**< flag to disable FW logic for autonomous clock gating */ + uint32_t prevent_clock_gating; + /**< reserved for future use */ + uint32_t reserved[8]; +} __attribute__((packed)); + +/* PM params info reply - SOF_IPC_PM_STATE_GET */ +struct sof_ipc_pm_state_reply { + struct sof_ipc_reply rhdr; + uint32_t pm_state; /**< power state SOF_PM_STATE_D.. */ + /**< flag to disable FW logic for autonomous D0ix<->D0 transitions */ + uint32_t prevent_power_gating; + /**< flag to disable FW logic for autonomous clock gating */ + uint32_t prevent_clock_gating; + /**< reserved for future use */ + uint32_t reserved[8]; +} __attribute__((packed)); + #endif diff --git a/src/ipc/handler.c b/src/ipc/handler.c index 6dbb1253b79a..61f51c2884e6 100644 --- a/src/ipc/handler.c +++ b/src/ipc/handler.c @@ -127,6 +127,60 @@ /* IPC context - shared with platform IPC driver */ struct ipc *_ipc; +/* + * function to build a memory structure based on limited amount of data passed + * using IPC registers: + * Assumptions for data format in the IPC registers + * reg0: + * 0xRFFFGCCC + * - R is reserved (1 bit) + * - F is for flags (like a fragmented message etc.) + * - G is global cmd type (4 bits) - as defined in header.h + * - C is command type (12 bits) - as defined in header.h + * + * reg1: + * 0xRDDDDDDD + * - R is reserved (2 bit) for compatibility with cAVS 1.5 HW + * - D is for message data (30 bits) + * + * Specifically for PM_STATE_SET command data in reg1 is defined as + * 0xRFFFFSPC + * - R is reserved (2 bit) for compatibility with cAVS 1.5 HW + * - F is reserved for future use (18 bits) + * - S is power state SOF_PM_STATE_D.. (4 bits) + * - P is power gating SOF_PM_PG_.... (4 bits) + * - C is clock gating SOF_PM_CG_... (4 bits) + */ +static inline struct sof_ipc_cmd_hdr *mailbox_validate_register(void) +{ + struct sof_ipc_cmd_hdr *hdr = _ipc->comp_data; + struct ipc_data *iipc = ipc_get_drvdata(_ipc); + + /* TODO: This is a snippet to illustrate the idea of building + * a message from data stored in registers + */ + + /* #1 retrieve global/command data from reg0 */ + + /* Assumption is that the ID number (16 bits) + * monotonic and overflows is set to 0 + */ + hdr->cmd = (iipc->reg1 << 16); + + /* #2 validated the command value */ + + /* Assumption is that at the first stage the only message supported over + * registers will be PM_STATE_SET D0 from D0ix + */ + + /* #3 create structure sof_ipc_pm_state */ + + /* #4 fill the structure fields using values from reg0 & reg1 */ + + /* #5 copy the structure to address pointed by hdr */ + return hdr; +} + static inline struct sof_ipc_cmd_hdr *mailbox_validate(void) { struct sof_ipc_cmd_hdr *hdr = _ipc->comp_data; @@ -647,7 +701,7 @@ static int ipc_pm_context_save(uint32_t header) /* write the context to the host driver */ //mailbox_hostbox_write(0, pm_ctx, sizeof(*pm_ctx)); - iipc->pm_prepare_D3 = 1; + iipc->pm_target_state = SOF_PM_STATE_D3; return 0; } @@ -687,6 +741,47 @@ static int ipc_pm_core_enable(uint32_t header) return 0; } +/* set power management state */ +static int ipc_pm_state_set(uint32_t header) +{ + struct sof_ipc_pm_state pm_state; + struct ipc_data *iipc = ipc_get_drvdata(_ipc); + + /* copy message with ABI safe method */ + IPC_COPY_CMD(pm_state, _ipc->comp_data); + + trace_ipc("ipc: pm state: %d, prevent: cg %d pg %d -> set", + pm_state.pm_state, pm_state.prevent_clock_gating, + pm_state.prevent_power_gating); + + iipc->pm_target_state = pm_state.pm_state; + /* TODO: add handling for clock & power gating policies */ + + return 0; +} + +/* get power management state */ +static int ipc_pm_state_get(uint32_t header) +{ + struct sof_ipc_pm_state_reply reply; + struct ipc_data *iipc = ipc_get_drvdata(_ipc); + + /* write component values to the outbox */ + reply.rhdr.hdr.size = sizeof(reply); + reply.rhdr.hdr.cmd = header; + reply.rhdr.error = 0; + reply.pm_state = iipc->pm_target_state; + reply.prevent_clock_gating = 0; /* TODO: add data handling*/ + reply.prevent_power_gating = 0; /* TODO: add data handling */ + + trace_ipc("ipc: pm state: %d, prevent: cg %d pg %d -> get", + reply.pm_state, reply.prevent_clock_gating, + reply.prevent_power_gating); + + mailbox_hostbox_write(0, &reply, sizeof(reply)); + return 1; +} + static int ipc_glb_pm_message(uint32_t header) { uint32_t cmd = iCS(header); @@ -700,6 +795,10 @@ static int ipc_glb_pm_message(uint32_t header) return ipc_pm_context_size(header); case SOF_IPC_PM_CORE_ENABLE: return ipc_pm_core_enable(header); + case SOF_IPC_PM_STATE_SET: + return ipc_pm_state_set(header); + case SOF_IPC_PM_STATE_GET: + return ipc_pm_state_get(header); case SOF_IPC_PM_CLK_SET: case SOF_IPC_PM_CLK_GET: case SOF_IPC_PM_CLK_REQ: @@ -1102,8 +1201,13 @@ int ipc_cmd(void) { struct sof_ipc_cmd_hdr *hdr; uint32_t type; + struct ipc_data *iipc = ipc_get_drvdata(_ipc); + + if (iipc->ipc_payload == SOF_IPC_PAYLOAD_REGISTER) + hdr = mailbox_validate_register(); + else + hdr = mailbox_validate(); - hdr = mailbox_validate(); if (hdr == NULL) { trace_ipc_error("ipc: invalid IPC header."); return -EINVAL;