brintos

brintos / linux-shallow public Read only

0
0
Text · 6.5 KiB · 81554d2 Raw
256 lines · c
1// SPDX-License-Identifier: GPL-2.02// Copyright (c) 2021, Linaro Limited3 4#include <linux/slab.h>5#include <linux/wait.h>6#include <linux/kernel.h>7#include <linux/module.h>8#include <linux/of.h>9#include <linux/delay.h>10#include <linux/of_platform.h>11#include <linux/jiffies.h>12#include <linux/soc/qcom/apr.h>13#include <dt-bindings/soc/qcom,gpr.h>14#include <dt-bindings/sound/qcom,q6dsp-lpass-ports.h>15#include "q6apm.h"16#include "q6prm.h"17#include "audioreach.h"18 19struct q6prm {20	struct device *dev;21	gpr_device_t *gdev;22	wait_queue_head_t wait;23	struct gpr_ibasic_rsp_result_t result;24	struct mutex lock;25};26 27#define PRM_CMD_REQUEST_HW_RSC		0x0100100F28#define PRM_CMD_RSP_REQUEST_HW_RSC	0x0200100229#define PRM_CMD_RELEASE_HW_RSC		0x0100101030#define PRM_CMD_RSP_RELEASE_HW_RSC	0x0200100331#define PARAM_ID_RSC_HW_CORE		0x0800103232#define PARAM_ID_RSC_LPASS_CORE		0x0800102B33#define PARAM_ID_RSC_AUDIO_HW_CLK	0x0800102C34 35struct prm_cmd_request_hw_core {36	struct apm_module_param_data param_data;37	uint32_t hw_clk_id;38} __packed;39 40struct prm_cmd_request_rsc {41	struct apm_module_param_data param_data;42	uint32_t num_clk_id;43	struct audio_hw_clk_cfg clock_id;44} __packed;45 46struct prm_cmd_release_rsc {47	struct apm_module_param_data param_data;48	uint32_t num_clk_id;49	struct audio_hw_clk_rel_cfg clock_id;50} __packed;51 52static int q6prm_send_cmd_sync(struct q6prm *prm, struct gpr_pkt *pkt, uint32_t rsp_opcode)53{54	return audioreach_send_cmd_sync(prm->dev, prm->gdev, &prm->result, &prm->lock,55					NULL, &prm->wait, pkt, rsp_opcode);56}57 58static int q6prm_set_hw_core_req(struct device *dev, uint32_t hw_block_id, bool enable)59{60	struct q6prm *prm = dev_get_drvdata(dev->parent);61	struct apm_module_param_data *param_data;62	struct prm_cmd_request_hw_core *req;63	gpr_device_t *gdev = prm->gdev;64	uint32_t opcode, rsp_opcode;65	struct gpr_pkt *pkt;66	int rc;67 68	if (enable) {69		opcode = PRM_CMD_REQUEST_HW_RSC;70		rsp_opcode = PRM_CMD_RSP_REQUEST_HW_RSC;71	} else {72		opcode = PRM_CMD_RELEASE_HW_RSC;73		rsp_opcode = PRM_CMD_RSP_RELEASE_HW_RSC;74	}75 76	pkt = audioreach_alloc_cmd_pkt(sizeof(*req), opcode, 0, gdev->svc.id, GPR_PRM_MODULE_IID);77	if (IS_ERR(pkt))78		return PTR_ERR(pkt);79 80	req = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE;81 82	param_data = &req->param_data;83 84	param_data->module_instance_id = GPR_PRM_MODULE_IID;85	param_data->error_code = 0;86	param_data->param_id = PARAM_ID_RSC_HW_CORE;87	param_data->param_size = sizeof(*req) - APM_MODULE_PARAM_DATA_SIZE;88 89	req->hw_clk_id = hw_block_id;90 91	rc = q6prm_send_cmd_sync(prm, pkt, rsp_opcode);92 93	kfree(pkt);94 95	return rc;96}97 98int q6prm_vote_lpass_core_hw(struct device *dev, uint32_t hw_block_id,99			     const char *client_name, uint32_t *client_handle)100{101	return q6prm_set_hw_core_req(dev, hw_block_id, true);102 103}104EXPORT_SYMBOL_GPL(q6prm_vote_lpass_core_hw);105 106int q6prm_unvote_lpass_core_hw(struct device *dev, uint32_t hw_block_id, uint32_t client_handle)107{108	return q6prm_set_hw_core_req(dev, hw_block_id, false);109}110EXPORT_SYMBOL_GPL(q6prm_unvote_lpass_core_hw);111 112static int q6prm_request_lpass_clock(struct device *dev, int clk_id, int clk_attr, int clk_root,113				     unsigned int freq)114{115	struct q6prm *prm = dev_get_drvdata(dev->parent);116	struct apm_module_param_data *param_data;117	struct prm_cmd_request_rsc *req;118	gpr_device_t *gdev = prm->gdev;119	struct gpr_pkt *pkt;120	int rc;121 122	pkt = audioreach_alloc_cmd_pkt(sizeof(*req), PRM_CMD_REQUEST_HW_RSC, 0, gdev->svc.id,123				       GPR_PRM_MODULE_IID);124	if (IS_ERR(pkt))125		return PTR_ERR(pkt);126 127	req = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE;128 129	param_data = &req->param_data;130 131	param_data->module_instance_id = GPR_PRM_MODULE_IID;132	param_data->error_code = 0;133	param_data->param_id = PARAM_ID_RSC_AUDIO_HW_CLK;134	param_data->param_size = sizeof(*req) - APM_MODULE_PARAM_DATA_SIZE;135 136	req->num_clk_id = 1;137	req->clock_id.clock_id = clk_id;138	req->clock_id.clock_freq = freq;139	req->clock_id.clock_attri = clk_attr;140	req->clock_id.clock_root = clk_root;141 142	rc = q6prm_send_cmd_sync(prm, pkt, PRM_CMD_RSP_REQUEST_HW_RSC);143 144	kfree(pkt);145 146	return rc;147}148 149static int q6prm_release_lpass_clock(struct device *dev, int clk_id, int clk_attr, int clk_root,150			  unsigned int freq)151{152	struct q6prm *prm = dev_get_drvdata(dev->parent);153	struct apm_module_param_data *param_data;154	struct prm_cmd_release_rsc *rel;155	gpr_device_t *gdev = prm->gdev;156	struct gpr_pkt *pkt;157	int rc;158 159	pkt = audioreach_alloc_cmd_pkt(sizeof(*rel), PRM_CMD_RELEASE_HW_RSC, 0, gdev->svc.id,160				       GPR_PRM_MODULE_IID);161	if (IS_ERR(pkt))162		return PTR_ERR(pkt);163 164	rel = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE;165 166	param_data = &rel->param_data;167 168	param_data->module_instance_id = GPR_PRM_MODULE_IID;169	param_data->error_code = 0;170	param_data->param_id = PARAM_ID_RSC_AUDIO_HW_CLK;171	param_data->param_size = sizeof(*rel) - APM_MODULE_PARAM_DATA_SIZE;172 173	rel->num_clk_id = 1;174	rel->clock_id.clock_id = clk_id;175 176	rc = q6prm_send_cmd_sync(prm, pkt, PRM_CMD_RSP_RELEASE_HW_RSC);177 178	kfree(pkt);179 180	return rc;181}182 183int q6prm_set_lpass_clock(struct device *dev, int clk_id, int clk_attr, int clk_root,184			  unsigned int freq)185{186	if (freq)187		return q6prm_request_lpass_clock(dev, clk_id, clk_attr, clk_root, freq);188 189	return q6prm_release_lpass_clock(dev, clk_id, clk_attr, clk_root, freq);190}191EXPORT_SYMBOL_GPL(q6prm_set_lpass_clock);192 193static int prm_callback(struct gpr_resp_pkt *data, void *priv, int op)194{195	gpr_device_t *gdev = priv;196	struct q6prm *prm = dev_get_drvdata(&gdev->dev);197	struct gpr_ibasic_rsp_result_t *result;198	struct gpr_hdr *hdr = &data->hdr;199 200	switch (hdr->opcode) {201	case PRM_CMD_RSP_REQUEST_HW_RSC:202	case PRM_CMD_RSP_RELEASE_HW_RSC:203		result = data->payload;204		prm->result.opcode = hdr->opcode;205		prm->result.status = result->status;206		wake_up(&prm->wait);207		break;208	default:209		break;210	}211 212	return 0;213}214 215static int prm_probe(gpr_device_t *gdev)216{217	struct device *dev = &gdev->dev;218	struct q6prm *cc;219 220	cc = devm_kzalloc(dev, sizeof(*cc), GFP_KERNEL);221	if (!cc)222		return -ENOMEM;223 224	cc->dev = dev;225	cc->gdev = gdev;226	mutex_init(&cc->lock);227	init_waitqueue_head(&cc->wait);228	dev_set_drvdata(dev, cc);229 230	if (!q6apm_is_adsp_ready())231		return -EPROBE_DEFER;232 233	return devm_of_platform_populate(dev);234}235 236#ifdef CONFIG_OF237static const struct of_device_id prm_device_id[]  = {238	{ .compatible = "qcom,q6prm" },239	{},240};241MODULE_DEVICE_TABLE(of, prm_device_id);242#endif243 244static gpr_driver_t prm_driver = {245	.probe = prm_probe,246	.gpr_callback = prm_callback,247	.driver = {248		.name = "qcom-prm",249		.of_match_table = of_match_ptr(prm_device_id),250	},251};252 253module_gpr_driver(prm_driver);254MODULE_DESCRIPTION("Q6 Proxy Resource Manager");255MODULE_LICENSE("GPL");256