Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0ccf3cc6 authored by Vikash Garodia's avatar Vikash Garodia
Browse files

msm: vidc: unprepare clocks during power collapse



If any clock is still prepared (only disabled), apps will
not vote for XO shutdown. Unprepared clocks will remove the
vote and facilitate XO shutdown.
During power collapse, video drivers now disable and
unprepare venus clocks.
There was minimal power savings during internal tests,
but customer reported to have impact of 13mA without
the change.
The change add different clock states. Clock state
transitions with clock api is shown below

====================  clk_prepare_enable()    ==================
|DISABLED_UNPREPARED| ----------------------  |ENABLED_PREPARED|
====================  clk_disable_unprepare() ==================
   ||                                                   ||
   || clk_unprepare() ================    clk_disable() ||
    -----------------|DISABLED_PREPARED| ---------------
      clk_prepare()   ================    clk_enable()

Change-Id: Id23302e359abcdf91c993835c6353fb251c96fe6
CRs-Fixed: 604629
Signed-off-by: default avatarVikash Garodia <vgarodia@codeaurora.org>
parent de5acd14
Loading
Loading
Loading
Loading
+56 −39
Original line number Diff line number Diff line
@@ -90,6 +90,12 @@ static int venus_hfi_power_enable(void *dev);

static inline int venus_hfi_clk_gating_off(struct venus_hfi_device *device);

static inline int venus_hfi_prepare_enable_clks(
	struct venus_hfi_device *device);

static inline void venus_hfi_disable_unprepare_clks(
	struct venus_hfi_device *device);

static void venus_hfi_dump_packet(u8 *packet)
{
	u32 c = 0, packet_size = *(u32 *)packet;
@@ -531,7 +537,7 @@ static void venus_hfi_write_register(
		dprintk(VIDC_ERR, "Invalid params: %p\n", device);
		return;
	}
	if (!device->clocks_enabled) {
	if (device->clk_state != ENABLED_PREPARED) {
		dprintk(VIDC_WARN,
			"HFI Write register failed : Clocks are OFF\n");
		return;
@@ -553,7 +559,7 @@ static int venus_hfi_read_register(struct venus_hfi_device *device, u32 reg)
		dprintk(VIDC_ERR, "Invalid params: %p\n", device);
		return -EINVAL;
	}
	if (!device->clocks_enabled) {
	if (device->clk_state != ENABLED_PREPARED) {
		dprintk(VIDC_WARN,
			"HFI Read register failed : Clocks are OFF\n");
		return -EINVAL;
@@ -1145,7 +1151,7 @@ static inline int venus_hfi_clk_enable(struct venus_hfi_device *device)
	}
	WARN(!mutex_is_locked(&device->clk_pwr_lock),
				"Clock/power lock must be acquired");
	if (device->clocks_enabled) {
	if (device->clk_state == ENABLED_PREPARED) {
		dprintk(VIDC_DBG, "Clocks already enabled\n");
		return 0;
	}
@@ -1164,8 +1170,7 @@ static inline int venus_hfi_clk_enable(struct venus_hfi_device *device)

		++i;
	}

	device->clocks_enabled = 1;
	device->clk_state = ENABLED_PREPARED;
	return 0;
fail_clk_enable:
	venus_hfi_for_each_clock(device, cl) {
@@ -1179,6 +1184,7 @@ fail_clk_enable:

		--i;
	}
	device->clk_state = DISABLED_PREPARED;
	return rc;
}

@@ -1192,7 +1198,7 @@ static inline void venus_hfi_clk_disable(struct venus_hfi_device *device)
	}
	WARN(!mutex_is_locked(&device->clk_pwr_lock),
			"Clock/power lock must be acquired");
	if (!device->clocks_enabled) {
	if (device->clk_state != ENABLED_PREPARED) {
		dprintk(VIDC_DBG, "Clocks already disabled\n");
		return;
	}
@@ -1218,7 +1224,7 @@ static inline void venus_hfi_clk_disable(struct venus_hfi_device *device)
		}
	}

	device->clocks_enabled = 0;
	device->clk_state = DISABLED_PREPARED;
}

static DECLARE_COMPLETION(pc_prep_done);
@@ -1232,9 +1238,11 @@ static int venus_hfi_halt_axi(struct venus_hfi_device *device)
		dprintk(VIDC_ERR, "Invalid input: %p\n", device);
		return -EINVAL;
	}
	mutex_lock(&device->clk_pwr_lock);
	if (venus_hfi_clk_gating_off(device)) {
		dprintk(VIDC_ERR, "Failed to turn off clk gating\n");
		return -EIO;
		rc = -EIO;
		goto err_clk_gating_off;
	}
	/* Halt AXI and AXI OCMEM VBIF Access */
	reg = venus_hfi_read_register(device, VENUS_VBIF_AXI_HALT_CTRL0);
@@ -1249,6 +1257,8 @@ static int venus_hfi_halt_axi(struct venus_hfi_device *device)
			VENUS_VBIF_AXI_HALT_ACK_TIMEOUT_US);
	if (rc)
		dprintk(VIDC_WARN, "AXI bus port halt timeout\n");
err_clk_gating_off:
	mutex_unlock(&device->clk_pwr_lock);
	return rc;
}

@@ -1276,7 +1286,7 @@ static inline int venus_hfi_power_off(struct venus_hfi_device *device)
		venus_hfi_clk_disable(device);
		return rc;
	}
	venus_hfi_clk_disable(device);
	venus_hfi_disable_unprepare_clks(device);
	venus_hfi_iommu_detach(device);
	rc = regulator_disable(venus_hfi_get_regulator(device, "venus"));
	if (rc) {
@@ -1286,7 +1296,7 @@ static inline int venus_hfi_power_off(struct venus_hfi_device *device)

	venus_hfi_unvote_buses(device);

	device->power_enabled = 0;
	device->power_enabled = false;
	dprintk(VIDC_INFO, "entering power collapse\n");
already_disabled:
	return rc;
@@ -1319,7 +1329,11 @@ static inline int venus_hfi_power_on(struct venus_hfi_device *device)
		goto err_iommu_attach;
	}

	if (device->clk_state == DISABLED_UNPREPARED)
		rc = venus_hfi_prepare_enable_clks(device);
	else if (device->clk_state == DISABLED_PREPARED)
		rc = venus_hfi_clk_enable(device);

	if (rc) {
		dprintk(VIDC_ERR, "Failed to enable clocks\n");
		goto err_enable_clk;
@@ -1370,7 +1384,7 @@ static inline int venus_hfi_power_on(struct venus_hfi_device *device)
		dprintk(VIDC_ERR, "Failed to allocate OCMEM");
		goto err_alloc_ocmem;
	}
	device->power_enabled = 1;
	device->power_enabled = true;
	dprintk(VIDC_INFO, "resuming from power collapse\n");
	return rc;
err_alloc_ocmem:
@@ -1414,7 +1428,7 @@ static inline int venus_hfi_clk_gating_off(struct venus_hfi_device *device)
		dprintk(VIDC_ERR, "Invalid params: %p\n", device);
		return -EINVAL;
	}
	if (device->clocks_enabled) {
	if (device->clk_state == ENABLED_PREPARED) {
		dprintk(VIDC_DBG, "Clocks are already enabled\n");
		goto already_enabled;
	}
@@ -1437,7 +1451,7 @@ static inline int venus_hfi_clk_gating_off(struct venus_hfi_device *device)
				VIDC_WRAPPER_INTR_MASK_A2HVCODEC_BMSK);
	}
already_enabled:
	device->clocks_enabled = 1;
	device->clk_state = ENABLED_PREPARED;
fail_clk_power_on:
	return rc;
}
@@ -2153,7 +2167,7 @@ static inline void venus_hfi_clk_gating_on(struct venus_hfi_device *device)
		dprintk(VIDC_ERR, "Invalid params: %p\n", device);
		return;
	}
	if (!device->clocks_enabled) {
	if (device->clk_state != ENABLED_PREPARED) {
		dprintk(VIDC_DBG, "Clocks are already disabled\n");
		goto already_disabled;
	}
@@ -2170,7 +2184,7 @@ static inline void venus_hfi_clk_gating_on(struct venus_hfi_device *device)
			msecs_to_jiffies(msm_vidc_pwr_collapse_delay)))
		dprintk(VIDC_DBG, "PM work already scheduled\n");
already_disabled:
	device->clocks_enabled = 0;
	device->clk_state = DISABLED_PREPARED;
}

static void venus_hfi_core_clear_interrupt(struct venus_hfi_device *device)
@@ -2890,10 +2904,10 @@ static void venus_hfi_pm_hndlr(struct work_struct *work)
	struct venus_hfi_device *device = list_first_entry(
			&hal_ctxt.dev_head, struct venus_hfi_device, list);
	mutex_lock(&device->clk_pwr_lock);
	if (device->clocks_enabled || !device->power_enabled) {
	if (device->clk_state == ENABLED_PREPARED || !device->power_enabled) {
		dprintk(VIDC_DBG,
				"Clocks status: %d, Power status: %d, ignore power off\n",
				device->clocks_enabled, device->power_enabled);
				device->clk_state, device->power_enabled);
		goto clks_enabled;
	}
	mutex_unlock(&device->clk_pwr_lock);
@@ -2914,7 +2928,7 @@ static void venus_hfi_pm_hndlr(struct work_struct *work)
	}

	mutex_lock(&device->clk_pwr_lock);
	if (device->clocks_enabled) {
	if (device->clk_state == ENABLED_PREPARED) {
		dprintk(VIDC_ERR,
				"Clocks are still enabled after PC_PREP_DONE, ignore power off\n");
		goto clks_enabled;
@@ -3216,17 +3230,18 @@ static inline void venus_hfi_deinit_clocks(struct venus_hfi_device *device)
		clk_put(cl->clk);
}

static inline void venus_hfi_disable_clks(struct venus_hfi_device *device)
static inline void venus_hfi_disable_unprepare_clks(
	struct venus_hfi_device *device)
{
	struct clock_info *cl;
	if (!device) {
		dprintk(VIDC_ERR, "Invalid params: %p\n", device);
		return;
	}

	mutex_lock(&device->clk_pwr_lock);
	WARN_ON(!mutex_is_locked(&device->clk_pwr_lock));
	venus_hfi_for_each_clock(device, cl) {
		if (!device->clocks_enabled && cl->has_sw_power_collapse) {
		if (device->clk_state == DISABLED_PREPARED &&
				cl->has_sw_power_collapse) {
			dprintk(VIDC_DBG,
				"Omitting clk_disable of %s in %s as it's already disabled\n",
				cl->name, __func__);
@@ -3236,12 +3251,10 @@ static inline void venus_hfi_disable_clks(struct venus_hfi_device *device)
			clk_disable_unprepare(cl->clk);
		}
	}

	device->clocks_enabled = 0;
	mutex_unlock(&device->clk_pwr_lock);
	device->clk_state = DISABLED_UNPREPARED;
}

static inline int venus_hfi_enable_clks(struct venus_hfi_device *device)
static inline int venus_hfi_prepare_enable_clks(struct venus_hfi_device *device)
{
	struct clock_info *cl = NULL, *cl_fail = NULL;
	int rc = 0;
@@ -3249,8 +3262,12 @@ static inline int venus_hfi_enable_clks(struct venus_hfi_device *device)
		dprintk(VIDC_ERR, "Invalid params: %p\n", device);
		return -EINVAL;
	}
	WARN_ON(!mutex_is_locked(&device->clk_pwr_lock));

	mutex_lock(&device->clk_pwr_lock);
	if (device->clk_state == ENABLED_PREPARED) {
		dprintk(VIDC_DBG, "Clocks already prepared and enabled\n");
		return 0;
	}
	venus_hfi_for_each_clock(device, cl) {
		rc = clk_prepare_enable(cl->clk);
		if (rc) {
@@ -3262,9 +3279,7 @@ static inline int venus_hfi_enable_clks(struct venus_hfi_device *device)
		dprintk(VIDC_DBG, "Clock: %s enabled\n", cl->name);
	}

	device->clocks_enabled = 1;
	mutex_unlock(&device->clk_pwr_lock);

	device->clk_state = ENABLED_PREPARED;
	return rc;

fail_clk_enable:
@@ -3274,6 +3289,7 @@ fail_clk_enable:
		usleep(100);
		clk_disable_unprepare(cl->clk);
	}
	device->clk_state = DISABLED_UNPREPARED;
	mutex_unlock(&device->clk_pwr_lock);

	return rc;
@@ -3776,17 +3792,16 @@ static int venus_hfi_load_fw(void *dev)
			goto fail_load_fw;
		}
	}
	device->power_enabled = 1;
	mutex_unlock(&device->clk_pwr_lock);
	device->power_enabled = true;

	/* Clocks can be enabled only after pil_get since
	 * gdsc is turned-on in pil_get*/
	rc = venus_hfi_enable_clks(device);
	rc = venus_hfi_prepare_enable_clks(device);
	mutex_unlock(&device->clk_pwr_lock);
	if (rc) {
		dprintk(VIDC_ERR, "Failed to enable clocks: %d\n", rc);
		goto fail_enable_clks;
	}

	/* Hand off control of regulators to h/w _after_ enabling clocks */
	venus_hfi_enable_hw_power_collapse(device);

@@ -3800,7 +3815,9 @@ static int venus_hfi_load_fw(void *dev)

	return rc;
fail_protect_mem:
	venus_hfi_disable_clks(device);
	mutex_lock(&device->clk_pwr_lock);
	venus_hfi_disable_unprepare_clks(device);
	mutex_unlock(&device->clk_pwr_lock);
fail_enable_clks:
	if (device->resources.fw.cookie)
		subsystem_put(device->resources.fw.cookie);
@@ -3808,7 +3825,7 @@ fail_load_fw:
	mutex_lock(&device->clk_pwr_lock);
	device->resources.fw.cookie = NULL;
	venus_hfi_disable_regulators(device);
	device->power_enabled = 0;
	device->power_enabled = false;
	mutex_unlock(&device->clk_pwr_lock);
fail_enable_gdsc:
	venus_hfi_iommu_detach(device);
@@ -3836,10 +3853,10 @@ static void venus_hfi_unload_fw(void *dev)
		 */
		if (venus_hfi_halt_axi(device))
			dprintk(VIDC_WARN, "Failed to halt AXI\n");
		venus_hfi_disable_clks(device);
		mutex_lock(&device->clk_pwr_lock);
		venus_hfi_disable_unprepare_clks(device);
		venus_hfi_disable_regulators(device);
		device->power_enabled = 0;
		device->power_enabled = false;
		mutex_unlock(&device->clk_pwr_lock);
		device->resources.fw.cookie = NULL;
	}
+8 −2
Original line number Diff line number Diff line
@@ -122,6 +122,12 @@ enum bus_index {
	BUS_IDX_MAX
};

enum clock_state {
	DISABLED_UNPREPARED,
	ENABLED_PREPARED,
	DISABLED_PREPARED
};

struct vidc_mem_addr {
	ion_phys_addr_t align_device_addr;
	u8 *align_virtual_addr;
@@ -171,8 +177,8 @@ struct venus_hfi_device {
	u32 device_id;
	u32 clk_load;
	struct vidc_bus_vote_data *bus_load;
	u32 clocks_enabled;
	u32 power_enabled;
	enum clock_state clk_state;
	bool power_enabled;
	struct mutex read_lock;
	struct mutex write_lock;
	struct mutex clk_pwr_lock;