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

Commit 5d9177a7 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "msm: vidc: Add thermal mitigation feature"

parents ebeec9a5 9e4d2d25
Loading
Loading
Loading
Loading
+47 −3
Original line number Diff line number Diff line
@@ -388,6 +388,49 @@ static ssize_t show_pwr_collapse_delay(struct device *dev,
static DEVICE_ATTR(pwr_collapse_delay, 0644, show_pwr_collapse_delay,
		store_pwr_collapse_delay);

static ssize_t show_thermal_level(struct device *dev,
		struct device_attribute *attr,
		char *buf)
{
	return snprintf(buf, PAGE_SIZE, "%d\n", vidc_driver->thermal_level);
}

static ssize_t store_thermal_level(struct device *dev,
		struct device_attribute *attr,
		const char *buf, size_t count)
{
	int rc = 0, val = 0;

	rc = kstrtoint(buf, 0, &val);
	if (rc || val < 0) {
		dprintk(VIDC_WARN,
			"Invalid thermal level value: %s\n", buf);
		return -EINVAL;
	}
	dprintk(VIDC_DBG, "Thermal level old %d new %d\n",
			vidc_driver->thermal_level, val);

	if (val == vidc_driver->thermal_level)
		return count;
	vidc_driver->thermal_level = val;

	msm_comm_handle_thermal_event();
	return count;
}

static DEVICE_ATTR(thermal_level, S_IRUGO | S_IWUSR, show_thermal_level,
		store_thermal_level);

static struct attribute *msm_vidc_core_attrs[] = {
		&dev_attr_pwr_collapse_delay.attr,
		&dev_attr_thermal_level.attr,
		NULL
};

static struct attribute_group msm_vidc_core_attr_group = {
		.attrs = msm_vidc_core_attrs,
};

static int msm_vidc_probe(struct platform_device *pdev)
{
	int rc = 0;
@@ -407,10 +450,10 @@ static int msm_vidc_probe(struct platform_device *pdev)
		dprintk(VIDC_ERR, "Failed to init core\n");
		goto err_core_init;
	}
	rc = device_create_file(&pdev->dev, &dev_attr_pwr_collapse_delay);
	rc = sysfs_create_group(&pdev->dev.kobj, &msm_vidc_core_attr_group);
	if (rc) {
		dprintk(VIDC_ERR,
				"Failed to create pwr_collapse_delay sysfs node");
				"Failed to create attributes\n");
		goto err_core_init;
	}
	if (core->hfi_type == VIDC_HFI_Q6) {
@@ -513,7 +556,7 @@ err_dec_attr_link_name:
err_dec_register:
	v4l2_device_unregister(&core->v4l2_dev);
err_v4l2_register:
	device_remove_file(&pdev->dev, &dev_attr_pwr_collapse_delay);
	sysfs_remove_group(&pdev->dev.kobj, &msm_vidc_core_attr_group);
err_core_init:
	kfree(core);
err_no_mem:
@@ -546,6 +589,7 @@ static int msm_vidc_remove(struct platform_device *pdev)
	v4l2_device_unregister(&core->v4l2_dev);

	msm_vidc_free_platform_resources(&core->resources);
	sysfs_remove_group(&pdev->dev.kobj, &msm_vidc_core_attr_group);
	kfree(core);
	return rc;
}
+215 −51
Original line number Diff line number Diff line
@@ -1080,6 +1080,43 @@ static void handle_session_error(enum command_response cmd, void *data)
	}
}

static void msm_comm_clean_notify_client(struct msm_vidc_core *core)
{
	int rc = 0;
	struct msm_vidc_inst *inst = NULL;
	struct hfi_device *hdev = NULL;
	if (!core || !core->device) {
		dprintk(VIDC_ERR, "%s: Invalid params\n", __func__);
		return;
	}

	dprintk(VIDC_WARN, "%s: Core %p\n", __func__, core);
	mutex_lock(&core->lock);
	core->state = VIDC_CORE_INVALID;

	list_for_each_entry(inst, &core->instances, list) {
		mutex_lock(&inst->lock);
		inst->state = MSM_VIDC_CORE_INVALID;
		hdev = inst->core->device;
		if (hdev && inst->session) {
			dprintk(VIDC_DBG,
				"cleaning up inst: 0x%p\n", inst);
			rc = call_hfi_op(hdev, session_clean,
					(void *) inst->session);
			if (rc)
				dprintk(VIDC_ERR,
					"Sess clean failed :%p\n", inst);
		}
		inst->session = NULL;
		mutex_unlock(&inst->lock);
		dprintk(VIDC_WARN,
			"%s Send sys error for inst %p\n", __func__, inst);
		msm_vidc_queue_v4l2_event(inst,
				V4L2_EVENT_MSM_VIDC_SYS_ERROR);
	}
	mutex_unlock(&core->lock);
}

struct sys_err_handler_data {
	struct msm_vidc_core *core;
	struct delayed_work work;
@@ -1133,9 +1170,6 @@ static void handle_sys_error(enum command_response cmd, void *data)
	struct msm_vidc_cb_cmd_done *response = data;
	struct msm_vidc_core *core = NULL;
	struct sys_err_handler_data *handler = NULL;
	struct hfi_device *hdev = NULL;
	struct msm_vidc_inst *inst = NULL;
	int rc = 0;

	subsystem_crashed("venus");
	if (!response) {
@@ -1152,35 +1186,7 @@ static void handle_sys_error(enum command_response cmd, void *data)
	}

	dprintk(VIDC_WARN, "SYS_ERROR %d received for core %p\n", cmd, core);
	mutex_lock(&core->lock);
	core->state = VIDC_CORE_INVALID;

	/*
	* 1. Delete each instance session from hfi list
	* 2. Notify all clients about hardware error.
	*/
	list_for_each_entry(inst, &core->instances, list) {
		mutex_lock(&inst->lock);
		inst->state = MSM_VIDC_CORE_INVALID;
		if (inst->core)
			hdev = inst->core->device;
		if (hdev && inst->session) {
			dprintk(VIDC_DBG,
			"cleaning up inst: 0x%p\n", inst);
			rc = call_hfi_op(hdev, session_clean,
				(void *) inst->session);
			if (rc)
				dprintk(VIDC_ERR,
					"Sess clean failed :%p\n",
					inst);
		}
		inst->session = NULL;
		mutex_unlock(&inst->lock);
		msm_vidc_queue_v4l2_event(inst,
				V4L2_EVENT_MSM_VIDC_SYS_ERROR);
	}
	mutex_unlock(&core->lock);

	msm_comm_clean_notify_client(core);

	handler = kzalloc(sizeof(*handler), GFP_KERNEL);
	if (!handler) {
@@ -2143,6 +2149,167 @@ void msm_comm_scale_clocks_and_bus(struct msm_vidc_inst *inst)
	}
}

static inline enum msm_vidc_thermal_level msm_comm_vidc_thermal_level(int level)
{
	switch (level) {
	case 0:
		return VIDC_THERMAL_NORMAL;
	case 1:
		return VIDC_THERMAL_LOW;
	case 2:
		return VIDC_THERMAL_HIGH;
	default:
		return VIDC_THERMAL_CRITICAL;
	}
}

static unsigned long msm_comm_get_clock_rate(struct msm_vidc_core *core)
{
	struct hfi_device *hdev;
	unsigned long freq = 0;

	if (!core || !core->device) {
		dprintk(VIDC_ERR, "%s Invalid params\n", __func__);
		return -EINVAL;
	}
	hdev = core->device;

	freq = call_hfi_op(hdev, get_core_clock_rate, hdev->hfi_device_data);
	dprintk(VIDC_DBG, "clock freq %ld\n", freq);

	return freq;
}

static bool is_core_turbo(struct msm_vidc_core *core, unsigned long freq)
{
	int i = 0;
	struct msm_vidc_platform_resources *res = &core->resources;
	struct load_freq_table *table = res->load_freq_tbl;
	u32 max_freq = 0;

	for (i = 0; i < res->load_freq_tbl_size; i++) {
		if (max_freq < table[i].freq)
			max_freq = table[i].freq;
	}
	return freq >= max_freq;
}

static bool is_thermal_permissible(struct msm_vidc_core *core)
{
	enum msm_vidc_thermal_level tl;
	unsigned long freq = 0;
	bool is_turbo = false;

	tl = msm_comm_vidc_thermal_level(vidc_driver->thermal_level);
	freq = msm_comm_get_clock_rate(core);

	is_turbo = is_core_turbo(core, freq);
	dprintk(VIDC_DBG,
		"Core freq %ld Thermal level %d Turbo mode %d\n",
		freq, tl, is_turbo);

	if ((!is_turbo && tl >= VIDC_THERMAL_CRITICAL) ||
				(is_turbo && tl >= VIDC_THERMAL_LOW)) {
		dprintk(VIDC_ERR,
			"Video session not allowed. Turbo mode %d Thermal level %d\n",
			is_turbo, tl);
		return false;
	}
	return true;
}

static int msm_comm_session_abort(struct msm_vidc_inst *inst)
{
	int rc = 0, abort_completion = 0;
	struct hfi_device *hdev;

	if (!inst || !inst->core || !inst->core->device) {
		dprintk(VIDC_ERR, "%s invalid params\n", __func__);
		return -EINVAL;
	}
	hdev = inst->core->device;

	rc = call_hfi_op(hdev, session_abort, (void *)inst->session);
	if (rc) {
		dprintk(VIDC_ERR,
			"%s session_abort failed rc: %d\n", __func__, rc);
		return rc;
	}
	abort_completion = SESSION_MSG_INDEX(SESSION_ABORT_DONE);
	init_completion(&inst->completions[abort_completion]);
	rc = wait_for_completion_timeout(
			&inst->completions[abort_completion],
			msecs_to_jiffies(msm_vidc_hw_rsp_timeout));
	if (!rc) {
		dprintk(VIDC_ERR,
				"%s: Wait interrupted or timed out [%p]: %d\n",
				__func__, inst, abort_completion);
		rc = -EBUSY;
	} else {
		rc = 0;
	}

	return rc;
}

static void handle_thermal_event(struct msm_vidc_core *core)
{
	int rc = 0;
	struct msm_vidc_inst *inst;

	if (!core || !core->device) {
		dprintk(VIDC_ERR, "%s Invalid params\n", __func__);
		return;
	}
	mutex_lock(&core->lock);
	list_for_each_entry(inst, &core->instances, list) {
		if (!inst->session)
			continue;

		mutex_unlock(&core->lock);
		if (inst->state >= MSM_VIDC_OPEN_DONE &&
			inst->state < MSM_VIDC_CLOSE_DONE) {
			dprintk(VIDC_WARN, "%s: abort inst %p\n",
				__func__, inst);
			rc = msm_comm_session_abort(inst);
			if (rc) {
				dprintk(VIDC_ERR,
					"%s session_abort failed rc: %d\n",
					__func__, rc);
				goto err_sess_abort;
			}
			change_inst_state(inst, MSM_VIDC_CORE_INVALID);
			dprintk(VIDC_WARN,
				"%s Send sys error for inst %p\n",
				__func__, inst);
			msm_vidc_queue_v4l2_event(inst,
					V4L2_EVENT_MSM_VIDC_SYS_ERROR);
		} else {
			msm_comm_generate_session_error(inst);
		}
		mutex_lock(&core->lock);
	}
	mutex_unlock(&core->lock);
	return;

err_sess_abort:
	msm_comm_clean_notify_client(core);
	return;
}

void msm_comm_handle_thermal_event()
{
	struct msm_vidc_core *core;

	list_for_each_entry(core, &vidc_driver->cores, list) {
		if (!is_thermal_permissible(core)) {
			dprintk(VIDC_WARN,
				"Thermal level critical, stop all active sessions!\n");
			handle_thermal_event(core);
		}
	}
}

static int msm_comm_init_core_done(struct msm_vidc_inst *inst)
{
	struct msm_vidc_core *core = inst->core;
@@ -3031,6 +3198,7 @@ int msm_comm_try_state(struct msm_vidc_inst *inst, int state)
		if (rc || state <= get_flipped_state(inst->state, state))
			break;
	case MSM_VIDC_CORE_UNINIT:
	case MSM_VIDC_CORE_INVALID:
		dprintk(VIDC_DBG, "Sending core uninit\n");
		rc = msm_vidc_deinit_core(inst);
		if (rc || state == get_flipped_state(inst->state, state))
@@ -4377,6 +4545,7 @@ int msm_vidc_check_session_supported(struct msm_vidc_inst *inst)
	struct msm_vidc_core_capability *capability;
	int rc = 0;
	struct hfi_device *hdev;
	struct msm_vidc_core *core;

	if (!inst || !inst->core || !inst->core->device) {
		dprintk(VIDC_WARN, "%s: Invalid parameter\n", __func__);
@@ -4384,6 +4553,7 @@ int msm_vidc_check_session_supported(struct msm_vidc_inst *inst)
	}
	capability = &inst->capability;
	hdev = inst->core->device;
	core = inst->core;
	rc = msm_vidc_load_supported(inst);
	if (rc) {
		change_inst_state(inst, MSM_VIDC_CORE_INVALID);
@@ -4391,6 +4561,13 @@ int msm_vidc_check_session_supported(struct msm_vidc_inst *inst)
			"%s: Hardware is overloaded\n", __func__);
		return rc;
	}

	if (!is_thermal_permissible(core)) {
		dprintk(VIDC_WARN,
			"Thermal level critical, stop all active sessions!\n");
		return -ENOTSUPP;
	}

	if (!rc && inst->capability.capability_set) {
		if (inst->prop.width[CAPTURE_PORT] < capability->width.min ||
			inst->prop.height[CAPTURE_PORT] <
@@ -4481,27 +4658,14 @@ int msm_comm_kill_session(struct msm_vidc_inst *inst)
	 */
	if (inst->state >= MSM_VIDC_OPEN_DONE &&
			inst->state < MSM_VIDC_CLOSE_DONE) {
		struct hfi_device *hdev = inst->core->device;
		int abort_completion = SESSION_MSG_INDEX(SESSION_ABORT_DONE);

		rc = call_hfi_op(hdev, session_abort, (void *) inst->session);
		if (rc) {
			dprintk(VIDC_ERR, "session_abort failed rc: %d\n", rc);
		rc = msm_comm_session_abort(inst);
		if (rc == -EBUSY) {
			msm_comm_generate_sys_error(inst);
			return 0;
		} else if (rc)
			return rc;
		}

		init_completion(&inst->completions[abort_completion]);
		rc = wait_for_completion_timeout(
				&inst->completions[abort_completion],
				msecs_to_jiffies(msm_vidc_hw_rsp_timeout));
		if (!rc) {
			dprintk(VIDC_ERR,
					"%s: Wait interrupted or timed out [%p]: %d\n",
					__func__, inst, abort_completion);
			msm_comm_generate_sys_error(inst);
		} else {
		change_inst_state(inst, MSM_VIDC_CLOSE_DONE);
		}
	} else {
		dprintk(VIDC_WARN,
				"Inactive session %p, triggering an internal session error\n",
+2 −0
Original line number Diff line number Diff line
@@ -169,6 +169,7 @@ struct msm_vidc_drv {
	struct list_head cores;
	int num_cores;
	struct dentry *debugfs_root;
	int thermal_level;
};

struct msm_video_device {
@@ -389,6 +390,7 @@ int qbuf_dynamic_buf(struct msm_vidc_inst *inst,
int unmap_and_deregister_buf(struct msm_vidc_inst *inst,
			struct buffer_info *binfo);

void msm_comm_handle_thermal_event(void);
void *msm_smem_new_client(enum smem_type mtype,
				void *platform_resources);
struct msm_smem *msm_smem_alloc(void *clt, size_t size, u32 align, u32 flags,
+32 −0
Original line number Diff line number Diff line
@@ -1352,6 +1352,20 @@ static inline int venus_hfi_reset_core(struct venus_hfi_device *device)
	return rc;
}

static struct clock_info *venus_hfi_get_clock(struct venus_hfi_device *device,
		char *name)
{
	struct clock_info *vc;

	venus_hfi_for_each_clock(device, vc) {
		if (!strcmp(vc->name, name))
			return vc;
	}
	dprintk(VIDC_WARN, "%s Clock %s not found\n", __func__, name);

	return NULL;
}

static unsigned long venus_hfi_get_clock_rate(struct clock_info *clock,
	int num_mbs_per_sec, int codecs_enabled)
{
@@ -1376,6 +1390,23 @@ static unsigned long venus_hfi_get_clock_rate(struct clock_info *clock,
	return freq;
}

static unsigned long venus_hfi_get_core_clock_rate(void *dev)
{
	struct venus_hfi_device *device = (struct venus_hfi_device *) dev;
	struct clock_info *vc;

	if (!device) {
		dprintk(VIDC_ERR, "%s Invalid args: %p\n", __func__, device);
		return -EINVAL;
	}

	vc = venus_hfi_get_clock(device, "core_clk");
	if (vc)
		return clk_get_rate(vc->clk);
	else
		return 0;
}

static int venus_hfi_suspend(void *dev)
{
	int rc = 0;
@@ -4193,6 +4224,7 @@ static void venus_init_hfi_callbacks(struct hfi_device *hdev)
	hdev->get_core_capabilities = venus_hfi_get_core_capabilities;
	hdev->power_enable = venus_hfi_power_enable;
	hdev->suspend = venus_hfi_suspend;
	hdev->get_core_clock_rate = venus_hfi_get_core_clock_rate;
}

int venus_hfi_initialize(struct hfi_device *hdev, u32 device_id,
+8 −0
Original line number Diff line number Diff line
@@ -1259,6 +1259,13 @@ enum fw_info {
	FW_INFO_MAX,
};

enum msm_vidc_thermal_level {
	VIDC_THERMAL_NORMAL = 0,
	VIDC_THERMAL_LOW,
	VIDC_THERMAL_HIGH,
	VIDC_THERMAL_CRITICAL
};

enum vidc_bus_vote_data_session {
	VIDC_BUS_VOTE_DATA_SESSION_INVALID = 0,
	/* No declarations exist. Values generated by VIDC_VOTE_DATA_SESSION_VAL
@@ -1340,6 +1347,7 @@ struct hfi_device {
	int (*get_core_capabilities)(void);
	int (*power_enable)(void *dev);
	int (*suspend)(void *dev);
	unsigned long (*get_core_clock_rate)(void *dev);
};

typedef void (*hfi_cmd_response_callback) (enum command_response cmd,