162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2023 Intel Corporation. All rights reserved.
462306a36Sopenharmony_ci * Intel Visual Sensing Controller ACE Linux driver
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci/*
862306a36Sopenharmony_ci * To set ownership of camera sensor, there is specific command, which
962306a36Sopenharmony_ci * is sent via MEI protocol. That's a two-step scheme where the firmware
1062306a36Sopenharmony_ci * first acks receipt of the command and later responses the command was
1162306a36Sopenharmony_ci * executed. The command sending function uses "completion" as the
1262306a36Sopenharmony_ci * synchronization mechanism. The notification for command is received
1362306a36Sopenharmony_ci * via a mei callback which wakes up the caller. There can be only one
1462306a36Sopenharmony_ci * outstanding command at a time.
1562306a36Sopenharmony_ci *
1662306a36Sopenharmony_ci * The power line of camera sensor is directly connected to IVSC instead
1762306a36Sopenharmony_ci * of host, when camera sensor ownership is switched to host, sensor is
1862306a36Sopenharmony_ci * already powered up by firmware.
1962306a36Sopenharmony_ci */
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#include <linux/acpi.h>
2262306a36Sopenharmony_ci#include <linux/completion.h>
2362306a36Sopenharmony_ci#include <linux/delay.h>
2462306a36Sopenharmony_ci#include <linux/kernel.h>
2562306a36Sopenharmony_ci#include <linux/mei_cl_bus.h>
2662306a36Sopenharmony_ci#include <linux/module.h>
2762306a36Sopenharmony_ci#include <linux/mutex.h>
2862306a36Sopenharmony_ci#include <linux/pm_runtime.h>
2962306a36Sopenharmony_ci#include <linux/slab.h>
3062306a36Sopenharmony_ci#include <linux/uuid.h>
3162306a36Sopenharmony_ci#include <linux/workqueue.h>
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci#define	MEI_ACE_DRIVER_NAME	"ivsc_ace"
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci/* indicating driver message */
3662306a36Sopenharmony_ci#define	ACE_DRV_MSG		1
3762306a36Sopenharmony_ci/* indicating set command */
3862306a36Sopenharmony_ci#define	ACE_CMD_SET		4
3962306a36Sopenharmony_ci/* command timeout determined experimentally */
4062306a36Sopenharmony_ci#define	ACE_CMD_TIMEOUT		(5 * HZ)
4162306a36Sopenharmony_ci/* indicating the first command block */
4262306a36Sopenharmony_ci#define	ACE_CMD_INIT_BLOCK	1
4362306a36Sopenharmony_ci/* indicating the last command block */
4462306a36Sopenharmony_ci#define	ACE_CMD_FINAL_BLOCK	1
4562306a36Sopenharmony_ci/* size of camera status notification content */
4662306a36Sopenharmony_ci#define	ACE_CAMERA_STATUS_SIZE	5
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci/* UUID used to get firmware id */
4962306a36Sopenharmony_ci#define ACE_GET_FW_ID_UUID UUID_LE(0x6167DCFB, 0x72F1, 0x4584, 0xBF, \
5062306a36Sopenharmony_ci				   0xE3, 0x84, 0x17, 0x71, 0xAA, 0x79, 0x0B)
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci/* UUID used to get csi device */
5362306a36Sopenharmony_ci#define MEI_CSI_UUID UUID_LE(0x92335FCF, 0x3203, 0x4472, \
5462306a36Sopenharmony_ci			     0xAF, 0x93, 0x7b, 0x44, 0x53, 0xAC, 0x29, 0xDA)
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci/* identify firmware event type */
5762306a36Sopenharmony_cienum ace_event_type {
5862306a36Sopenharmony_ci	/* firmware ready */
5962306a36Sopenharmony_ci	ACE_FW_READY = 0x8,
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	/* command response */
6262306a36Sopenharmony_ci	ACE_CMD_RESPONSE = 0x10,
6362306a36Sopenharmony_ci};
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci/* identify camera sensor ownership */
6662306a36Sopenharmony_cienum ace_camera_owner {
6762306a36Sopenharmony_ci	ACE_CAMERA_IVSC,
6862306a36Sopenharmony_ci	ACE_CAMERA_HOST,
6962306a36Sopenharmony_ci};
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci/* identify the command id supported by firmware IPC */
7262306a36Sopenharmony_cienum ace_cmd_id {
7362306a36Sopenharmony_ci	/* used to switch camera sensor to host */
7462306a36Sopenharmony_ci	ACE_SWITCH_CAMERA_TO_HOST = 0x13,
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	/* used to switch camera sensor to IVSC */
7762306a36Sopenharmony_ci	ACE_SWITCH_CAMERA_TO_IVSC = 0x14,
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	/* used to get firmware id */
8062306a36Sopenharmony_ci	ACE_GET_FW_ID = 0x1A,
8162306a36Sopenharmony_ci};
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci/* ACE command header structure */
8462306a36Sopenharmony_cistruct ace_cmd_hdr {
8562306a36Sopenharmony_ci	u32 firmware_id : 16;
8662306a36Sopenharmony_ci	u32 instance_id : 8;
8762306a36Sopenharmony_ci	u32 type : 5;
8862306a36Sopenharmony_ci	u32 rsp : 1;
8962306a36Sopenharmony_ci	u32 msg_tgt : 1;
9062306a36Sopenharmony_ci	u32 _hw_rsvd_1 : 1;
9162306a36Sopenharmony_ci	u32 param_size : 20;
9262306a36Sopenharmony_ci	u32 cmd_id : 8;
9362306a36Sopenharmony_ci	u32 final_block : 1;
9462306a36Sopenharmony_ci	u32 init_block : 1;
9562306a36Sopenharmony_ci	u32 _hw_rsvd_2 : 2;
9662306a36Sopenharmony_ci} __packed;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci/* ACE command parameter structure */
9962306a36Sopenharmony_ciunion ace_cmd_param {
10062306a36Sopenharmony_ci	uuid_le uuid;
10162306a36Sopenharmony_ci	u32 param;
10262306a36Sopenharmony_ci};
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci/* ACE command structure */
10562306a36Sopenharmony_cistruct ace_cmd {
10662306a36Sopenharmony_ci	struct ace_cmd_hdr hdr;
10762306a36Sopenharmony_ci	union ace_cmd_param param;
10862306a36Sopenharmony_ci} __packed;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci/* ACE notification header */
11162306a36Sopenharmony_ciunion ace_notif_hdr {
11262306a36Sopenharmony_ci	struct _confirm {
11362306a36Sopenharmony_ci		u32 status : 24;
11462306a36Sopenharmony_ci		u32 type : 5;
11562306a36Sopenharmony_ci		u32 rsp : 1;
11662306a36Sopenharmony_ci		u32 msg_tgt : 1;
11762306a36Sopenharmony_ci		u32 _hw_rsvd_1 : 1;
11862306a36Sopenharmony_ci		u32 param_size : 20;
11962306a36Sopenharmony_ci		u32 cmd_id : 8;
12062306a36Sopenharmony_ci		u32 final_block : 1;
12162306a36Sopenharmony_ci		u32 init_block : 1;
12262306a36Sopenharmony_ci		u32 _hw_rsvd_2 : 2;
12362306a36Sopenharmony_ci	} __packed ack;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	struct _event {
12662306a36Sopenharmony_ci		u32 rsvd1 : 16;
12762306a36Sopenharmony_ci		u32 event_type : 8;
12862306a36Sopenharmony_ci		u32 type : 5;
12962306a36Sopenharmony_ci		u32 ack : 1;
13062306a36Sopenharmony_ci		u32 msg_tgt : 1;
13162306a36Sopenharmony_ci		u32 _hw_rsvd_1 : 1;
13262306a36Sopenharmony_ci		u32 rsvd2 : 30;
13362306a36Sopenharmony_ci		u32 _hw_rsvd_2 : 2;
13462306a36Sopenharmony_ci	} __packed event;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	struct _response {
13762306a36Sopenharmony_ci		u32 event_id : 16;
13862306a36Sopenharmony_ci		u32 notif_type : 8;
13962306a36Sopenharmony_ci		u32 type : 5;
14062306a36Sopenharmony_ci		u32 rsp : 1;
14162306a36Sopenharmony_ci		u32 msg_tgt : 1;
14262306a36Sopenharmony_ci		u32 _hw_rsvd_1 : 1;
14362306a36Sopenharmony_ci		u32 event_data_size : 16;
14462306a36Sopenharmony_ci		u32 request_target : 1;
14562306a36Sopenharmony_ci		u32 request_type : 5;
14662306a36Sopenharmony_ci		u32 cmd_id : 8;
14762306a36Sopenharmony_ci		u32 _hw_rsvd_2 : 2;
14862306a36Sopenharmony_ci	} __packed response;
14962306a36Sopenharmony_ci};
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci/* ACE notification content */
15262306a36Sopenharmony_ciunion ace_notif_cont {
15362306a36Sopenharmony_ci	u16 firmware_id;
15462306a36Sopenharmony_ci	u8 state_notif;
15562306a36Sopenharmony_ci	u8 camera_status[ACE_CAMERA_STATUS_SIZE];
15662306a36Sopenharmony_ci};
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci/* ACE notification structure */
15962306a36Sopenharmony_cistruct ace_notif {
16062306a36Sopenharmony_ci	union ace_notif_hdr hdr;
16162306a36Sopenharmony_ci	union ace_notif_cont cont;
16262306a36Sopenharmony_ci} __packed;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_cistruct mei_ace {
16562306a36Sopenharmony_ci	struct mei_cl_device *cldev;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	/* command ack */
16862306a36Sopenharmony_ci	struct ace_notif cmd_ack;
16962306a36Sopenharmony_ci	/* command response */
17062306a36Sopenharmony_ci	struct ace_notif cmd_response;
17162306a36Sopenharmony_ci	/* used to wait for command ack and response */
17262306a36Sopenharmony_ci	struct completion cmd_completion;
17362306a36Sopenharmony_ci	/* lock used to prevent multiple call to send command */
17462306a36Sopenharmony_ci	struct mutex lock;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	/* used to construct command */
17762306a36Sopenharmony_ci	u16 firmware_id;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	struct device *csi_dev;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	/* runtime PM link from ace to csi */
18262306a36Sopenharmony_ci	struct device_link *csi_link;
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	struct work_struct work;
18562306a36Sopenharmony_ci};
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_cistatic inline void init_cmd_hdr(struct ace_cmd_hdr *hdr)
18862306a36Sopenharmony_ci{
18962306a36Sopenharmony_ci	memset(hdr, 0, sizeof(struct ace_cmd_hdr));
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	hdr->type = ACE_CMD_SET;
19262306a36Sopenharmony_ci	hdr->msg_tgt = ACE_DRV_MSG;
19362306a36Sopenharmony_ci	hdr->init_block = ACE_CMD_INIT_BLOCK;
19462306a36Sopenharmony_ci	hdr->final_block = ACE_CMD_FINAL_BLOCK;
19562306a36Sopenharmony_ci}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_cistatic int construct_command(struct mei_ace *ace, struct ace_cmd *cmd,
19862306a36Sopenharmony_ci			     enum ace_cmd_id cmd_id)
19962306a36Sopenharmony_ci{
20062306a36Sopenharmony_ci	union ace_cmd_param *param = &cmd->param;
20162306a36Sopenharmony_ci	struct ace_cmd_hdr *hdr = &cmd->hdr;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	init_cmd_hdr(hdr);
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	hdr->cmd_id = cmd_id;
20662306a36Sopenharmony_ci	switch (cmd_id) {
20762306a36Sopenharmony_ci	case ACE_GET_FW_ID:
20862306a36Sopenharmony_ci		param->uuid = ACE_GET_FW_ID_UUID;
20962306a36Sopenharmony_ci		hdr->param_size = sizeof(param->uuid);
21062306a36Sopenharmony_ci		break;
21162306a36Sopenharmony_ci	case ACE_SWITCH_CAMERA_TO_IVSC:
21262306a36Sopenharmony_ci		param->param = 0;
21362306a36Sopenharmony_ci		hdr->firmware_id = ace->firmware_id;
21462306a36Sopenharmony_ci		hdr->param_size = sizeof(param->param);
21562306a36Sopenharmony_ci		break;
21662306a36Sopenharmony_ci	case ACE_SWITCH_CAMERA_TO_HOST:
21762306a36Sopenharmony_ci		hdr->firmware_id = ace->firmware_id;
21862306a36Sopenharmony_ci		break;
21962306a36Sopenharmony_ci	default:
22062306a36Sopenharmony_ci		return -EINVAL;
22162306a36Sopenharmony_ci	}
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	return hdr->param_size + sizeof(cmd->hdr);
22462306a36Sopenharmony_ci}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci/* send command to firmware */
22762306a36Sopenharmony_cistatic int mei_ace_send(struct mei_ace *ace, struct ace_cmd *cmd,
22862306a36Sopenharmony_ci			size_t len, bool only_ack)
22962306a36Sopenharmony_ci{
23062306a36Sopenharmony_ci	union ace_notif_hdr *resp_hdr = &ace->cmd_response.hdr;
23162306a36Sopenharmony_ci	union ace_notif_hdr *ack_hdr = &ace->cmd_ack.hdr;
23262306a36Sopenharmony_ci	struct ace_cmd_hdr *cmd_hdr = &cmd->hdr;
23362306a36Sopenharmony_ci	int ret;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	mutex_lock(&ace->lock);
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	reinit_completion(&ace->cmd_completion);
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	ret = mei_cldev_send(ace->cldev, (u8 *)cmd, len);
24062306a36Sopenharmony_ci	if (ret < 0)
24162306a36Sopenharmony_ci		goto out;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	ret = wait_for_completion_killable_timeout(&ace->cmd_completion,
24462306a36Sopenharmony_ci						   ACE_CMD_TIMEOUT);
24562306a36Sopenharmony_ci	if (ret < 0) {
24662306a36Sopenharmony_ci		goto out;
24762306a36Sopenharmony_ci	} else if (!ret) {
24862306a36Sopenharmony_ci		ret = -ETIMEDOUT;
24962306a36Sopenharmony_ci		goto out;
25062306a36Sopenharmony_ci	}
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	if (ack_hdr->ack.cmd_id != cmd_hdr->cmd_id) {
25362306a36Sopenharmony_ci		ret = -EINVAL;
25462306a36Sopenharmony_ci		goto out;
25562306a36Sopenharmony_ci	}
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	/* command ack status */
25862306a36Sopenharmony_ci	ret = ack_hdr->ack.status;
25962306a36Sopenharmony_ci	if (ret) {
26062306a36Sopenharmony_ci		ret = -EIO;
26162306a36Sopenharmony_ci		goto out;
26262306a36Sopenharmony_ci	}
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	if (only_ack)
26562306a36Sopenharmony_ci		goto out;
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	ret = wait_for_completion_killable_timeout(&ace->cmd_completion,
26862306a36Sopenharmony_ci						   ACE_CMD_TIMEOUT);
26962306a36Sopenharmony_ci	if (ret < 0) {
27062306a36Sopenharmony_ci		goto out;
27162306a36Sopenharmony_ci	} else if (!ret) {
27262306a36Sopenharmony_ci		ret = -ETIMEDOUT;
27362306a36Sopenharmony_ci		goto out;
27462306a36Sopenharmony_ci	} else {
27562306a36Sopenharmony_ci		ret = 0;
27662306a36Sopenharmony_ci	}
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	if (resp_hdr->response.cmd_id != cmd_hdr->cmd_id)
27962306a36Sopenharmony_ci		ret = -EINVAL;
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ciout:
28262306a36Sopenharmony_ci	mutex_unlock(&ace->lock);
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	return ret;
28562306a36Sopenharmony_ci}
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_cistatic int ace_set_camera_owner(struct mei_ace *ace,
28862306a36Sopenharmony_ci				enum ace_camera_owner owner)
28962306a36Sopenharmony_ci{
29062306a36Sopenharmony_ci	enum ace_cmd_id cmd_id;
29162306a36Sopenharmony_ci	struct ace_cmd cmd;
29262306a36Sopenharmony_ci	int cmd_size;
29362306a36Sopenharmony_ci	int ret;
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	if (owner == ACE_CAMERA_IVSC)
29662306a36Sopenharmony_ci		cmd_id = ACE_SWITCH_CAMERA_TO_IVSC;
29762306a36Sopenharmony_ci	else
29862306a36Sopenharmony_ci		cmd_id = ACE_SWITCH_CAMERA_TO_HOST;
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	cmd_size = construct_command(ace, &cmd, cmd_id);
30162306a36Sopenharmony_ci	if (cmd_size >= 0)
30262306a36Sopenharmony_ci		ret = mei_ace_send(ace, &cmd, cmd_size, false);
30362306a36Sopenharmony_ci	else
30462306a36Sopenharmony_ci		ret = cmd_size;
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	return ret;
30762306a36Sopenharmony_ci}
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci/* the first command downloaded to firmware */
31062306a36Sopenharmony_cistatic inline int ace_get_firmware_id(struct mei_ace *ace)
31162306a36Sopenharmony_ci{
31262306a36Sopenharmony_ci	struct ace_cmd cmd;
31362306a36Sopenharmony_ci	int cmd_size;
31462306a36Sopenharmony_ci	int ret;
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	cmd_size = construct_command(ace, &cmd, ACE_GET_FW_ID);
31762306a36Sopenharmony_ci	if (cmd_size >= 0)
31862306a36Sopenharmony_ci		ret = mei_ace_send(ace, &cmd, cmd_size, true);
31962306a36Sopenharmony_ci	else
32062306a36Sopenharmony_ci		ret = cmd_size;
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	return ret;
32362306a36Sopenharmony_ci}
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_cistatic void handle_command_response(struct mei_ace *ace,
32662306a36Sopenharmony_ci				    struct ace_notif *resp, int len)
32762306a36Sopenharmony_ci{
32862306a36Sopenharmony_ci	union ace_notif_hdr *hdr = &resp->hdr;
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	switch (hdr->response.cmd_id) {
33162306a36Sopenharmony_ci	case ACE_SWITCH_CAMERA_TO_IVSC:
33262306a36Sopenharmony_ci	case ACE_SWITCH_CAMERA_TO_HOST:
33362306a36Sopenharmony_ci		memcpy(&ace->cmd_response, resp, len);
33462306a36Sopenharmony_ci		complete(&ace->cmd_completion);
33562306a36Sopenharmony_ci		break;
33662306a36Sopenharmony_ci	case ACE_GET_FW_ID:
33762306a36Sopenharmony_ci		break;
33862306a36Sopenharmony_ci	default:
33962306a36Sopenharmony_ci		break;
34062306a36Sopenharmony_ci	}
34162306a36Sopenharmony_ci}
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_cistatic void handle_command_ack(struct mei_ace *ace,
34462306a36Sopenharmony_ci			       struct ace_notif *ack, int len)
34562306a36Sopenharmony_ci{
34662306a36Sopenharmony_ci	union ace_notif_hdr *hdr = &ack->hdr;
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	switch (hdr->ack.cmd_id) {
34962306a36Sopenharmony_ci	case ACE_GET_FW_ID:
35062306a36Sopenharmony_ci		ace->firmware_id = ack->cont.firmware_id;
35162306a36Sopenharmony_ci		fallthrough;
35262306a36Sopenharmony_ci	case ACE_SWITCH_CAMERA_TO_IVSC:
35362306a36Sopenharmony_ci	case ACE_SWITCH_CAMERA_TO_HOST:
35462306a36Sopenharmony_ci		memcpy(&ace->cmd_ack, ack, len);
35562306a36Sopenharmony_ci		complete(&ace->cmd_completion);
35662306a36Sopenharmony_ci		break;
35762306a36Sopenharmony_ci	default:
35862306a36Sopenharmony_ci		break;
35962306a36Sopenharmony_ci	}
36062306a36Sopenharmony_ci}
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci/* callback for receive */
36362306a36Sopenharmony_cistatic void mei_ace_rx(struct mei_cl_device *cldev)
36462306a36Sopenharmony_ci{
36562306a36Sopenharmony_ci	struct mei_ace *ace = mei_cldev_get_drvdata(cldev);
36662306a36Sopenharmony_ci	struct ace_notif event;
36762306a36Sopenharmony_ci	union ace_notif_hdr *hdr = &event.hdr;
36862306a36Sopenharmony_ci	int ret;
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	ret = mei_cldev_recv(cldev, (u8 *)&event, sizeof(event));
37162306a36Sopenharmony_ci	if (ret < 0) {
37262306a36Sopenharmony_ci		dev_err(&cldev->dev, "recv error: %d\n", ret);
37362306a36Sopenharmony_ci		return;
37462306a36Sopenharmony_ci	}
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	if (hdr->event.ack) {
37762306a36Sopenharmony_ci		handle_command_ack(ace, &event, ret);
37862306a36Sopenharmony_ci		return;
37962306a36Sopenharmony_ci	}
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	switch (hdr->event.event_type) {
38262306a36Sopenharmony_ci	case ACE_CMD_RESPONSE:
38362306a36Sopenharmony_ci		handle_command_response(ace, &event, ret);
38462306a36Sopenharmony_ci		break;
38562306a36Sopenharmony_ci	case ACE_FW_READY:
38662306a36Sopenharmony_ci		/*
38762306a36Sopenharmony_ci		 * firmware ready notification sent to driver
38862306a36Sopenharmony_ci		 * after HECI client connected with firmware.
38962306a36Sopenharmony_ci		 */
39062306a36Sopenharmony_ci		dev_dbg(&cldev->dev, "firmware ready\n");
39162306a36Sopenharmony_ci		break;
39262306a36Sopenharmony_ci	default:
39362306a36Sopenharmony_ci		break;
39462306a36Sopenharmony_ci	}
39562306a36Sopenharmony_ci}
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_cistatic int mei_ace_setup_dev_link(struct mei_ace *ace)
39862306a36Sopenharmony_ci{
39962306a36Sopenharmony_ci	struct device *dev = &ace->cldev->dev;
40062306a36Sopenharmony_ci	uuid_le uuid = MEI_CSI_UUID;
40162306a36Sopenharmony_ci	struct device *csi_dev;
40262306a36Sopenharmony_ci	char name[64];
40362306a36Sopenharmony_ci	int ret;
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	snprintf(name, sizeof(name), "%s-%pUl", dev_name(dev->parent), &uuid);
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	csi_dev = device_find_child_by_name(dev->parent, name);
40862306a36Sopenharmony_ci	if (!csi_dev) {
40962306a36Sopenharmony_ci		ret = -EPROBE_DEFER;
41062306a36Sopenharmony_ci		goto err;
41162306a36Sopenharmony_ci	}
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	/* setup link between mei_ace and mei_csi */
41462306a36Sopenharmony_ci	ace->csi_link = device_link_add(csi_dev, dev, DL_FLAG_PM_RUNTIME |
41562306a36Sopenharmony_ci					DL_FLAG_RPM_ACTIVE | DL_FLAG_STATELESS);
41662306a36Sopenharmony_ci	if (!ace->csi_link) {
41762306a36Sopenharmony_ci		ret = -EINVAL;
41862306a36Sopenharmony_ci		dev_err(dev, "failed to link to %s\n", dev_name(csi_dev));
41962306a36Sopenharmony_ci		goto err_put;
42062306a36Sopenharmony_ci	}
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	ace->csi_dev = csi_dev;
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	return 0;
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_cierr_put:
42762306a36Sopenharmony_ci	put_device(csi_dev);
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_cierr:
43062306a36Sopenharmony_ci	return ret;
43162306a36Sopenharmony_ci}
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci/* switch camera to host before probe sensor device */
43462306a36Sopenharmony_cistatic void mei_ace_post_probe_work(struct work_struct *work)
43562306a36Sopenharmony_ci{
43662306a36Sopenharmony_ci	struct acpi_device *adev;
43762306a36Sopenharmony_ci	struct mei_ace *ace;
43862306a36Sopenharmony_ci	struct device *dev;
43962306a36Sopenharmony_ci	int ret;
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	ace = container_of(work, struct mei_ace, work);
44262306a36Sopenharmony_ci	dev = &ace->cldev->dev;
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	ret = ace_set_camera_owner(ace, ACE_CAMERA_HOST);
44562306a36Sopenharmony_ci	if (ret) {
44662306a36Sopenharmony_ci		dev_err(dev, "switch camera to host failed: %d\n", ret);
44762306a36Sopenharmony_ci		return;
44862306a36Sopenharmony_ci	}
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	adev = ACPI_COMPANION(dev->parent);
45162306a36Sopenharmony_ci	if (!adev)
45262306a36Sopenharmony_ci		return;
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	acpi_dev_clear_dependencies(adev);
45562306a36Sopenharmony_ci}
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_cistatic int mei_ace_probe(struct mei_cl_device *cldev,
45862306a36Sopenharmony_ci			 const struct mei_cl_device_id *id)
45962306a36Sopenharmony_ci{
46062306a36Sopenharmony_ci	struct device *dev = &cldev->dev;
46162306a36Sopenharmony_ci	struct mei_ace *ace;
46262306a36Sopenharmony_ci	int ret;
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	ace = devm_kzalloc(dev, sizeof(struct mei_ace), GFP_KERNEL);
46562306a36Sopenharmony_ci	if (!ace)
46662306a36Sopenharmony_ci		return -ENOMEM;
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	ace->cldev = cldev;
46962306a36Sopenharmony_ci	mutex_init(&ace->lock);
47062306a36Sopenharmony_ci	init_completion(&ace->cmd_completion);
47162306a36Sopenharmony_ci	INIT_WORK(&ace->work, mei_ace_post_probe_work);
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	mei_cldev_set_drvdata(cldev, ace);
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	ret = mei_cldev_enable(cldev);
47662306a36Sopenharmony_ci	if (ret < 0) {
47762306a36Sopenharmony_ci		dev_err(dev, "mei_cldev_enable failed: %d\n", ret);
47862306a36Sopenharmony_ci		goto destroy_mutex;
47962306a36Sopenharmony_ci	}
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	ret = mei_cldev_register_rx_cb(cldev, mei_ace_rx);
48262306a36Sopenharmony_ci	if (ret) {
48362306a36Sopenharmony_ci		dev_err(dev, "event cb registration failed: %d\n", ret);
48462306a36Sopenharmony_ci		goto err_disable;
48562306a36Sopenharmony_ci	}
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	ret = ace_get_firmware_id(ace);
48862306a36Sopenharmony_ci	if (ret) {
48962306a36Sopenharmony_ci		dev_err(dev, "get firmware id failed: %d\n", ret);
49062306a36Sopenharmony_ci		goto err_disable;
49162306a36Sopenharmony_ci	}
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	pm_runtime_set_active(dev);
49462306a36Sopenharmony_ci	pm_runtime_enable(dev);
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	ret = mei_ace_setup_dev_link(ace);
49762306a36Sopenharmony_ci	if (ret)
49862306a36Sopenharmony_ci		goto disable_pm;
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	schedule_work(&ace->work);
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	return 0;
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_cidisable_pm:
50562306a36Sopenharmony_ci	pm_runtime_disable(dev);
50662306a36Sopenharmony_ci	pm_runtime_set_suspended(dev);
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_cierr_disable:
50962306a36Sopenharmony_ci	mei_cldev_disable(cldev);
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_cidestroy_mutex:
51262306a36Sopenharmony_ci	mutex_destroy(&ace->lock);
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	return ret;
51562306a36Sopenharmony_ci}
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_cistatic void mei_ace_remove(struct mei_cl_device *cldev)
51862306a36Sopenharmony_ci{
51962306a36Sopenharmony_ci	struct mei_ace *ace = mei_cldev_get_drvdata(cldev);
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	cancel_work_sync(&ace->work);
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	device_link_del(ace->csi_link);
52462306a36Sopenharmony_ci	put_device(ace->csi_dev);
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	pm_runtime_disable(&cldev->dev);
52762306a36Sopenharmony_ci	pm_runtime_set_suspended(&cldev->dev);
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	ace_set_camera_owner(ace, ACE_CAMERA_IVSC);
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	mutex_destroy(&ace->lock);
53262306a36Sopenharmony_ci}
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_cistatic int __maybe_unused mei_ace_runtime_suspend(struct device *dev)
53562306a36Sopenharmony_ci{
53662306a36Sopenharmony_ci	struct mei_ace *ace = dev_get_drvdata(dev);
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	return ace_set_camera_owner(ace, ACE_CAMERA_IVSC);
53962306a36Sopenharmony_ci}
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_cistatic int __maybe_unused mei_ace_runtime_resume(struct device *dev)
54262306a36Sopenharmony_ci{
54362306a36Sopenharmony_ci	struct mei_ace *ace = dev_get_drvdata(dev);
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	return ace_set_camera_owner(ace, ACE_CAMERA_HOST);
54662306a36Sopenharmony_ci}
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_cistatic const struct dev_pm_ops mei_ace_pm_ops = {
54962306a36Sopenharmony_ci	SET_RUNTIME_PM_OPS(mei_ace_runtime_suspend,
55062306a36Sopenharmony_ci			   mei_ace_runtime_resume, NULL)
55162306a36Sopenharmony_ci};
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci#define MEI_ACE_UUID UUID_LE(0x5DB76CF6, 0x0A68, 0x4ED6, \
55462306a36Sopenharmony_ci			     0x9B, 0x78, 0x03, 0x61, 0x63, 0x5E, 0x24, 0x47)
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_cistatic const struct mei_cl_device_id mei_ace_tbl[] = {
55762306a36Sopenharmony_ci	{ MEI_ACE_DRIVER_NAME, MEI_ACE_UUID, MEI_CL_VERSION_ANY },
55862306a36Sopenharmony_ci	{ /* sentinel */ }
55962306a36Sopenharmony_ci};
56062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(mei, mei_ace_tbl);
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_cistatic struct mei_cl_driver mei_ace_driver = {
56362306a36Sopenharmony_ci	.id_table = mei_ace_tbl,
56462306a36Sopenharmony_ci	.name = MEI_ACE_DRIVER_NAME,
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	.probe = mei_ace_probe,
56762306a36Sopenharmony_ci	.remove = mei_ace_remove,
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	.driver = {
57062306a36Sopenharmony_ci		.pm = &mei_ace_pm_ops,
57162306a36Sopenharmony_ci	},
57262306a36Sopenharmony_ci};
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_cimodule_mei_cl_driver(mei_ace_driver);
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ciMODULE_AUTHOR("Wentong Wu <wentong.wu@intel.com>");
57762306a36Sopenharmony_ciMODULE_AUTHOR("Zhifeng Wang <zhifeng.wang@intel.com>");
57862306a36Sopenharmony_ciMODULE_DESCRIPTION("Device driver for IVSC ACE");
57962306a36Sopenharmony_ciMODULE_LICENSE("GPL");
580